Skip to content

Commit

Permalink
implement gcd and lcm
Browse files Browse the repository at this point in the history
  • Loading branch information
s-kybound committed Apr 13, 2024
1 parent 6b9bb76 commit 966f4c2
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 33 deletions.
85 changes: 66 additions & 19 deletions src/stdlib/__tests__/base.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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", () => {
Expand All @@ -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));
});
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));
});
84 changes: 73 additions & 11 deletions src/stdlib/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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;
}

Expand All @@ -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");
}
Expand All @@ -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");
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = "";
Expand All @@ -969,4 +1031,4 @@ export const list$45$$62$string: Function = (l: core.List) => {
current = cdr(current);
}
return result;
}
};
12 changes: 9 additions & 3 deletions src/stdlib/core-math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
}
}

/**
Expand Down

0 comments on commit 966f4c2

Please sign in to comment.