summaryrefslogtreecommitdiff
path: root/web/src/math
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/math')
-rw-r--r--web/src/math/Ratio.test.ts27
-rw-r--r--web/src/math/Ratio.ts28
-rw-r--r--web/src/math/index.ts3
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);
+}