Skip to content

Commit 710470a

Browse files
committed
feat: add prime sieve and generator
1 parent eca3906 commit 710470a

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

Maths/Prime.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const assert = (condition: boolean, message: string) => {
2+
if (!condition) throw Error(message);
3+
};
4+
5+
/**
6+
* https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
7+
* @param limit An integer _n_ > 1
8+
* @returns All prime numbers from 2 through {@linkcode limit}
9+
*/
10+
export function sieveOfEratosthenes(limit: number): number[] {
11+
assert(limit > 1, "limit should be an integer greater than 1");
12+
assert(Number.isInteger(limit), "limit should be an integer greater than 1");
13+
14+
const maybePrime: boolean[] = new Array(limit + 1).fill(true);
15+
for (let i = 2; i * i <= limit; i++) {
16+
if (!maybePrime[i]) continue;
17+
for (let j = i * i; j <= limit; j += i) {
18+
maybePrime[j] = false;
19+
}
20+
}
21+
22+
return maybePrime
23+
.reduce(
24+
(primes, isPrime, number) => (isPrime ? [...primes, number] : primes),
25+
[] as number[]
26+
)
27+
.slice(2);
28+
}
29+
30+
/**
31+
* Generator that yields primes.
32+
*
33+
* Inspired by https://gist.github.com/e-nikolov/cd94db0de2a6b70da144124ae93a6458
34+
*/
35+
export function* primeGenerator() {
36+
type NumberGen = Generator<number, void, any>;
37+
38+
function* filter(input: NumberGen, prime: number): NumberGen {
39+
while (true) {
40+
const { done, value } = input.next();
41+
if (done) break;
42+
if (value % prime !== 0) yield value;
43+
}
44+
}
45+
46+
let chain: NumberGen = (function* () {
47+
let i = 2;
48+
while (true) yield i++;
49+
})();
50+
51+
while (true) {
52+
const { done, value } = chain.next();
53+
if (done) break;
54+
yield value;
55+
chain = filter(chain, value);
56+
}
57+
}

Maths/test/Prime.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { primeGenerator, sieveOfEratosthenes } from "../Prime";
2+
3+
describe(sieveOfEratosthenes, () => {
4+
test.each([-1, 0, 1, 2.123, 1337.80085])(
5+
"should throw an error when given an invalid limit=%d",
6+
(invalidLimit) => {
7+
expect(() => sieveOfEratosthenes(invalidLimit)).toThrow();
8+
}
9+
);
10+
test.each([
11+
[2, [2]],
12+
[3, [2, 3]],
13+
[4, [2, 3]],
14+
[5, [2, 3, 5]],
15+
[6, [2, 3, 5]],
16+
[7, [2, 3, 5, 7]],
17+
[8, [2, 3, 5, 7]],
18+
[9, [2, 3, 5, 7]],
19+
])(
20+
"should return the expected list of primes for limit=%i",
21+
(limit, expected) => {
22+
expect(sieveOfEratosthenes(limit)).toEqual(expected);
23+
}
24+
);
25+
});
26+
27+
describe(primeGenerator, () => {
28+
it("should generate prime numbers", () => {
29+
const expectedPrimes = sieveOfEratosthenes(123);
30+
const primeGen = primeGenerator();
31+
for (const prime of expectedPrimes) {
32+
expect(primeGen.next().value).toBe(prime);
33+
}
34+
});
35+
});

0 commit comments

Comments
 (0)