Update project euler samples to use range

This commit is contained in:
Andrew Morris
2023-06-02 15:18:58 +10:00
parent d7062e906a
commit 74c85b9b77
11 changed files with 300 additions and 271 deletions

View File

@@ -0,0 +1,8 @@
export default function assert(
value: boolean,
msg = "value was not true",
): asserts value {
if (value !== true) {
throw new Error(`Assertion failed: ${msg}`);
}
}

View File

@@ -9,16 +9,16 @@ export function Range_from<T = never>(
iter?: Iterable<T> | Iterator<T> | (() => Iterable<T>),
) {
if (iter === undefined) {
return Range_fromIterable([]);
return range([]);
}
if (typeof iter === "function") {
return Range_fromIterable(iter());
return range(iter());
}
// TODO: `in` operator
if (hasKey(iter, Symbol.iterator)) {
return Range_fromIterable(iter);
return range(iter);
}
if (hasKey(iter, "next")) {
@@ -38,14 +38,20 @@ export function Range_fromIterator<T = never>(iterator: Iterator<T>) {
});
}
export function Range_numbers(start: number, end: number) {
function* res() {
export function Range_numbers(start = 0, end?: number) {
if (end === undefined) {
return range((function* () {
for (let i = start;; i++) {
yield i;
}
})());
}
return range((function* () {
for (let i = start; i < end; i++) {
yield i;
}
}
return Range_fromIterable(res());
})());
}
export function Range_primes() {
@@ -79,7 +85,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
count() {
@@ -119,14 +125,34 @@ export class Range<T = never> implements Iterable<T> {
return res;
}
sum(): T extends number ? number : never {
sum<T extends number>(
// Warning: ValueScript has a bug where typing the `this` parameter causes it to create a
// phantom regular parameter. This only works because there aren't any other parameters.
// TODO: Fix this.
this: Range<T>,
) {
let res = 0;
for (const x of this.iterable) {
res += x as number;
res += x;
}
return res as T extends number ? number : never;
return res;
}
bigSum<T extends bigint>(
// Warning: ValueScript has a bug where typing the `this` parameter causes it to create a
// phantom regular parameter. This only works because there aren't any other parameters.
// TODO: Fix this.
this: Range<T>,
) {
let res = 0n;
for (const x of this.iterable) {
res += x as bigint;
}
return res as T extends bigint ? bigint : never;
}
product(): T extends number ? number : never {
@@ -148,7 +174,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
flatMap<MappedT>(fn: (x: T) => Iterable<MappedT>) {
@@ -162,7 +188,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
filter(fn: (x: T) => boolean) {
@@ -176,7 +202,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
// TODO: Negative indexes
@@ -220,7 +246,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
append<U>(newItems: Iterable<U>) {
@@ -231,7 +257,7 @@ export class Range<T = never> implements Iterable<T> {
yield* newItems;
}
return Range_fromIterable(res());
return range(res());
}
prepend<U>(newItems: Iterable<U>) {
@@ -242,7 +268,7 @@ export class Range<T = never> implements Iterable<T> {
yield* iterable;
}
return Range_fromIterable(res());
return range(res());
}
zip<U>(other: Iterable<U>) {
@@ -264,7 +290,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
skip(n: number) {
@@ -288,7 +314,7 @@ export class Range<T = never> implements Iterable<T> {
}
}
return Range_fromIterable(res());
return range(res());
}
reduce<S>(state: S, fn: (state: S, x: T) => S) {
@@ -298,6 +324,22 @@ export class Range<T = never> implements Iterable<T> {
return state;
}
while(fn: (x: T) => boolean) {
const iterable = this.iterable;
function* res() {
for (const x of iterable) {
if (fn(x)) {
yield x;
} else {
break;
}
}
}
return range(res());
}
}
function hasKey<Obj, K extends string | symbol>(

View File

@@ -1,13 +1,9 @@
//! test_output(233168)
import { Range_numbers } from "../helpers/range.ts";
export default function main() {
let sum = 0;
for (let i = 0; i < 1000; i++) {
if (i % 3 === 0 || i % 5 === 0) {
sum += i;
}
}
return sum;
return Range_numbers(0, 1000)
.filter((x) => x % 3 === 0 || x % 5 === 0)
.sum();
}

View File

@@ -1,132 +1,113 @@
//! test_output("Probably 5537376230 (trailing digits: 39036)")
//! test_output("5537376230")
import range from "../helpers/range.ts";
export default function main() {
const numStrs = [
"37107287533902102798797998220837590246510135740250",
"46376937677490009712648124896970078050417018260538",
"74324986199524741059474233309513058123726617309629",
"91942213363574161572522430563301811072406154908250",
"23067588207539346171171980310421047513778063246676",
"89261670696623633820136378418383684178734361726757",
"28112879812849979408065481931592621691275889832738",
"44274228917432520321923589422876796487670272189318",
"47451445736001306439091167216856844588711603153276",
"70386486105843025439939619828917593665686757934951",
"62176457141856560629502157223196586755079324193331",
"64906352462741904929101432445813822663347944758178",
"92575867718337217661963751590579239728245598838407",
"58203565325359399008402633568948830189458628227828",
"80181199384826282014278194139940567587151170094390",
"35398664372827112653829987240784473053190104293586",
"86515506006295864861532075273371959191420517255829",
"71693888707715466499115593487603532921714970056938",
"54370070576826684624621495650076471787294438377604",
"53282654108756828443191190634694037855217779295145",
"36123272525000296071075082563815656710885258350721",
"45876576172410976447339110607218265236877223636045",
"17423706905851860660448207621209813287860733969412",
"81142660418086830619328460811191061556940512689692",
"51934325451728388641918047049293215058642563049483",
"62467221648435076201727918039944693004732956340691",
"15732444386908125794514089057706229429197107928209",
"55037687525678773091862540744969844508330393682126",
"18336384825330154686196124348767681297534375946515",
"80386287592878490201521685554828717201219257766954",
"78182833757993103614740356856449095527097864797581",
"16726320100436897842553539920931837441497806860984",
"48403098129077791799088218795327364475675590848030",
"87086987551392711854517078544161852424320693150332",
"59959406895756536782107074926966537676326235447210",
"69793950679652694742597709739166693763042633987085",
"41052684708299085211399427365734116182760315001271",
"65378607361501080857009149939512557028198746004375",
"35829035317434717326932123578154982629742552737307",
"94953759765105305946966067683156574377167401875275",
"88902802571733229619176668713819931811048770190271",
"25267680276078003013678680992525463401061632866526",
"36270218540497705585629946580636237993140746255962",
"24074486908231174977792365466257246923322810917141",
"91430288197103288597806669760892938638285025333403",
"34413065578016127815921815005561868836468420090470",
"23053081172816430487623791969842487255036638784583",
"11487696932154902810424020138335124462181441773470",
"63783299490636259666498587618221225225512486764533",
"67720186971698544312419572409913959008952310058822",
"95548255300263520781532296796249481641953868218774",
"76085327132285723110424803456124867697064507995236",
"37774242535411291684276865538926205024910326572967",
"23701913275725675285653248258265463092207058596522",
"29798860272258331913126375147341994889534765745501",
"18495701454879288984856827726077713721403798879715",
"38298203783031473527721580348144513491373226651381",
"34829543829199918180278916522431027392251122869539",
"40957953066405232632538044100059654939159879593635",
"29746152185502371307642255121183693803580388584903",
"41698116222072977186158236678424689157993532961922",
"62467957194401269043877107275048102390895523597457",
"23189706772547915061505504953922979530901129967519",
"86188088225875314529584099251203829009407770775672",
"11306739708304724483816533873502340845647058077308",
"82959174767140363198008187129011875491310547126581",
"97623331044818386269515456334926366572897563400500",
"42846280183517070527831839425882145521227251250327",
"55121603546981200581762165212827652751691296897789",
"32238195734329339946437501907836945765883352399886",
"75506164965184775180738168837861091527357929701337",
"62177842752192623401942399639168044983993173312731",
"32924185707147349566916674687634660915035914677504",
"99518671430235219628894890102423325116913619626622",
"73267460800591547471830798392868535206946944540724",
"76841822524674417161514036427982273348055556214818",
"97142617910342598647204516893989422179826088076852",
"87783646182799346313767754307809363333018982642090",
"10848802521674670883215120185883543223812876952786",
"71329612474782464538636993009049310363619763878039",
"62184073572399794223406235393808339651327408011116",
"66627891981488087797941876876144230030984490851411",
"60661826293682836764744779239180335110989069790714",
"85786944089552990653640447425576083659976645795096",
"66024396409905389607120198219976047599490197230297",
"64913982680032973156037120041377903785566085089252",
"16730939319872750275468906903707539413042652315011",
"94809377245048795150954100921645863754710598436791",
"78639167021187492431995700641917969777599028300699",
"15368713711936614952811305876380278410754449733078",
"40789923115535562561142322423255033685442488917353",
"44889911501440648020369068063960672322193204149535",
"41503128880339536053299340368006977710650566631954",
"81234880673210146739058568557934581403627822703280",
"82616570773948327592232845941706525094512325230608",
"22918802058777319719839450180888072429661980811197",
"77158542502016545090413245809786882778948721859617",
"72107838435069186155435662884062257473692284509516",
"20849603980134001723930671666823555245252804609722",
"53503534226472524250874054075591789781264330331690",
const nums = [
37107287533902102798797998220837590246510135740250n,
46376937677490009712648124896970078050417018260538n,
74324986199524741059474233309513058123726617309629n,
91942213363574161572522430563301811072406154908250n,
23067588207539346171171980310421047513778063246676n,
89261670696623633820136378418383684178734361726757n,
28112879812849979408065481931592621691275889832738n,
44274228917432520321923589422876796487670272189318n,
47451445736001306439091167216856844588711603153276n,
70386486105843025439939619828917593665686757934951n,
62176457141856560629502157223196586755079324193331n,
64906352462741904929101432445813822663347944758178n,
92575867718337217661963751590579239728245598838407n,
58203565325359399008402633568948830189458628227828n,
80181199384826282014278194139940567587151170094390n,
35398664372827112653829987240784473053190104293586n,
86515506006295864861532075273371959191420517255829n,
71693888707715466499115593487603532921714970056938n,
54370070576826684624621495650076471787294438377604n,
53282654108756828443191190634694037855217779295145n,
36123272525000296071075082563815656710885258350721n,
45876576172410976447339110607218265236877223636045n,
17423706905851860660448207621209813287860733969412n,
81142660418086830619328460811191061556940512689692n,
51934325451728388641918047049293215058642563049483n,
62467221648435076201727918039944693004732956340691n,
15732444386908125794514089057706229429197107928209n,
55037687525678773091862540744969844508330393682126n,
18336384825330154686196124348767681297534375946515n,
80386287592878490201521685554828717201219257766954n,
78182833757993103614740356856449095527097864797581n,
16726320100436897842553539920931837441497806860984n,
48403098129077791799088218795327364475675590848030n,
87086987551392711854517078544161852424320693150332n,
59959406895756536782107074926966537676326235447210n,
69793950679652694742597709739166693763042633987085n,
41052684708299085211399427365734116182760315001271n,
65378607361501080857009149939512557028198746004375n,
35829035317434717326932123578154982629742552737307n,
94953759765105305946966067683156574377167401875275n,
88902802571733229619176668713819931811048770190271n,
25267680276078003013678680992525463401061632866526n,
36270218540497705585629946580636237993140746255962n,
24074486908231174977792365466257246923322810917141n,
91430288197103288597806669760892938638285025333403n,
34413065578016127815921815005561868836468420090470n,
23053081172816430487623791969842487255036638784583n,
11487696932154902810424020138335124462181441773470n,
63783299490636259666498587618221225225512486764533n,
67720186971698544312419572409913959008952310058822n,
95548255300263520781532296796249481641953868218774n,
76085327132285723110424803456124867697064507995236n,
37774242535411291684276865538926205024910326572967n,
23701913275725675285653248258265463092207058596522n,
29798860272258331913126375147341994889534765745501n,
18495701454879288984856827726077713721403798879715n,
38298203783031473527721580348144513491373226651381n,
34829543829199918180278916522431027392251122869539n,
40957953066405232632538044100059654939159879593635n,
29746152185502371307642255121183693803580388584903n,
41698116222072977186158236678424689157993532961922n,
62467957194401269043877107275048102390895523597457n,
23189706772547915061505504953922979530901129967519n,
86188088225875314529584099251203829009407770775672n,
11306739708304724483816533873502340845647058077308n,
82959174767140363198008187129011875491310547126581n,
97623331044818386269515456334926366572897563400500n,
42846280183517070527831839425882145521227251250327n,
55121603546981200581762165212827652751691296897789n,
32238195734329339946437501907836945765883352399886n,
75506164965184775180738168837861091527357929701337n,
62177842752192623401942399639168044983993173312731n,
32924185707147349566916674687634660915035914677504n,
99518671430235219628894890102423325116913619626622n,
73267460800591547471830798392868535206946944540724n,
76841822524674417161514036427982273348055556214818n,
97142617910342598647204516893989422179826088076852n,
87783646182799346313767754307809363333018982642090n,
10848802521674670883215120185883543223812876952786n,
71329612474782464538636993009049310363619763878039n,
62184073572399794223406235393808339651327408011116n,
66627891981488087797941876876144230030984490851411n,
60661826293682836764744779239180335110989069790714n,
85786944089552990653640447425576083659976645795096n,
66024396409905389607120198219976047599490197230297n,
64913982680032973156037120041377903785566085089252n,
16730939319872750275468906903707539413042652315011n,
94809377245048795150954100921645863754710598436791n,
78639167021187492431995700641917969777599028300699n,
15368713711936614952811305876380278410754449733078n,
40789923115535562561142322423255033685442488917353n,
44889911501440648020369068063960672322193204149535n,
41503128880339536053299340368006977710650566631954n,
81234880673210146739058568557934581403627822703280n,
82616570773948327592232845941706525094512325230608n,
22918802058777319719839450180888072429661980811197n,
77158542502016545090413245809786882778948721859617n,
72107838435069186155435662884062257473692284509516n,
20849603980134001723930671666823555245252804609722n,
53503534226472524250874054075591789781264330331690n,
];
let sum = 0;
for (let i = 0; i < numStrs.length; i++) {
let s13 = "";
for (let j = 0; j < 13; j++) {
s13 += numStrs[i][j];
}
sum += +s13;
}
const sumStr = `${sum}`;
let leadingDigits = "";
let trailingDigits = "";
for (let i = 0; i < sumStr.length; i++) {
if (i < 10) {
leadingDigits += sumStr[i];
} else {
trailingDigits += sumStr[i];
}
}
return `Probably ${leadingDigits} (trailing digits: ${trailingDigits})`;
return range(nums)
.bigSum()
.toString()
.slice(0, 10);
}

View File

@@ -1,7 +1,7 @@
//! test_output(1366)
import plus from "./helpers/plus.ts";
import range from "../helpers/range.ts";
export default function main() {
return Array.from(`${2n ** 1000n}`).map(Number).reduce(plus);
return range(`${2n ** 1000n}`).map(Number).sum();
}

View File

@@ -1,11 +1,10 @@
import { Range_numbers } from "../helpers/range.ts";
export default function main() {
let sum = 0;
for (let i = 1; i <= 1000; i++) {
sum += countEligibleLetters(toWords(i));
}
return sum;
return Range_numbers(1, 1001)
.map(toWords)
.map(countEligibleLetters)
.sum();
}
function toWords(n: number): string {
@@ -14,31 +13,31 @@ function toWords(n: number): string {
// very beneficial. (Instead, there's a giant literal in the bytecode here
// and it needs to be decoded every time we enter this code path.)
return [
'zero',
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
][n];
}
if (n < 20) {
return [
'ten',
'eleven',
'twelve',
'thirteen',
'fourteen',
'fifteen',
'sixteen',
'seventeen',
'eighteen',
'nineteen',
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
][n - 10];
}
@@ -47,14 +46,14 @@ function toWords(n: number): string {
const tennerIndex = (n - lastDigit) / 10 - 2;
const tenner = [
'twenty',
'thirty',
'forty',
'fifty',
'sixty',
'seventy',
'eighty',
'ninety',
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety",
][tennerIndex];
if (lastDigit === 0) {
@@ -68,7 +67,7 @@ function toWords(n: number): string {
const lastTwoDigits = n % 100;
const hundreds = (n - lastTwoDigits) / 100;
let res: string = `${toWords(hundreds)} hundred`;
let res = `${toWords(hundreds)} hundred`;
if (lastTwoDigits !== 0) {
res += ` and ${toWords(lastTwoDigits)}`;
@@ -78,7 +77,7 @@ function toWords(n: number): string {
}
if (n === 1000) {
return 'one thousand';
return "one thousand";
}
panic();
@@ -94,7 +93,7 @@ function countEligibleLetters(str: string) {
for (let i = 0; i < str.length; i++) {
const c = str[i];
if (c !== ' ' && c !== '-') {
if (c !== " " && c !== "-") {
count++;
}
}

View File

@@ -1,19 +1,19 @@
//! test_output(4613732)
export default function main() {
let sum = 0;
import range from "../helpers/range.ts";
export default function main() {
return range(fibonacci())
.while((x) => x < 4_000_000)
.filter((x) => x % 2 === 0)
.sum();
}
function* fibonacci() {
let [fibLast, fib] = [0, 1];
while (true) {
yield fib;
[fibLast, fib] = [fib, fibLast + fib];
if (fib > 4000000) {
return sum;
}
if (fib % 2 === 0) {
sum += fib;
}
}
}

View File

@@ -1,3 +1,4 @@
import range from "../helpers/range.ts";
import plus from "./helpers/plus.ts";
export default function main() {
@@ -15,11 +16,7 @@ function nameListScore(names: string[]) {
}
function nameScore(name: string) {
let sum = 0;
for (const c of name) {
sum += c.codePointAt(0)! - "A".codePointAt(0)! + 1;
}
return sum;
return range(name)
.map((c) => c.codePointAt(0)! - "A".codePointAt(0)! + 1)
.sum();
}

View File

@@ -1,29 +1,25 @@
import plus from "./helpers/plus.ts";
import { Range_numbers } from "../helpers/range.ts";
import { properFactorSum } from "./helpers/properFactorSum.ts";
export default function main() {
let abundantNumbers = [];
const abundantNumbers = [
...Range_numbers(1, 28123)
.filter(isAbundant),
];
for (let i = 1; i < 28123; i++) {
if (isAbundant(i)) {
abundantNumbers.push(i);
}
}
return Range_numbers(1, 28123)
.indexed()
.flatMap(function* ([_i, n]) {
// Uncomment to see progress (program takes ~50s with a release build)
// if (_i % 1000 === 0) {
// Debug.log(_i);
// }
let nonAbundantSums = [];
for (let i = 1; i < 28123; i++) {
if (!hasAbundantSum(i, abundantNumbers)) {
nonAbundantSums.push(i);
}
// Uncomment to see progress (program takes ~30s with a release build)
// if (i % 1000 === 0) {
// Debug.log(i);
// }
}
return nonAbundantSums.reduce(plus);
if (!hasAbundantSum(n, abundantNumbers)) {
yield n;
}
})
.sum();
}
function isAbundant(n: number) {

View File

@@ -1,18 +1,29 @@
//! test_output(4782)
export default function main() {
let fibLast = 1n;
let fib = 1n;
let fibIndex = 2;
import assert from "../helpers/assert.ts";
import range from "../helpers/range.ts";
export default function main() {
// TODO: Remove the temptation pull out this constant (optimization to eval
// known expressions).
const threshold = 10n ** 999n;
while (fib < threshold) {
[fib, fibLast] = [fib + fibLast, fib];
fibIndex++;
}
const result = range(fibonacci())
.indexed()
.filter(([_, x]) => x > threshold)
.first();
return fibIndex;
assert(result !== undefined);
return result[0];
}
function* fibonacci() {
let fibLast = 1n;
let fib = 0n;
while (true) {
yield fib;
[fib, fibLast] = [fib + fibLast, fib];
}
}

View File

@@ -1,37 +1,36 @@
import plus from "./helpers/plus.ts";
import range from "../helpers/range.ts";
// There's an analytic solution for this, but that kinda eliminates the need to
// write code altogether. We're showcasing programming techniques here, not
// just writing down the simplest solution.
export default function () {
return [
1,
raySum([1, 9, 25], 501) - 1,
raySum([1, 3, 13], 501) - 1,
raySum([1, 5, 17], 501) - 1,
raySum([1, 7, 21], 501) - 1,
].reduce(plus);
}
const rayStarters = [
[1, 9, 25],
[1, 3, 13],
[1, 5, 17],
[1, 7, 21],
];
type QuadraticTriplet = [number, number, number];
let sum = range(rayStarters)
.map(([a, b, c]) => range(ray(a, b, c)).limit(501).sum())
.sum();
/**
* There's an analytic solution for this, but that kinda eliminates the need to
* write code altogether. We're showcasing programming techniques here, not
* just writing down the simplest solution.
*/
function raySum(triplet: QuadraticTriplet, len: number) {
let sum = triplet.reduce(plus);
for (let i = 3; i < len; i++) {
triplet = nextTriplet(triplet);
sum += triplet[2];
}
// The central 1 has been counted 4 times, so subtract 3.
sum -= 3;
return sum;
}
function nextTriplet([a, b, c]: QuadraticTriplet): QuadraticTriplet {
return [
b,
c,
3 * c - 3 * b + a,
];
function* ray(a: number, b: number, c: number) {
yield a;
yield b;
yield c;
while (true) {
const newC = 3 * c - 3 * b + a;
yield newC;
[a, b, c] = [b, c, newC];
}
}