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 1b86c2a9e6df97ac719ec891edc64e8584d5b7e3 Mon Sep 17 00:00:00 2001 From: sarthakgupta1907-cell Date: Mon, 9 Nov 2026 12:41:07 +0530 Subject: [PATCH 2/2] feat: add Zeckendorf representation algorithm --- Maths/ZeckendorfRepresentation.js | 32 +++++++++++++ Maths/test/ZeckendorfRepresentation.test.js | 51 +++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 Maths/ZeckendorfRepresentation.js create mode 100644 Maths/test/ZeckendorfRepresentation.test.js diff --git a/Maths/ZeckendorfRepresentation.js b/Maths/ZeckendorfRepresentation.js new file mode 100644 index 0000000000..6bcbfbbf80 --- /dev/null +++ b/Maths/ZeckendorfRepresentation.js @@ -0,0 +1,32 @@ +/** + * @function zeckendorfRepresentation + * @description Zeckendorf's theorem states that every positive integer + * can be uniquely represented as a sum of non-consecutive Fibonacci numbers. + * This function returns those Fibonacci numbers. + * @param {number} n - A positive integer + * @returns {number[]} - Array of Fibonacci numbers that sum to n + * @see https://en.wikipedia.org/wiki/Zeckendorf%27s_theorem + * @example zeckendorfRepresentation(11) => [8, 3] + * @example zeckendorfRepresentation(100) => [89, 8, 3] + */ +const zeckendorfRepresentation = (n) => { + if (!Number.isInteger(n) || n < 1) { + throw new TypeError('Input must be a positive integer') + } + const fibs = [1, 2] + while (fibs[fibs.length - 1] < n) { + const len = fibs.length + fibs.push(fibs[len - 1] + fibs[len - 2]) + } + const result = [] + let remaining = n + for (let i = fibs.length - 1; i >= 0 && remaining > 0; i--) { + if (fibs[i] <= remaining) { + result.push(fibs[i]) + remaining -= fibs[i] + } + } + return result +} + +export { zeckendorfRepresentation } diff --git a/Maths/test/ZeckendorfRepresentation.test.js b/Maths/test/ZeckendorfRepresentation.test.js new file mode 100644 index 0000000000..a20b8b446e --- /dev/null +++ b/Maths/test/ZeckendorfRepresentation.test.js @@ -0,0 +1,51 @@ +import { zeckendorfRepresentation } from '../ZeckendorfRepresentation.js' + +describe('zeckendorfRepresentation', () => { + test('zeckendorfRepresentation(1) returns [1]', () => { + expect(zeckendorfRepresentation(1)).toEqual([1]) + }) + + test('zeckendorfRepresentation(2) returns [2]', () => { + expect(zeckendorfRepresentation(2)).toEqual([2]) + }) + + test('zeckendorfRepresentation(3) returns [3]', () => { + expect(zeckendorfRepresentation(3)).toEqual([3]) + }) + + test('zeckendorfRepresentation(11) returns [8, 3]', () => { + expect(zeckendorfRepresentation(11)).toEqual([8, 3]) + }) + + test('zeckendorfRepresentation(100) returns [89, 8, 3]', () => { + expect(zeckendorfRepresentation(100)).toEqual([89, 8, 3]) + }) + + test('result sums to original number', () => { + const result = zeckendorfRepresentation(50) + const sum = result.reduce((acc, n) => acc + n, 0) + expect(sum).toBe(50) + }) + + test('no two consecutive Fibonacci numbers in result', () => { + const fibs = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] + const result = zeckendorfRepresentation(100) + for (let i = 0; i < result.length - 1; i++) { + const idx1 = fibs.indexOf(result[i]) + const idx2 = fibs.indexOf(result[i + 1]) + expect(Math.abs(idx1 - idx2)).toBeGreaterThan(1) + } + }) + + test('throws TypeError for zero', () => { + expect(() => zeckendorfRepresentation(0)).toThrow(TypeError) + }) + + test('throws TypeError for negative input', () => { + expect(() => zeckendorfRepresentation(-11)).toThrow(TypeError) + }) + + test('throws TypeError for float input', () => { + expect(() => zeckendorfRepresentation(11.5)).toThrow(TypeError) + }) +})