The longest Collatz chain that starts below 30,000: arithmetic and a loop, with no allocation, so it measures the NaNVM runtime's number handling alone. The demo below runs it as JavaScript and as WebAssembly, and shows what each run wrote, how long each took, and the second time as a multiple of the first.
The same FunctionalScript program, run two ways: as the JavaScript it already is, and compiled by fjs compile to Rust on the NaNVM runtime, then to WebAssembly. Run calls each in turn and shows what it wrote to standard output and how long the call took.
FunctionalScript source:
/**
* Finds the start below 30,000 with the longest Collatz chain, then writes
* a line to standard output saying which it is.
*
* A computation on numbers alone, beside the sorting sample, which builds a
* new array at every step: here nothing is allocated while it searches. A
* chain's length and its start travel as one number, {@link key}, rather
* than as a pair.
*
* The program is an effect: one `write` command, whose text is encoded to
* UTF-8 bytes in a bit vector by {@link bytes} here, because `fjs compile`
* cannot compile `log` and `utf8` yet.
*
* @module
*//** The search covers the starts from `1` up to, not including, this. */constlimit = 30_000/** A start's place in a {@link key}: above every start below {@link limit}. */constscale = 1_048_576/**
* How many steps `x` takes to reach `1`, plus `count`: halve an even number,
* and triple an odd one and add one. Starts below {@link limit} take a few
* hundred steps at most, so the recursion stays shallow.
*
* @type {(x: number, count: number) => number}
*/conststeps = (x, count) => x === 1 ? count : steps(x % 2 === 0 ? x / 2 : 3 * x + 1, count + 1)
/**
* A start and its chain's length as one number, ordered by the length and
* then by the start.
*
* @type {(start: number) => number}
*/constkey = start => steps(start, 0) * scale + start/** @type {(a: number, b: number) => number} */constlarger = (a, b) => a > b ? a : b/**
* The largest {@link key} of the `n` starts from `lo`, found in halves, so
* the depth is `log n`.
*
* @type {(lo: number, n: number) => number}
*/constlongest = (lo, n) => n === 1 ? key(lo) : larger(longest(lo, n >> 1), longest(lo + (n >> 1), n - (n >> 1)))
/**
* ASCII text as the bit vector a `write` command carries: the bytes, most
* significant bit first. An ASCII byte's first bit is `0`, so
* `fjs/types/bit_vec` spells the vector negated, with that bit set to `1`
* as the stop bit: the loop starts past it, with `1n`.
*
* @type {(text: string) => bigint}
*/constbytes = text => {
/** @type {readonly [number, bigint]} */lets = [1, 1n]
while (s[0] < text.length * 8) {
consti = s[0]
s = [i + 1, s[1] * 2n + ((text.charCodeAt(i >> 3) >> (7 - i % 8)) & 1 ? 1n : 0n)]
}
return -s[1]
}
/** What the program writes, from the largest key. @type {(best: number) => string} */constreport = best =>
`the longest Collatz chain below ${limit} starts at ${best % scale} and takes ${(best - best % scale) / scale} steps\n`
/**
* The entry: everything happens in the call, none of it as the module loads,
* so that the time the page shows holds the search.
*/exportdefault () => ({
command: 'write',
payload: ['stdout', bytes(report(longest(1, limit - 1)))],
continuation: () => () => ['ok', undefined],
})
Rust that fjs compile writes from it, on the NaNVM runtime: