Skip to content

Commit d0cc3fd

Browse files
authored
Add sum of multiples (#288)
by: oxe-i <[email protected]>
1 parent 1b43e74 commit d0cc3fd

File tree

13 files changed

+3702
-0
lines changed

13 files changed

+3702
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,14 @@
446446
"transforming"
447447
]
448448
},
449+
{
450+
"slug": "sum-of-multiples",
451+
"name": "Sum of Multiples",
452+
"uuid": "ce06cacb-ec4e-46f2-a1b7-f496f0db097f",
453+
"practices": [],
454+
"prerequisites": [],
455+
"difficulty": 4
456+
},
449457
{
450458
"slug": "twelve-days",
451459
"name": "Twelve Days",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Instructions
2+
3+
Your task is to write the code that calculates the energy points that get awarded to players when they complete a level.
4+
5+
The points awarded depend on two things:
6+
7+
- The level (a number) that the player completed.
8+
- The base value of each magical item collected by the player during that level.
9+
10+
The energy points are awarded according to the following rules:
11+
12+
1. For each magical item, take the base value and find all the multiples of that value that are less than the level number.
13+
2. Combine the sets of numbers.
14+
3. Remove any duplicates.
15+
4. Calculate the sum of all the numbers that are left.
16+
17+
Let's look at an example:
18+
19+
**The player completed level 20 and found two magical items with base values of 3 and 5.**
20+
21+
To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than level 20.
22+
23+
- Multiples of 3 less than 20: `{3, 6, 9, 12, 15, 18}`
24+
- Multiples of 5 less than 20: `{5, 10, 15}`
25+
- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}`
26+
- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78`
27+
- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Introduction
2+
3+
You work for a company that makes an online, fantasy-survival game.
4+
5+
When a player finishes a level, they are awarded energy points.
6+
The amount of energy awarded depends on which magical items the player found while exploring that level.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"oxe-i"
4+
],
5+
"files": {
6+
"solution": [
7+
"sum_of_multiples.asm"
8+
],
9+
"test": [
10+
"sum_of_multiples_test.c"
11+
],
12+
"example": [
13+
".meta/example.asm"
14+
]
15+
},
16+
"blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.",
17+
"source": "A variation on Problem 1 at Project Euler",
18+
"source_url": "https://projecteuler.net/problem=1"
19+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
section .text
2+
global sum
3+
sum:
4+
; rdi - limit as uint64_t
5+
; rsi - input array of uint64_t, representing factors
6+
; rdx - size of input array as size_t (uint64_t)
7+
; return is a uint64_t in rax
8+
9+
mov r8, rdi
10+
11+
; prologue
12+
push rbp
13+
mov rbp, rsp
14+
sub rsp, r8 ; array of bytes representing each potential multiple
15+
; limit is exclusive
16+
17+
18+
lea rdi, [rsp]
19+
mov rcx, r8
20+
mov al, 0
21+
cld
22+
rep stosb ; initializes array to all "false"
23+
24+
lea rdi, [rsp]
25+
mov rcx, -1
26+
extract_factor:
27+
inc rcx
28+
cmp rcx, rdx
29+
jge end_extraction ; reached end of input array
30+
31+
lodsq
32+
cmp rax, 0
33+
jle extract_factor ; zero has no multiple other than itself and doesn't contribute to the sum.
34+
; negative numbers are not factors
35+
36+
mov r9, rax
37+
fill_multiples:
38+
cmp r9, r8
39+
jge extract_factor ; limit is exclusive
40+
41+
mov byte [rdi + r9], 1 ; sets multiple as "true"
42+
add r9, rax ; gets next multiple
43+
jmp fill_multiples
44+
45+
end_extraction:
46+
xor rax, rax ; accumulator
47+
mov rcx, r8
48+
accumulate_factors:
49+
mov r10b, byte [rdi + rcx]
50+
xor r11, r11
51+
cmp r10b, 1
52+
cmove r11, rcx ; conditional move reduces branching and makes the loop a bit more tight
53+
; it also makes using LOOP easier, reducing overall code size
54+
add rax, r11
55+
loop accumulate_factors
56+
57+
end_sum:
58+
;epilogue
59+
mov rsp, rbp
60+
pop rbp
61+
ret
62+
63+
%ifidn __OUTPUT_FORMAT__,elf64
64+
section .note.GNU-stack noalloc noexec nowrite progbits
65+
%endif
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[54aaab5a-ce86-4edc-8b40-d3ab2400a279]
13+
description = "no multiples within limit"
14+
15+
[361e4e50-c89b-4f60-95ef-5bc5c595490a]
16+
description = "one factor has multiples within limit"
17+
18+
[e644e070-040e-4ae0-9910-93c69fc3f7ce]
19+
description = "more than one multiple within limit"
20+
21+
[607d6eb9-535c-41ce-91b5-3a61da3fa57f]
22+
description = "more than one factor with multiples within limit"
23+
24+
[f47e8209-c0c5-4786-b07b-dc273bf86b9b]
25+
description = "each multiple is only counted once"
26+
27+
[28c4b267-c980-4054-93e9-07723db615ac]
28+
description = "a much larger limit"
29+
30+
[09c4494d-ff2d-4e0f-8421-f5532821ee12]
31+
description = "three factors"
32+
33+
[2d0d5faa-f177-4ad6-bde9-ebb865083751]
34+
description = "factors not relatively prime"
35+
36+
[ece8f2e8-96aa-4166-bbb7-6ce71261e354]
37+
description = "some pairs of factors relatively prime and some not"
38+
39+
[624fdade-6ffb-400e-8472-456a38c171c0]
40+
description = "one factor is a multiple of another"
41+
42+
[949ee7eb-db51-479c-b5cb-4a22b40ac057]
43+
description = "much larger factors"
44+
45+
[41093673-acbd-482c-ab80-d00a0cbedecd]
46+
description = "all numbers are multiples of 1"
47+
48+
[1730453b-baaa-438e-a9c2-d754497b2a76]
49+
description = "no factors means an empty sum"
50+
51+
[214a01e9-f4bf-45bb-80f1-1dce9fbb0310]
52+
description = "the only multiple of 0 is 0"
53+
54+
[c423ae21-a0cb-4ec7-aeb1-32971af5b510]
55+
description = "the factor 0 does not affect the sum of multiples of other factors"
56+
57+
[17053ba9-112f-4ac0-aadb-0519dd836342]
58+
description = "solutions using include-exclude must extend to cardinality greater than 3"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
AS = nasm
2+
3+
CFLAGS = -g -Wall -Wextra -pedantic -Werror
4+
LDFLAGS =
5+
ASFLAGS = -g -F dwarf -Werror
6+
7+
ifeq ($(shell uname -s),Darwin)
8+
ifeq ($(shell sysctl -n hw.optional.arm64 2>/dev/null),1)
9+
ALL_CFLAGS = -target x86_64-apple-darwin
10+
endif
11+
ALL_LDFLAGS = -Wl,-pie
12+
ALL_ASFLAGS = -f macho64 --prefix _
13+
else
14+
ALL_LDFLAGS = -pie -Wl,--fatal-warnings
15+
ALL_ASFLAGS = -f elf64
16+
endif
17+
18+
ALL_CFLAGS += -std=c99 -fPIE -m64 $(CFLAGS)
19+
ALL_LDFLAGS += $(LDFLAGS)
20+
ALL_ASFLAGS += $(ASFLAGS)
21+
22+
C_OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
23+
AS_OBJS = $(patsubst %.asm,%.o,$(wildcard *.asm))
24+
ALL_OBJS = $(filter-out example.o,$(C_OBJS) $(AS_OBJS) vendor/unity.o)
25+
26+
CC_CMD = $(CC) $(ALL_CFLAGS) -c -o $@ $<
27+
28+
all: tests
29+
@./$<
30+
31+
tests: $(ALL_OBJS)
32+
@$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $(ALL_OBJS)
33+
34+
%.o: %.asm
35+
@$(AS) $(ALL_ASFLAGS) -o $@ $<
36+
37+
%.o: %.c
38+
@$(CC_CMD)
39+
40+
vendor/unity.o: vendor/unity.c vendor/unity.h vendor/unity_internals.h
41+
@$(CC_CMD)
42+
43+
clean:
44+
@rm -f *.o vendor/*.o tests
45+
46+
.PHONY: all clean
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
section .text
2+
global sum
3+
sum:
4+
; Provide your implementation here
5+
ret
6+
7+
%ifidn __OUTPUT_FORMAT__,elf64
8+
section .note.GNU-stack noalloc noexec nowrite progbits
9+
%endif
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Version: 0
2+
3+
#include "vendor/unity.h"
4+
5+
#include <stddef.h>
6+
#include <stdint.h>
7+
8+
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
9+
10+
extern uint64_t sum(uint64_t limit, const uint64_t *factors, size_t factor_count);
11+
12+
void setUp(void) {
13+
}
14+
15+
void tearDown(void) {
16+
}
17+
18+
void test_no_multiples_within_limit(void) {
19+
const uint64_t factors[] = {3, 5};
20+
TEST_ASSERT_EQUAL_UINT64(0, sum(1, factors, ARRAY_SIZE(factors)));
21+
}
22+
23+
void test_one_factor_has_multiples_within_limit(void) {
24+
TEST_IGNORE();
25+
const uint64_t factors[] = {3, 5};
26+
TEST_ASSERT_EQUAL_UINT64(3, sum(4, factors, ARRAY_SIZE(factors)));
27+
}
28+
29+
void test_more_than_one_multiple_within_limit(void) {
30+
TEST_IGNORE();
31+
const uint64_t factors[] = {3};
32+
TEST_ASSERT_EQUAL_UINT64(9, sum(7, factors, ARRAY_SIZE(factors)));
33+
}
34+
35+
void test_more_than_one_factor_with_multiples_within_limit(void) {
36+
TEST_IGNORE();
37+
const uint64_t factors[] = {3, 5};
38+
TEST_ASSERT_EQUAL_UINT64(23, sum(10, factors, ARRAY_SIZE(factors)));
39+
}
40+
41+
void test_each_multiple_is_only_counted_once(void) {
42+
TEST_IGNORE();
43+
const uint64_t factors[] = {3, 5};
44+
TEST_ASSERT_EQUAL_UINT64(2318, sum(100, factors, ARRAY_SIZE(factors)));
45+
}
46+
47+
void test_a_much_larger_limit(void) {
48+
TEST_IGNORE();
49+
const uint64_t factors[] = {3, 5};
50+
TEST_ASSERT_EQUAL_UINT64(233168, sum(1000, factors, ARRAY_SIZE(factors)));
51+
}
52+
53+
void test_three_factors(void) {
54+
TEST_IGNORE();
55+
const uint64_t factors[] = {7, 13, 17};
56+
TEST_ASSERT_EQUAL_UINT64(51, sum(20, factors, ARRAY_SIZE(factors)));
57+
}
58+
59+
void test_factors_not_relatively_prime(void) {
60+
TEST_IGNORE();
61+
const uint64_t factors[] = {4, 6};
62+
TEST_ASSERT_EQUAL_UINT64(30, sum(15, factors, ARRAY_SIZE(factors)));
63+
}
64+
65+
void test_some_pairs_of_factors_relatively_prime_and_some_not(void) {
66+
TEST_IGNORE();
67+
const uint64_t factors[] = {5, 6, 8};
68+
TEST_ASSERT_EQUAL_UINT64(4419, sum(150, factors, ARRAY_SIZE(factors)));
69+
}
70+
71+
void test_one_factor_is_a_multiple_of_another(void) {
72+
TEST_IGNORE();
73+
const uint64_t factors[] = {5, 25};
74+
TEST_ASSERT_EQUAL_UINT64(275, sum(51, factors, ARRAY_SIZE(factors)));
75+
}
76+
77+
void test_much_larger_factors(void) {
78+
TEST_IGNORE();
79+
const uint64_t factors[] = {43, 47};
80+
TEST_ASSERT_EQUAL_UINT64(2203160, sum(10000, factors, ARRAY_SIZE(factors)));
81+
}
82+
83+
void test_all_numbers_are_multiples_of_1(void) {
84+
TEST_IGNORE();
85+
const uint64_t factors[] = {1};
86+
TEST_ASSERT_EQUAL_UINT64(4950, sum(100, factors, ARRAY_SIZE(factors)));
87+
}
88+
89+
void test_no_factors_means_an_empty_sum(void) {
90+
TEST_IGNORE();
91+
TEST_ASSERT_EQUAL_UINT64(0, sum(10000, NULL, 0));
92+
}
93+
94+
void test_the_only_multiple_of_0_is_0(void) {
95+
TEST_IGNORE();
96+
const uint64_t factors[] = {0};
97+
TEST_ASSERT_EQUAL_UINT64(0, sum(1, factors, ARRAY_SIZE(factors)));
98+
}
99+
100+
void test_the_factor_0_does_not_affect_the_sum_of_multiples_of_other_factors(void) {
101+
TEST_IGNORE();
102+
const uint64_t factors[] = {3, 0};
103+
TEST_ASSERT_EQUAL_UINT64(3, sum(4, factors, ARRAY_SIZE(factors)));
104+
}
105+
106+
void test_solutions_using_includeexclude_must_extend_to_cardinality_greater_than_3(void) {
107+
TEST_IGNORE();
108+
const uint64_t factors[] = {2, 3, 5, 7, 11};
109+
TEST_ASSERT_EQUAL_UINT64(39614537, sum(10000, factors, ARRAY_SIZE(factors)));
110+
}
111+
112+
int main(void) {
113+
UNITY_BEGIN();
114+
RUN_TEST(test_no_multiples_within_limit);
115+
RUN_TEST(test_one_factor_has_multiples_within_limit);
116+
RUN_TEST(test_more_than_one_multiple_within_limit);
117+
RUN_TEST(test_more_than_one_factor_with_multiples_within_limit);
118+
RUN_TEST(test_each_multiple_is_only_counted_once);
119+
RUN_TEST(test_a_much_larger_limit);
120+
RUN_TEST(test_three_factors);
121+
RUN_TEST(test_factors_not_relatively_prime);
122+
RUN_TEST(test_some_pairs_of_factors_relatively_prime_and_some_not);
123+
RUN_TEST(test_one_factor_is_a_multiple_of_another);
124+
RUN_TEST(test_much_larger_factors);
125+
RUN_TEST(test_all_numbers_are_multiples_of_1);
126+
RUN_TEST(test_no_factors_means_an_empty_sum);
127+
RUN_TEST(test_the_only_multiple_of_0_is_0);
128+
RUN_TEST(test_the_factor_0_does_not_affect_the_sum_of_multiples_of_other_factors);
129+
RUN_TEST(test_solutions_using_includeexclude_must_extend_to_cardinality_greater_than_3);
130+
return UNITY_END();
131+
}

0 commit comments

Comments
 (0)