Skip to content

Commit fcd441c

Browse files
feat: add lucky numbers algorithm
1 parent 1d88e87 commit fcd441c

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

‎Maths/LuckyNumbers.js‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @function getLuckyNumbers
3+
* @description Lucky numbers are generated by a sieve similar to the
4+
* Sieve of Eratosthenes. Starting with natural numbers, every 2nd
5+
* number is removed, then every 3rd, then every 7th, etc.
6+
* Sequence: 1, 3, 7, 9, 13, 15, 21, 25, 31, 33...
7+
* @param {number} limit - Upper limit to generate lucky numbers up to
8+
* @returns {number[]} - Array of lucky numbers up to limit
9+
* @see https://en.wikipedia.org/wiki/Lucky_number
10+
* @example getLuckyNumbers(20) => [1, 3, 7, 9, 13, 15]
11+
* @example getLuckyNumbers(50) => [1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49]
12+
*/
13+
const getLuckyNumbers = (limit) => {
14+
if (!Number.isInteger(limit) || limit < 1) {
15+
throw new TypeError('Input must be a positive integer')
16+
}
17+
let sieve = Array.from({ length: limit }, (_, i) => i + 1).filter(
18+
(n) => n % 2 !== 0
19+
)
20+
let i = 1
21+
while (i < sieve.length && sieve[i] <= sieve.length) {
22+
const step = sieve[i]
23+
sieve = sieve.filter((_, idx) => (idx + 1) % step !== 0)
24+
i++
25+
}
26+
return sieve
27+
}
28+
29+
export { getLuckyNumbers }

‎Maths/test/LuckyNumbers.test.js‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { getLuckyNumbers } from '../LuckyNumbers.js'
2+
3+
describe('getLuckyNumbers', () => {
4+
test('getLuckyNumbers(1) returns [1]', () => {
5+
expect(getLuckyNumbers(1)).toEqual([1])
6+
})
7+
8+
test('getLuckyNumbers(10) returns [1, 3, 7, 9]', () => {
9+
expect(getLuckyNumbers(10)).toEqual([1, 3, 7, 9])
10+
})
11+
12+
test('getLuckyNumbers(20) returns correct list', () => {
13+
expect(getLuckyNumbers(20)).toEqual([1, 3, 7, 9, 13, 15])
14+
})
15+
16+
test('all results are within limit', () => {
17+
const result = getLuckyNumbers(50)
18+
expect(result.every((n) => n <= 50)).toBe(true)
19+
})
20+
21+
test('result is sorted in ascending order', () => {
22+
const result = getLuckyNumbers(50)
23+
expect(result).toEqual([...result].sort((a, b) => a - b))
24+
})
25+
26+
test('1 is always a lucky number', () => {
27+
expect(getLuckyNumbers(10)[0]).toBe(1)
28+
})
29+
30+
test('getLuckyNumbers(50) has correct count', () => {
31+
expect(getLuckyNumbers(50)).toHaveLength(13)
32+
})
33+
34+
test('throws TypeError for zero', () => {
35+
expect(() => getLuckyNumbers(0)).toThrow(TypeError)
36+
})
37+
38+
test('throws TypeError for negative input', () => {
39+
expect(() => getLuckyNumbers(-10)).toThrow(TypeError)
40+
})
41+
42+
test('throws TypeError for float input', () => {
43+
expect(() => getLuckyNumbers(10.5)).toThrow(TypeError)
44+
})
45+
})

0 commit comments

Comments
 (0)