functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
456 lines (455 loc) • 11.2 kB
JavaScript
import { sum, abs, serialize, log2, bitLength, mask, min, combination, factorial } from "./module.f.js";
const oldLog2 = (v) => {
if (v <= 0n) {
return -1n;
}
let result = 0n;
let i = 1n;
while (true) {
const n = v >> i;
if (n === 0n) {
// overshot
break;
}
v = n;
result += i;
i <<= 1n; // multiple by two
}
// We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0.
// Because of this, We check if `i` is greater than 1 before we divide it by 2.
while (i !== 1n) {
i >>= 1n;
const n = v >> i;
if (n !== 0n) {
result += i;
v = n;
}
}
return result;
};
const strBinLog2 = (v) => BigInt(v.toString(2).length) - 1n;
const strHexLog2 = (v) => {
const len = (BigInt(v.toString(16).length) - 1n) << 2n;
return len + 31n - BigInt(Math.clz32(Number(v >> len)));
};
const str32Log2 = (v) => {
const len = (BigInt(v.toString(32).length) - 1n) * 5n;
return len + 31n - BigInt(Math.clz32(Number(v >> len)));
};
export const clz32Log2 = (v) => {
if (v <= 0n) {
return -1n;
}
let result = 31n;
let i = 32n;
while (true) {
const n = v >> i;
if (n === 0n) {
// overshot
break;
}
v = n;
result += i;
i <<= 1n;
}
// We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0.
// Because of this, We check if `i` is greater than 32 before we divide it by 2.
while (i !== 32n) {
i >>= 1n;
const n = v >> i;
if (n !== 0n) {
result += i;
v = n;
}
}
return result - BigInt(Math.clz32(Number(v)));
};
const m1023log2 = (v) => {
if (v <= 0n) {
return -1n;
}
//
// 1. Fast Doubling.
//
let result = -1n;
// `bigints` higher than 2**1023 may lead to `Inf` during conversion to `number`.
// For example: `Number((1n << 1024n) - (1n << 970n)) === Inf`.
let i = 1023n;
while (true) {
const n = v >> i;
if (n === 0n) {
// overshot
break;
}
v = n;
result += i;
i <<= 1n;
}
//
// 2. Binary Search.
//
// We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0.
// Because of this, We check if `i` is greater than 1023 before we divide it by 2.
while (i !== 1023n) {
i >>= 1n;
const n = v >> i;
if (n !== 0n) {
result += i;
v = n;
}
}
//
// 3. Remainder Phase.
//
// We know that `v` is less than `1n << 1023` so we can calculate a remainder using
// `Math.log2`.
const rem = BigInt(Math.log2(Number(v)) | 0);
// (v >> rem) is either `0` or `1`, and it's used as a correction for
// Math.log2 rounding.
return result + rem + (v >> rem);
};
const benchmark = f => () => {
let e = 1048575n;
let c = 1n << e;
for (let i = 0n; i < 1_100; ++i) {
{
const x = f(c);
if (x !== e) {
throw x;
}
}
{
const x = f(c - 1n);
if (x !== e - 1n) {
throw [e, x];
}
}
c >>= 1n;
--e;
}
};
const benchmarkSmall = f => () => {
let e = 2000n;
let c = 1n << e;
do {
{
const x = f(c);
if (x !== e) {
throw [e, x];
}
}
for (let j = 1n; j < min(c >> 1n)(1000n); ++j) {
const x = f(c - j);
if (x !== e - 1n) {
throw [j, e, x];
}
}
c >>= 1n;
--e;
} while (c !== 0n);
};
export default {
example: () => {
const total = sum([1n, 2n, 3n]); // 6n
if (total !== 6n) {
throw total;
}
const absoluteValue = abs(-42n); // 42n
if (absoluteValue !== 42n) {
throw absoluteValue;
}
const logValue = log2(8n); // 3n
if (logValue !== 3n) {
throw logValue;
}
const bitCount = bitLength(255n); // 8n
if (bitCount !== 8n) {
throw bitCount;
}
const bitmask = mask(5n); // 31n
if (bitmask !== 31n) {
throw benchmark;
}
const m = min(3n)(13n); // 3n
if (m !== 3n) {
throw m;
}
const c = combination(3n, 2n, 1n); // 60n
if (c !== 60n) {
throw c;
}
},
benchmark: () => {
const list = {
// strBinLog2,
// strHexLog2,
str32Log2,
// oldLog2,
// clz32Log2,
// m1023log2,
log2,
};
const transform = (b) => Object.fromEntries(Object.entries(list).map(([k, f]) => [k, b(f)]));
return {
big: transform(benchmark),
small: transform(benchmarkSmall),
};
},
mask: () => {
const result = mask(3n); // 7n
if (result !== 7n) {
throw result;
}
},
sum: () => {
const result = sum([2n, 3n, 4n, 5n]);
if (result !== 14n) {
throw result;
}
},
abs: [
() => {
const result = abs(10n);
if (result !== 10n) {
throw result;
}
},
() => {
const result = abs(-10n);
if (result !== 10n) {
throw result;
}
}
],
serialize: [
() => {
const result = serialize(0n);
if (result !== '0n') {
throw result;
}
},
() => {
const result = serialize(123456789012345678901234567890n);
if (result !== '123456789012345678901234567890n') {
throw result;
}
},
() => {
const result = serialize(-55555n);
if (result !== '-55555n') {
throw result;
}
},
],
log2: [
() => {
const result = log2(-3n);
if (result !== -1n) {
throw result;
}
},
() => {
const result = log2(0n);
if (result !== -1n) {
throw result;
}
},
() => {
const result = log2(1n);
if (result !== 0n) {
throw result;
}
},
() => {
const result = log2(2n);
if (result !== 1n) {
throw result;
}
},
() => {
const result = log2(3n);
if (result !== 1n) {
throw result;
}
},
() => {
const result = log2(4n);
if (result !== 2n) {
throw result;
}
},
() => {
const result = log2(7n);
if (result !== 2n) {
throw result;
}
},
() => {
const result = log2(8n);
if (result !== 3n) {
throw result;
}
},
() => {
const result = log2(15n);
if (result !== 3n) {
throw result;
}
},
() => {
const result = log2(16n);
if (result !== 4n) {
throw result;
}
},
() => {
// max for Bun (131_072 Bytes)
const v = 1048575n;
const result = log2(1n << v);
if (result !== v) {
throw result;
}
},
() => {
const v = 0x18945n;
const result = log2(v);
if (result !== 16n) {
throw result;
}
}
],
toString2: () => {
// max for Bun (131_072 Bytes)
const v = 1048575n;
const result = (1n << v).toString(2).length - 1;
if (result !== 1_048_575) {
throw result;
}
},
minus: () => {
let i = 0n;
while (i < 1048575n) {
const s = -i;
if (i !== -s) {
throw [i, s];
}
i += 1n;
}
},
not: () => {
let i = 0n;
while (i < 1048575n) {
const s = ~i;
if (i !== ~s) {
throw [i, s];
}
i += 1n;
}
},
bitLen: {
0: () => {
const s = bitLength(0n);
if (s !== 0n) {
throw s;
}
},
m: () => {
let i = 0n;
while (i < 10000n) {
const s = bitLength(1n << i);
if (s !== i + 1n) {
throw [s, i];
}
i += 1n;
}
},
big: () => {
const s = bitLength(1n << 1000000n);
if (s !== 1000001n) {
throw s;
}
},
neg: [
() => {
const s = bitLength(-1n);
if (s !== 1n) {
throw s;
}
},
() => {
const s = bitLength(-2n);
if (s !== 2n) {
throw s;
}
},
() => {
const s = bitLength(-3n);
if (s !== 2n) {
throw s;
}
},
() => {
const s = bitLength(-4n);
if (s !== 3n) {
throw s;
}
},
]
},
factorial: () => {
{
const r = factorial(3n);
if (r !== 6n) {
throw r;
}
}
{
const r = factorial(5n);
if (r !== 120n) {
throw r;
}
}
},
combination: () => {
const r = combination(2n, 3n);
if (r != 120n / (2n * 6n)) {
throw r;
}
},
combination50x50: () => {
{
const r = combination(1n, 1n);
if (r !== 2n) {
throw r;
}
}
{
const r = combination(2n, 2n);
if (r !== 6n) {
throw r;
}
}
{
const r = combination(3n, 3n);
if (r !== 20n) {
throw r;
}
}
{
const r = combination(4n, 4n);
if (r !== 70n) {
throw r;
}
}
},
combination3: () => {
const r = combination(2n, 3n, 4n, 2n);
// 2! * 3! * 4! * 2! : 2! * 2! * 3!
// 2+3+4+2 = 5*6*7*8*9*10*11
// e = 5 * 6 * 7 * 8 * 9 * 10 * 11 / (2n * 2n * 6n) =
// e = 5 * 7 * 2 * 9 * 10 * 11 = 69300
if (r !== 69300n) {
throw r;
}
}
};