From d4e1ec2e42d00f1909b2bae247a53f87758282f8 Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Wed, 7 Feb 2024 22:59:18 +0200 Subject: [PATCH] Implement harmonic addition and subtraction --- src/__tests__/fraction.spec.ts | 54 ++++++++++++++++++++++++++++++++++ src/fraction.ts | 28 ++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/__tests__/fraction.spec.ts b/src/__tests__/fraction.spec.ts index 5ed50c8..b8c14ba 100644 --- a/src/__tests__/fraction.spec.ts +++ b/src/__tests__/fraction.spec.ts @@ -371,4 +371,58 @@ describe('Fraction', () => { const fraction = new Fraction(3, 2); expect(fraction.geoRoundTo('10/9')!.equals('10000/6561')).toBe(true); }); + + it('has harmonic addition', () => { + const fraction = new Fraction('7/5'); + expect(fraction.lensAdd('13/11').toFraction()).toBe('91/142'); + }); + + it('has harmonic addition of zero (left)', () => { + const fraction = new Fraction(0); + expect(fraction.lensAdd('3/2').toFraction()).toBe('0'); + }); + + it('has harmonic addition of zero (right)', () => { + const fraction = new Fraction('3/2'); + expect(fraction.lensAdd(0).toFraction()).toBe('0'); + }); + + it('has harmonic addition of zero (both)', () => { + const fraction = new Fraction(0); + expect(fraction.lensAdd(0).toFraction()).toBe('0'); + }); + + it('has harmonic subtraction', () => { + const fraction = new Fraction('7/5'); + expect(fraction.lensSub('13/11').toFraction()).toBe('-91/12'); + }); + + it('has harmonic subtraction of zero (left)', () => { + const fraction = new Fraction(0); + expect(fraction.lensSub('3/2').toFraction()).toBe('0'); + }); + + it('has harmonic subtraction of zero (right)', () => { + const fraction = new Fraction('3/2'); + expect(fraction.lensSub(0).toFraction()).toBe('0'); + }); + + it('has harmonic subtraction of zero (both)', () => { + const fraction = new Fraction(0); + expect(fraction.lensSub(0).toFraction()).toBe('0'); + }); + + it('cancels harmonic addition with harmonic subtraction', () => { + const a = new Fraction( + Math.floor(Math.random() * 1000), + Math.floor(Math.random() * 1000) + 1 + ); + const b = new Fraction( + Math.floor(Math.random() * 1000), + Math.floor(Math.random() * 1000) + 1 + ); + const lensSum = a.lensAdd(b); + expect(lensSum.lensSub(b).equals(a)).toBe(true); + expect(lensSum.lensSub(a).equals(b)).toBe(true); + }); }); diff --git a/src/fraction.ts b/src/fraction.ts index d24adda..85e1234 100644 --- a/src/fraction.ts +++ b/src/fraction.ts @@ -496,6 +496,34 @@ export class Fraction { return new Fraction(this.s * this.n * d - s * n * this.d, this.d * d); } + /** + * Perform harmonic addition of two rational numbers according to the thin lens equation f⁻¹ = u⁻¹ + v⁻¹ + * + * Ex: new Fraction('5/3').lensAdd('3/2') => 15/19 + */ + lensAdd(other: FractionValue) { + const {s, n, d} = new Fraction(other); + if (!n) { + // Based on behavior in the limit where both terms become zero. + return new Fraction({s: 0, n: 0, d: 1}); + } + return new Fraction(this.s * this.n * s * n, this.n * d + n * this.d); + } + + /** + * Perform harmonic subtraction of two rational numbers u⁻¹ = f⁻¹ - v⁻¹ (rearranged thin lens equation) + * + * Ex: new Fraction('15/19').lensSub('3/2') => 5/3 + */ + lensSub(other: FractionValue) { + const {s, n, d} = new Fraction(other); + if (!n) { + // Based on behavior in the limit where both terms become zero. + return new Fraction({s: 0, n: 0, d: 1}); + } + return new Fraction(this.s * this.n * s * n, n * this.d - this.n * d); + } + /** * Multiplies two rational numbers *