From 966f4c2794f4511e0bb3e4c991557ca0439073db Mon Sep 17 00:00:00 2001 From: s-kybound Date: Sat, 13 Apr 2024 22:16:37 +0800 Subject: [PATCH] implement gcd and lcm --- src/stdlib/__tests__/base.ts | 85 ++++++++++++++++++++++++++++-------- src/stdlib/base.ts | 84 ++++++++++++++++++++++++++++++----- src/stdlib/core-math.ts | 12 +++-- 3 files changed, 148 insertions(+), 33 deletions(-) diff --git a/src/stdlib/__tests__/base.ts b/src/stdlib/__tests__/base.ts index 503189a..70a9500 100644 --- a/src/stdlib/__tests__/base.ts +++ b/src/stdlib/__tests__/base.ts @@ -1,5 +1,5 @@ -import * as base from '../base'; -import { SchemeInteger } from '../core-math'; +import * as base from "../base"; +import { SchemeInteger } from "../core-math"; function makeInteger(n: number): SchemeInteger { return SchemeInteger.build(n); @@ -22,7 +22,9 @@ test("modulo works with negative numbers", () => { expect(base.modulo(makeInteger(-5), makeInteger(7))).toEqual(makeInteger(2)); expect(base.modulo(makeInteger(-5), makeInteger(8))).toEqual(makeInteger(3)); expect(base.modulo(makeInteger(-5), makeInteger(9))).toEqual(makeInteger(4)); - expect(base.modulo(makeInteger(-5), makeInteger(-4))).toEqual(makeInteger(-1)); + expect(base.modulo(makeInteger(-5), makeInteger(-4))).toEqual( + makeInteger(-1), + ); }); test("quotient works with positive numbers", () => { @@ -31,28 +33,73 @@ test("quotient works with positive numbers", () => { expect(base.quotient(makeInteger(5), makeInteger(6))).toEqual(makeInteger(0)); expect(base.quotient(makeInteger(5), makeInteger(7))).toEqual(makeInteger(0)); expect(base.quotient(makeInteger(5), makeInteger(8))).toEqual(makeInteger(0)); - expect(base.quotient(makeInteger(5), makeInteger(-4))).toEqual(makeInteger(-1)); + expect(base.quotient(makeInteger(5), makeInteger(-4))).toEqual( + makeInteger(-1), + ); }); test("quotient works with negative numbers", () => { - expect(base.quotient(makeInteger(-5), makeInteger(3))).toEqual(makeInteger(-1)); - expect(base.quotient(makeInteger(-5), makeInteger(5))).toEqual(makeInteger(-1)); - expect(base.quotient(makeInteger(-5), makeInteger(6))).toEqual(makeInteger(0)); - expect(base.quotient(makeInteger(-5), makeInteger(7))).toEqual(makeInteger(0)); - expect(base.quotient(makeInteger(-5), makeInteger(8))).toEqual(makeInteger(0)); - expect(base.quotient(makeInteger(-5), makeInteger(-4))).toEqual(makeInteger(1)); + expect(base.quotient(makeInteger(-5), makeInteger(3))).toEqual( + makeInteger(-1), + ); + expect(base.quotient(makeInteger(-5), makeInteger(5))).toEqual( + makeInteger(-1), + ); + expect(base.quotient(makeInteger(-5), makeInteger(6))).toEqual( + makeInteger(0), + ); + expect(base.quotient(makeInteger(-5), makeInteger(7))).toEqual( + makeInteger(0), + ); + expect(base.quotient(makeInteger(-5), makeInteger(8))).toEqual( + makeInteger(0), + ); + expect(base.quotient(makeInteger(-5), makeInteger(-4))).toEqual( + makeInteger(1), + ); }); test("remainder works with positive numbers", () => { - expect(base.remainder(makeInteger(5), makeInteger(3))).toEqual(makeInteger(2)); - expect(base.remainder(makeInteger(5), makeInteger(5))).toEqual(makeInteger(0)); - expect(base.remainder(makeInteger(5), makeInteger(6))).toEqual(makeInteger(5)); - expect(base.remainder(makeInteger(5), makeInteger(7))).toEqual(makeInteger(5)); + expect(base.remainder(makeInteger(5), makeInteger(3))).toEqual( + makeInteger(2), + ); + expect(base.remainder(makeInteger(5), makeInteger(5))).toEqual( + makeInteger(0), + ); + expect(base.remainder(makeInteger(5), makeInteger(6))).toEqual( + makeInteger(5), + ); + expect(base.remainder(makeInteger(5), makeInteger(7))).toEqual( + makeInteger(5), + ); }); test("remainder works with negative numbers", () => { - expect(base.remainder(makeInteger(-5), makeInteger(3))).toEqual(makeInteger(-2)); - expect(base.remainder(makeInteger(-5), makeInteger(5))).toEqual(makeInteger(0)); - expect(base.remainder(makeInteger(-5), makeInteger(6))).toEqual(makeInteger(-5)); - expect(base.remainder(makeInteger(-5), makeInteger(7))).toEqual(makeInteger(-5)); -}); \ No newline at end of file + expect(base.remainder(makeInteger(-5), makeInteger(3))).toEqual( + makeInteger(-2), + ); + expect(base.remainder(makeInteger(-5), makeInteger(5))).toEqual( + makeInteger(0), + ); + expect(base.remainder(makeInteger(-5), makeInteger(6))).toEqual( + makeInteger(-5), + ); + expect(base.remainder(makeInteger(-5), makeInteger(7))).toEqual( + makeInteger(-5), + ); +}); + +test("gcd", () => { + expect(base.gcd(makeInteger(8), makeInteger(12))).toEqual(makeInteger(4)); + expect(base.gcd(makeInteger(8), makeInteger(13))).toEqual(makeInteger(1)); + expect(base.gcd(makeInteger(0), makeInteger(13))).toEqual(makeInteger(13)); + expect(base.gcd(makeInteger(0), makeInteger(0))).toEqual(makeInteger(0)); + expect(base.gcd(makeInteger(4), makeInteger(-2))).toEqual(makeInteger(2)); +}); + +test("lcm", () => { + expect(base.lcm(makeInteger(8), makeInteger(12))).toEqual(makeInteger(24)); + expect(base.lcm(makeInteger(8), makeInteger(13))).toEqual(makeInteger(104)); + expect(base.lcm(makeInteger(0), makeInteger(13))).toEqual(makeInteger(0)); + expect(base.lcm(makeInteger(4), makeInteger(-2))).toEqual(makeInteger(4)); +}); diff --git a/src/stdlib/base.ts b/src/stdlib/base.ts index 5927650..40a76c3 100644 --- a/src/stdlib/base.ts +++ b/src/stdlib/base.ts @@ -240,7 +240,10 @@ export const $47$: Function = ( export const abs: Function = (n: core.SchemeNumber[]) => negative$63$(n) ? atomic_negate(n) : n; -export const quotient: Function = (a: core.SchemeInteger, b: core.SchemeInteger) => { +export const quotient: Function = ( + a: core.SchemeInteger, + b: core.SchemeInteger, +) => { if (!integer$63$(a) || !integer$63$(b)) { error("quotient: expected integers"); } @@ -250,18 +253,27 @@ export const quotient: Function = (a: core.SchemeInteger, b: core.SchemeInteger) let remainder = modulo(a, b); - let quotient = atomic_divide(atomic_subtract(a, remainder), b) as core.SchemeInteger; + let quotient = atomic_divide( + atomic_subtract(a, remainder), + b, + ) as core.SchemeInteger; if (atomic_equals(remainder, make_number(0))) { return quotient; } // if both a and b are same-signed, we are done - if (atomic_less_than(a, make_number(0)) && atomic_less_than(b, make_number(0))) { + if ( + atomic_less_than(a, make_number(0)) && + atomic_less_than(b, make_number(0)) + ) { return quotient; } - if (atomic_greater_than(a, make_number(0)) && atomic_greater_than(b, make_number(0))) { + if ( + atomic_greater_than(a, make_number(0)) && + atomic_greater_than(b, make_number(0)) + ) { return quotient; } @@ -278,9 +290,12 @@ export const quotient: Function = (a: core.SchemeInteger, b: core.SchemeInteger) } return quotient; -} +}; -export const remainder: Function = (a: core.SchemeInteger, b: core.SchemeInteger) => { +export const remainder: Function = ( + a: core.SchemeInteger, + b: core.SchemeInteger, +) => { if (!integer$63$(a) || !integer$63$(b)) { error("remainder: expected integers"); } @@ -290,12 +305,18 @@ export const remainder: Function = (a: core.SchemeInteger, b: core.SchemeInteger let q = quotient(a, b); - let remainder = atomic_subtract(a, atomic_multiply(q, b)) as core.SchemeInteger; + let remainder = atomic_subtract( + a, + atomic_multiply(q, b), + ) as core.SchemeInteger; return remainder; -} +}; -export const modulo: Function = (a: core.SchemeInteger, b: core.SchemeInteger) => { +export const modulo: Function = ( + a: core.SchemeInteger, + b: core.SchemeInteger, +) => { if (!integer$63$(a) || !integer$63$(b)) { error("modulo: expected integers"); } @@ -344,8 +365,49 @@ export const modulo: Function = (a: core.SchemeInteger, b: core.SchemeInteger) = return working; } } +}; + +function atomic_gcd( + a: core.SchemeInteger, + b: core.SchemeInteger, +): core.SchemeInteger { + if (atomic_equals(b, make_number(0))) { + return abs(a); + } + return abs(atomic_gcd(b, remainder(a, b))); +} + +export const gcd: Function = (...vals: core.SchemeInteger[]) => { + if (vals.length === 0) { + return core.SchemeInteger.EXACT_ZERO; + } + + if (vals.length === 1) { + return vals[0]; + } + + return vals.reduce(atomic_gcd); +}; + +function atomic_lcm( + a: core.SchemeInteger, + b: core.SchemeInteger, +): core.SchemeInteger { + return abs(atomic_multiply(quotient(a, gcd(a, b)), b)) as core.SchemeInteger; } +export const lcm: Function = (...vals: core.SchemeInteger[]) => { + if (vals.length === 0) { + return core.SchemeInteger.build(1); + } + + if (vals.length === 1) { + return vals[0]; + } + + return vals.reduce(atomic_lcm); +}; + // pair operations export const cons: Function = core.pair; @@ -959,7 +1021,7 @@ export const string$45$$62$list: Function = (s: string) => { result = cons(s[i], result); } return result; -} +}; export const list$45$$62$string: Function = (l: core.List) => { let result = ""; @@ -969,4 +1031,4 @@ export const list$45$$62$string: Function = (l: core.List) => { current = cdr(current); } return result; -} \ No newline at end of file +}; diff --git a/src/stdlib/core-math.ts b/src/stdlib/core-math.ts index 6392d08..b13f79c 100644 --- a/src/stdlib/core-math.ts +++ b/src/stdlib/core-math.ts @@ -942,10 +942,16 @@ function simplify(a: SchemeNumber): SchemeNumber { case NumberType.COMPLEX: // safe to cast as simplify never promotes a number return SchemeComplex.build( - simplify((a as SchemeComplex).getReal()) as SchemeInteger | SchemeRational | SchemeReal, - simplify((a as SchemeComplex).getImaginary()) as SchemeInteger | SchemeRational | SchemeReal, + simplify((a as SchemeComplex).getReal()) as + | SchemeInteger + | SchemeRational + | SchemeReal, + simplify((a as SchemeComplex).getImaginary()) as + | SchemeInteger + | SchemeRational + | SchemeReal, ); - } + } } /**