From 1d88e87116626e08a066b8434575d93b054150d4 Mon Sep 17 00:00:00 2001 From: sarthakgupta1907-cell Date: Mon, 9 Nov 2026 12:38:07 +0530 Subject: [PATCH 1/2] feat: add interquartile range algorithm --- Maths/InterquartileRange.js | 31 ++++++++++++++++++++ Maths/test/InterquartileRange.test.js | 41 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Maths/InterquartileRange.js create mode 100644 Maths/test/InterquartileRange.test.js diff --git a/Maths/InterquartileRange.js b/Maths/InterquartileRange.js new file mode 100644 index 0000000000..691460487f --- /dev/null +++ b/Maths/InterquartileRange.js @@ -0,0 +1,31 @@ +/** + * @function interquartileRange + * @description Calculates the interquartile range (IQR) of an array. + * IQR is the difference between the 75th and 25th percentiles, + * representing the middle 50% spread of the data. + * @param {number[]} arr - An array of numbers + * @returns {number} - The interquartile range + * @see https://en.wikipedia.org/wiki/Interquartile_range + * @example interquartileRange([1, 2, 3, 4, 5]) => 2 + * @example interquartileRange([1, 2, 3, 4, 5, 6, 7]) => 3 + */ +const interquartileRange = (arr) => { + if (!Array.isArray(arr) || arr.length === 0) { + throw new TypeError('Input must be a non-empty array') + } + if (!arr.every((n) => typeof n === 'number' && !isNaN(n))) { + throw new TypeError('All elements must be numbers') + } + const sorted = [...arr].sort((a, b) => a - b) + const getPercentile = (data, p) => { + if (p === 0) return data[0] + if (p === 100) return data[data.length - 1] + const index = (p / 100) * (data.length - 1) + const lower = Math.floor(index) + const upper = Math.ceil(index) + return data[lower] + (data[upper] - data[lower]) * (index - lower) + } + return getPercentile(sorted, 75) - getPercentile(sorted, 25) +} + +export { interquartileRange } diff --git a/Maths/test/InterquartileRange.test.js b/Maths/test/InterquartileRange.test.js new file mode 100644 index 0000000000..e851cb8654 --- /dev/null +++ b/Maths/test/InterquartileRange.test.js @@ -0,0 +1,41 @@ +import { interquartileRange } from '../InterquartileRange.js' + +describe('interquartileRange', () => { + test('interquartileRange([1,2,3,4,5]) returns 2', () => { + expect(interquartileRange([1, 2, 3, 4, 5])).toBeCloseTo(2) + }) + + test('interquartileRange([1,2,3,4,5,6,7]) returns 3', () => { + expect(interquartileRange([1, 2, 3, 4, 5, 6, 7])).toBeCloseTo(3) + }) + + test('interquartileRange identical elements returns 0', () => { + expect(interquartileRange([5, 5, 5, 5])).toBe(0) + }) + + test('interquartileRange works with unsorted array', () => { + expect(interquartileRange([5, 3, 1, 4, 2])).toBeCloseTo(2) + }) + + test('interquartileRange works with negative numbers', () => { + expect(interquartileRange([-3, -2, -1, 0, 1, 2, 3])).toBeCloseTo(3) + }) + + test('interquartileRange does not mutate original array', () => { + const arr = [5, 3, 1, 4, 2] + interquartileRange(arr) + expect(arr).toEqual([5, 3, 1, 4, 2]) + }) + + test('throws TypeError for empty array', () => { + expect(() => interquartileRange([])).toThrow(TypeError) + }) + + test('throws TypeError for non-array input', () => { + expect(() => interquartileRange('123')).toThrow(TypeError) + }) + + test('throws TypeError for array with non-numbers', () => { + expect(() => interquartileRange([1, 'two', 3])).toThrow(TypeError) + }) +}) From fcd441cf0a8dee34cd99a18c311986845a722ea8 Mon Sep 17 00:00:00 2001 From: sarthakgupta1907-cell Date: Mon, 9 Nov 2026 12:47:20 +0530 Subject: [PATCH 2/2] feat: add lucky numbers algorithm --- Maths/LuckyNumbers.js | 29 +++++++++++++++++++++ Maths/test/LuckyNumbers.test.js | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 Maths/LuckyNumbers.js create mode 100644 Maths/test/LuckyNumbers.test.js diff --git a/Maths/LuckyNumbers.js b/Maths/LuckyNumbers.js new file mode 100644 index 0000000000..5f1c426a48 --- /dev/null +++ b/Maths/LuckyNumbers.js @@ -0,0 +1,29 @@ +/** + * @function getLuckyNumbers + * @description Lucky numbers are generated by a sieve similar to the + * Sieve of Eratosthenes. Starting with natural numbers, every 2nd + * number is removed, then every 3rd, then every 7th, etc. + * Sequence: 1, 3, 7, 9, 13, 15, 21, 25, 31, 33... + * @param {number} limit - Upper limit to generate lucky numbers up to + * @returns {number[]} - Array of lucky numbers up to limit + * @see https://en.wikipedia.org/wiki/Lucky_number + * @example getLuckyNumbers(20) => [1, 3, 7, 9, 13, 15] + * @example getLuckyNumbers(50) => [1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49] + */ +const getLuckyNumbers = (limit) => { + if (!Number.isInteger(limit) || limit < 1) { + throw new TypeError('Input must be a positive integer') + } + let sieve = Array.from({ length: limit }, (_, i) => i + 1).filter( + (n) => n % 2 !== 0 + ) + let i = 1 + while (i < sieve.length && sieve[i] <= sieve.length) { + const step = sieve[i] + sieve = sieve.filter((_, idx) => (idx + 1) % step !== 0) + i++ + } + return sieve +} + +export { getLuckyNumbers } diff --git a/Maths/test/LuckyNumbers.test.js b/Maths/test/LuckyNumbers.test.js new file mode 100644 index 0000000000..4173c0d59a --- /dev/null +++ b/Maths/test/LuckyNumbers.test.js @@ -0,0 +1,45 @@ +import { getLuckyNumbers } from '../LuckyNumbers.js' + +describe('getLuckyNumbers', () => { + test('getLuckyNumbers(1) returns [1]', () => { + expect(getLuckyNumbers(1)).toEqual([1]) + }) + + test('getLuckyNumbers(10) returns [1, 3, 7, 9]', () => { + expect(getLuckyNumbers(10)).toEqual([1, 3, 7, 9]) + }) + + test('getLuckyNumbers(20) returns correct list', () => { + expect(getLuckyNumbers(20)).toEqual([1, 3, 7, 9, 13, 15]) + }) + + test('all results are within limit', () => { + const result = getLuckyNumbers(50) + expect(result.every((n) => n <= 50)).toBe(true) + }) + + test('result is sorted in ascending order', () => { + const result = getLuckyNumbers(50) + expect(result).toEqual([...result].sort((a, b) => a - b)) + }) + + test('1 is always a lucky number', () => { + expect(getLuckyNumbers(10)[0]).toBe(1) + }) + + test('getLuckyNumbers(50) has correct count', () => { + expect(getLuckyNumbers(50)).toHaveLength(13) + }) + + test('throws TypeError for zero', () => { + expect(() => getLuckyNumbers(0)).toThrow(TypeError) + }) + + test('throws TypeError for negative input', () => { + expect(() => getLuckyNumbers(-10)).toThrow(TypeError) + }) + + test('throws TypeError for float input', () => { + expect(() => getLuckyNumbers(10.5)).toThrow(TypeError) + }) +})