Skip to content

Commit cf737c0

Browse files
authored
Add sieve exercise (#283)
1 parent 518e4ae commit cf737c0

File tree

13 files changed

+3692
-0
lines changed

13 files changed

+3692
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@
119119
"transforming"
120120
]
121121
},
122+
{
123+
"slug": "sieve",
124+
"name": "Sieve",
125+
"uuid": "6cc147ff-65cb-417d-9883-6b7cf910e828",
126+
"practices": [],
127+
"prerequisites": [],
128+
"difficulty": 5
129+
},
122130
{
123131
"slug": "space-age",
124132
"name": "Space Age",
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Instructions
2+
3+
Your task is to create a program that implements the Sieve of Eratosthenes algorithm to find all prime numbers less than or equal to a given number.
4+
5+
A prime number is a number larger than 1 that is only divisible by 1 and itself.
6+
For example, 2, 3, 5, 7, 11, and 13 are prime numbers.
7+
By contrast, 6 is _not_ a prime number as it not only divisible by 1 and itself, but also by 2 and 3.
8+
9+
To use the Sieve of Eratosthenes, first, write out all the numbers from 2 up to and including your given number.
10+
Then, follow these steps:
11+
12+
1. Find the next unmarked number (skipping over marked numbers).
13+
This is a prime number.
14+
2. Mark all the multiples of that prime number as **not** prime.
15+
16+
Repeat the steps until you've gone through every number.
17+
At the end, all the unmarked numbers are prime.
18+
19+
~~~~exercism/note
20+
The Sieve of Eratosthenes marks off multiples of each prime using addition (repeatedly adding the prime) or multiplication (directly computing its multiples), rather than checking each number for divisibility.
21+
22+
The tests don't check that you've implemented the algorithm, only that you've come up with the correct primes.
23+
~~~~
24+
25+
## Example
26+
27+
Let's say you're finding the primes less than or equal to 10.
28+
29+
- Write out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked.
30+
31+
```text
32+
2 3 4 5 6 7 8 9 10
33+
```
34+
35+
- 2 is unmarked and is therefore a prime.
36+
Mark 4, 6, 8 and 10 as "not prime".
37+
38+
```text
39+
2 3 [4] 5 [6] 7 [8] 9 [10]
40+
41+
```
42+
43+
- 3 is unmarked and is therefore a prime.
44+
Mark 6 and 9 as not prime _(marking 6 is optional - as it's already been marked)_.
45+
46+
```text
47+
2 3 [4] 5 [6] 7 [8] [9] [10]
48+
49+
```
50+
51+
- 4 is marked as "not prime", so we skip over it.
52+
53+
```text
54+
2 3 [4] 5 [6] 7 [8] [9] [10]
55+
56+
```
57+
58+
- 5 is unmarked and is therefore a prime.
59+
Mark 10 as not prime _(optional - as it's already been marked)_.
60+
61+
```text
62+
2 3 [4] 5 [6] 7 [8] [9] [10]
63+
64+
```
65+
66+
- 6 is marked as "not prime", so we skip over it.
67+
68+
```text
69+
2 3 [4] 5 [6] 7 [8] [9] [10]
70+
71+
```
72+
73+
- 7 is unmarked and is therefore a prime.
74+
75+
```text
76+
2 3 [4] 5 [6] 7 [8] [9] [10]
77+
78+
```
79+
80+
- 8 is marked as "not prime", so we skip over it.
81+
82+
```text
83+
2 3 [4] 5 [6] 7 [8] [9] [10]
84+
85+
```
86+
87+
- 9 is marked as "not prime", so we skip over it.
88+
89+
```text
90+
2 3 [4] 5 [6] 7 [8] [9] [10]
91+
92+
```
93+
94+
- 10 is marked as "not prime", so we stop as there are no more numbers to check.
95+
96+
```text
97+
2 3 [4] 5 [6] 7 [8] [9] [10]
98+
99+
```
100+
101+
You've examined all the numbers and found that 2, 3, 5, and 7 are still unmarked, meaning they're the primes less than or equal to 10.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Introduction
2+
3+
You bought a big box of random computer parts at a garage sale.
4+
You've started putting the parts together to build custom computers.
5+
6+
You want to test the performance of different combinations of parts, and decide to create your own benchmarking program to see how your computers compare.
7+
You choose the famous "Sieve of Eratosthenes" algorithm, an ancient algorithm, but one that should push your computers to the limits.
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+
"sieve.asm"
8+
],
9+
"test": [
10+
"sieve_test.c"
11+
],
12+
"example": [
13+
".meta/example.asm"
14+
]
15+
},
16+
"blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.",
17+
"source": "Sieve of Eratosthenes at Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"
19+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
section .text
2+
global primes
3+
primes:
4+
; rdi - output array of uint32_t
5+
; esi - limit
6+
; output array should be modified in place
7+
; the size of array is returned in eax
8+
9+
xor rax, rax
10+
cmp esi, 2
11+
jl sieve_end ; if limit is less than 2 returns right away
12+
13+
; prologue
14+
; creates an array of bytes on the stack to represent possible primes
15+
mov r8d, esi
16+
inc r8d ; limit is inclusive
17+
18+
push rbp
19+
mov rbp, rsp
20+
sub rsp, r8
21+
lea rsi, [rsp]
22+
23+
; sets all bytes to "true"
24+
mov r10, rdi
25+
mov rdi, rsi
26+
mov rcx, r8
27+
mov al, 1
28+
cld
29+
rep stosb
30+
31+
; loops over the array to get factors
32+
mov rdi, r10
33+
mov r11, rsi
34+
mov rcx, 2 ; current number to check
35+
xor r9, r9 ; counter for num of primes
36+
37+
get_primes_loop:
38+
lodsb
39+
cmp al, 1
40+
je store_prime
41+
42+
next_factor:
43+
inc rcx
44+
cmp rcx, r8
45+
jl get_primes_loop
46+
47+
; epilogue
48+
mov rax, r9
49+
mov rsp, rbp
50+
pop rbp
51+
52+
sieve_end:
53+
ret
54+
55+
store_prime:
56+
mov rax, rcx
57+
stosd
58+
inc r9
59+
60+
remove_composites:
61+
add rax, rcx
62+
cmp rax, r8
63+
jge next_factor
64+
65+
mov byte [r11 + rax - 2], 0
66+
jmp remove_composites
67+
68+
%ifidn __OUTPUT_FORMAT__,elf64
69+
section .note.GNU-stack noalloc noexec nowrite progbits
70+
%endif
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
[88529125-c4ce-43cc-bb36-1eb4ddd7b44f]
13+
description = "no primes under two"
14+
15+
[4afe9474-c705-4477-9923-840e1024cc2b]
16+
description = "find first prime"
17+
18+
[974945d8-8cd9-4f00-9463-7d813c7f17b7]
19+
description = "find primes up to 10"
20+
21+
[2e2417b7-3f3a-452a-8594-b9af08af6d82]
22+
description = "limit is prime"
23+
24+
[92102a05-4c7c-47de-9ed0-b7d5fcd00f21]
25+
description = "find primes up to 1000"

exercises/practice/sieve/Makefile

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

exercises/practice/sieve/sieve.asm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
section .text
2+
global primes
3+
primes:
4+
; Provide your implementation here
5+
ret
6+
7+
%ifidn __OUTPUT_FORMAT__,elf64
8+
section .note.GNU-stack noalloc noexec nowrite progbits
9+
%endif

exercises/practice/sieve/sieve_test.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Version: 0
2+
3+
#include "vendor/unity.h"
4+
5+
#define BUFFER_SIZE 200
6+
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
7+
8+
extern uint32_t primes(uint32_t *buffer, uint32_t limit);
9+
10+
void setUp(void) {
11+
}
12+
13+
void tearDown(void) {
14+
}
15+
16+
void test_no_primes_under_two(void) {
17+
uint32_t buffer[BUFFER_SIZE];
18+
19+
TEST_ASSERT_EQUAL_UINT32(0, primes(buffer, 1));
20+
}
21+
22+
void test_find_first_prime(void) {
23+
TEST_IGNORE();
24+
uint32_t buffer[BUFFER_SIZE];
25+
26+
uint32_t expected[] = {2};
27+
28+
TEST_ASSERT_EQUAL_UINT32(ARRAY_SIZE(expected), primes(buffer, 2));
29+
TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, buffer, ARRAY_SIZE(expected));
30+
}
31+
32+
void test_find_primes_up_to_10(void) {
33+
TEST_IGNORE();
34+
uint32_t buffer[BUFFER_SIZE];
35+
36+
uint32_t expected[] = {2, 3, 5, 7};
37+
38+
TEST_ASSERT_EQUAL_UINT32(ARRAY_SIZE(expected), primes(buffer, 10));
39+
TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, buffer, ARRAY_SIZE(expected));
40+
}
41+
42+
void test_limit_is_prime(void) {
43+
TEST_IGNORE();
44+
uint32_t buffer[BUFFER_SIZE];
45+
46+
uint32_t expected[] = {2, 3, 5, 7, 11, 13};
47+
48+
TEST_ASSERT_EQUAL_UINT32(ARRAY_SIZE(expected), primes(buffer, 13));
49+
TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, buffer, ARRAY_SIZE(expected));
50+
}
51+
52+
void test_find_primes_up_to_1000(void) {
53+
TEST_IGNORE();
54+
uint32_t buffer[BUFFER_SIZE];
55+
56+
uint32_t expected[] = {
57+
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
58+
71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
59+
151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
60+
233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
61+
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
62+
419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499,
63+
503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
64+
607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691,
65+
701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
66+
811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907,
67+
911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
68+
};
69+
70+
TEST_ASSERT_EQUAL_UINT32(ARRAY_SIZE(expected), primes(buffer, 1000));
71+
TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, buffer, ARRAY_SIZE(expected));
72+
}
73+
74+
int main(void) {
75+
UNITY_BEGIN();
76+
RUN_TEST(test_no_primes_under_two);
77+
RUN_TEST(test_find_first_prime);
78+
RUN_TEST(test_find_primes_up_to_10);
79+
RUN_TEST(test_limit_is_prime);
80+
RUN_TEST(test_find_primes_up_to_1000);
81+
return UNITY_END();
82+
}

0 commit comments

Comments
 (0)