compiling-the-lambda-calculus/examples/sum_of_even_squares.ts
2024-02-13 20:00:02 -07:00

152 lines
3.2 KiB
TypeScript

const isEven = (n: number) => {
let even = true; // 0 is even
for (let i = 1; i <= n; i++) {
even = !even;
}
return even;
};
const sumOfEvenSquares = (upTo: number) => {
let sum = 0;
for (let i = 1; i <= upTo; i++) {
if (isEven(i)) {
sum += i * i;
}
}
return sum;
};
/**
* functional
*/
const isEvenHelper = (curr: number, max: number, previous = false) => {
if (curr === max) {
return !previous;
}
return isEvenHelper(curr + 1, max, !previous);
};
const isEvenFn = (n: number) => {
return isEvenHelper(0, n);
};
const map = (
transform: (x: number, ind: number) => number,
list: number[],
index = 0,
) => {
if (list.length === 0) {
return [];
}
const [head, ...tail] = list;
const transformed = transform(head, index);
return [transformed].concat(map(transform, tail, index + 1));
};
const filter = (predicate: (x: number) => boolean, list: number[]) => {
if (list.length === 0) {
return [];
}
const [head, ...tail] = list;
if (predicate(head)) {
return [head].concat(filter(predicate, tail));
}
return filter(predicate, tail);
};
const reduce = (
accumulation: (x: number, accumulator: number) => number,
accumulator: number,
list: number[],
) => {
if (list.length === 0) {
return accumulator;
}
const [head, ...tail] = list;
const newAccumulator = accumulation(head, accumulator);
return reduce(accumulation, newAccumulator, tail);
};
const curriedReduce = (
accumulation: (x: number, accumulator: number) => number,
) => {
return (accumulator: number) => {
const reduce = (list: number[]) => {
if (list.length === 0) {
return accumulator;
}
const [head, ...tail] = list;
const newAccumulator = accumulation(head, accumulator);
return curriedReduce(accumulation)(newAccumulator)(tail);
};
return reduce;
};
};
const sum = curriedReduce((x, acc) => x + acc)(0);
const sumOfEvenSquaresFn = (upTo: number) => {
const numbersUpTo = Array(upTo + 1)
.fill(0)
.map((_, i) => i);
const squares = numbersUpTo.filter((x) => isEvenFn(x)).map((x) => x * x);
return sum(squares);
};
/**
* test
*/
const assert = (predicate: () => boolean, message: string) => {
const start = performance.now();
console.log(message + "...");
const result = predicate();
const runtimeFmt = ` in ${(performance.now() - start).toFixed(5)}ms`;
if (result) {
console.log(" ... PASSED" + runtimeFmt);
return;
}
console.error(" ... FAILED" + runtimeFmt);
throw new Error("predicate failed for test: " + message);
};
/*
Σ(2i)^2 = 4 * Σ(i)^2 = 4 * (n * (n + 1) * (2 * n + 1)) / 6
*/
const constantTimeMathMagic = (x: number) => {
const n = Math.floor(x / 2); // even numbers
return (4 * (n * (n + 1) * (2 * n + 1))) / 6;
};
const test = (sumEvenSquarer: (x: number) => number) => {
assert(() => sumEvenSquarer(5) === 2 ** 2 + 4 ** 2, "up to 5");
assert(
() => sumEvenSquarer(10) === 2 ** 2 + 4 ** 2 + 6 ** 2 + 8 ** 2 + 10 ** 2,
"up to 10",
);
//assert(
// () => sumEvenSquarer(75_000) === constantTimeMathMagic(75_000),
// "up to 75,000",
//);
};
test(sumOfEvenSquares);
test(sumOfEvenSquaresFn);