diff --git a/Maths/LowestCommonMultiple.ts b/Maths/LowestCommonMultiple.ts new file mode 100644 index 00000000..fe311b64 --- /dev/null +++ b/Maths/LowestCommonMultiple.ts @@ -0,0 +1,58 @@ +/** + * @function LowestCommonMultiple + * @description Determine the lowest common multiple of a group of numbers. + * @param {Number[]} nums - An array of numbers. + * @return {Number} - The lowest common multiple. + * @see https://www.mathsisfun.com/least-common-multiple.html + * @example LowestCommonMultiple(3, 4) = 12 + * @example LowestCommonMultiple(8, 6) = 24 + * @example LowestCommonMultiple(5, 8, 3) = 120 + */ + +import { greatestCommonFactor } from "./GreatestCommonFactor"; + +//A naive solution which requires no additional mathematical algorithm + +export const naiveLCM = (nums: number[]): number => { + if (nums.some((num) => num < 0)) { + throw new Error("numbers must be positive to determine lowest common multiple"); + } + + if (nums.length === 0) { + throw new Error("at least one number must be passed in"); + } + + const max_num = Math.max(...nums); + let current_num = max_num; + + while (true) { + if (nums.every((num) => current_num % num === 0)){ + return current_num; + } else { + current_num += max_num; + } + } +} + +//A typically more efficient solution which requires prior knowledge of GCF +//Note that due to utilizing GCF, which requires natural numbers, this method only accepts natural numbers. + +export const binaryLCM = (a: number, b: number): number => { + if (a < 0 || b < 0) { + throw new Error("numbers must be positive to determine lowest common multiple"); + } + + if (!Number.isInteger(a) || !Number.isInteger(b)) { + throw new Error("this method, which utilizes GCF, requires natural numbers."); + } + + return a * b / greatestCommonFactor([a, b]); +} + +export const lowestCommonMultiple = (nums: number[]): number => { + if (nums.length === 0) { + throw new Error("at least one number must be passed in"); + } + + return nums.reduce(binaryLCM); +} \ No newline at end of file diff --git a/Maths/test/LowestCommonMultiple.test.ts b/Maths/test/LowestCommonMultiple.test.ts new file mode 100644 index 00000000..00dac6d3 --- /dev/null +++ b/Maths/test/LowestCommonMultiple.test.ts @@ -0,0 +1,58 @@ +import { binaryLCM, lowestCommonMultiple, naiveLCM } from "../LowestCommonMultiple"; + +describe("naiveLCM", () => { + test.each([[[3, 4], 12], [[8, 6], 24], [[5, 8, 3], 120], [[0.8, 0.4], 0.8]])( + "of given two numbers is correct", + (nums, expected) => { + expect(naiveLCM(nums)).toBe(expected); + }, + ); + + test("only positive numbers should be accepted", () => { + expect(() => naiveLCM([-2, -3])).toThrowError( + "numbers must be positive to determine lowest common multiple", + ); + }); + + test("at least one number must be passed in", () => { + expect(() => naiveLCM([])).toThrowError( + "at least one number must be passed in", + ); + }); +}); + +describe("binaryLCM", () => { + test.each([[3, 4, 12], [8, 6, 24], [8, 16, 16]])( + "of given two numbers is correct", + (numa, numb, expected) => { + expect(binaryLCM(numa, numb)).toBe(expected); + }, + ); + + test("only whole numbers should be accepted", () => { + expect(() => binaryLCM(-2, -3)).toThrowError( + "numbers must be positive to determine lowest common multiple", + ); + }); +}); + +describe("lowestCommonMultiple", () => { + test.each([[[3, 4], 12], [[8, 6], 24], [[5, 8, 3], 120], [[8, 16], 16]])( + "of given two numbers is correct", + (nums, expected) => { + expect(lowestCommonMultiple(nums)).toBe(expected); + }, + ); + + test("only positive numbers should be accepted", () => { + expect(() => lowestCommonMultiple([-2, -3])).toThrowError( + "numbers must be positive to determine lowest common multiple", + ); + }); + + test("at least one number must be passed in", () => { + expect(() => lowestCommonMultiple([])).toThrowError( + "at least one number must be passed in", + ); + }); +});