diff options
| author | Josh Kingsley <josh@joshkingsley.me> | 2025-11-06 22:31:12 +0200 |
|---|---|---|
| committer | Josh Kingsley <josh@joshkingsley.me> | 2025-11-06 22:31:12 +0200 |
| commit | 6ea86b1b56aebbae7edeb37b01d7bf5cd145bf60 (patch) | |
| tree | bfd0178097a56087471543950caebed5f8280040 /web/src/math | |
| parent | dad5b47f0bb480532043df8d488f5609f731b00d (diff) | |
feat(web): change subvisions
Diffstat (limited to 'web/src/math')
| -rw-r--r-- | web/src/math/Ratio.test.ts | 27 | ||||
| -rw-r--r-- | web/src/math/Ratio.ts | 28 | ||||
| -rw-r--r-- | web/src/math/index.ts | 3 |
3 files changed, 56 insertions, 2 deletions
diff --git a/web/src/math/Ratio.test.ts b/web/src/math/Ratio.test.ts new file mode 100644 index 0000000..da6fef2 --- /dev/null +++ b/web/src/math/Ratio.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, test } from "vitest"; +import Ratio from "./Ratio"; + +describe(Ratio, () => { + describe(Ratio.prototype.add, () => { + test("returns fractions in simplest form", () => { + const a = Ratio.fromInteger(0); + const b = new Ratio(1, 4); + + const c = a.add(b); + expect(c.numerator).toBe(1); + expect(c.denominator).toBe(4); + + const d = c.add(b); + expect(d.numerator).toBe(1); + expect(d.denominator).toBe(2); + + const e = d.add(b); + expect(e.numerator).toBe(3); + expect(e.denominator).toBe(4); + + const f = e.add(b); + expect(f.numerator).toBe(1); + expect(f.denominator).toBe(1); + }); + }); +}); diff --git a/web/src/math/Ratio.ts b/web/src/math/Ratio.ts index 0cca966..e2a1fbf 100644 --- a/web/src/math/Ratio.ts +++ b/web/src/math/Ratio.ts @@ -1,3 +1,5 @@ +import { gcd } from "."; + /** Serializable representation of a ratio. */ export type RatioData = [numerator: number, denominator: number]; @@ -25,8 +27,10 @@ export default class Ratio { throw new RangeError("Ratio demnominator cannot be zero"); } - this.#numerator = numerator; - this.#denominator = denominator; + const divisor = gcd(numerator, denominator); + + this.#numerator = numerator / divisor; + this.#denominator = denominator / divisor; } multiplyRatio(other: Ratio): Ratio { @@ -63,10 +67,22 @@ export default class Ratio { return left < right ? -1 : left > right ? 1 : 0; } + equals(other: Ratio): boolean { + return this.compare(other) === 0; + } + toNumber(): number { return this.numerator / this.denominator; } + toString(): string { + return `${this.numerator}/${this.denominator}`; + } + + [Symbol.for("nodejs.util.inspect.custom")](): string { + return `Ratio { ${this.numerator}/${this.denominator} }`; + } + static fromInteger(n: number): Ratio { return new Ratio(n, 1); } @@ -78,4 +94,12 @@ export default class Ratio { static fromData(ratio: RatioData): Ratio { return new Ratio(ratio[0], ratio[1]); } + + static min(...ratios: Ratio[]): Ratio { + return ratios.reduce((a, b) => (a.compare(b) <= 0 ? a : b)); + } + + static max(...ratios: Ratio[]): Ratio { + return ratios.reduce((a, b) => (a.compare(b) >= 0 ? a : b)); + } } diff --git a/web/src/math/index.ts b/web/src/math/index.ts new file mode 100644 index 0000000..70dbb67 --- /dev/null +++ b/web/src/math/index.ts @@ -0,0 +1,3 @@ +export function gcd(a: number, b: number): number { + return b === 0 ? Math.abs(a) : gcd(b, a % b); +} |
