UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

205 lines (204 loc) 5.98 kB
/** * Bit vectors that normalize the most-significant bit using signed `bigint` values. * * A value whose top bit is already set remains positive, while other values are * negated after toggling the leading bit so the stop bit is always `1`. The sign bit * therefore acts as the stop bit that encodes the logical length of the vector. * * MSb is most-significant bit first. * * ``` * - byte: 0x53 = 0b0101_0011 * - 0123_4567 * ``` * * LSb is least-significant bit first. * * ``` * - byte: 0x53 = 0b0101_0011 * - 7654_3210 * ``` * * @module */ import { abs, bitLength, mask, max, xor } from "../bigint/module.f.js"; import { flip } from "../function/module.f.js"; import { fold } from "../list/module.f.js"; import { asBase, asNominal } from "../nominal/module.f.js"; import { repeat as mRepeat } from "../monoid/module.f.js"; /** * An empty vector of bits. */ export const empty = asNominal(0n); /** * Calculates the length of the given vector of bits. */ export const length = (v) => bitLength(asBase(v)); const lazyEmpty = () => empty; /** * Creates a vector of bits of the given `len` and the provided unsigned integer. * * @example * * ```js * const vec4 = vec(4n) * const v0 = vec4(5n) // -0xDn = -0b1101 * const v1 = vec4(0x5FEn) // 0xEn = 0b1110 * ``` */ export const vec = (len) => { if (len <= 0n) { return lazyEmpty; } const m = mask(len); const last = len - 1n; const lastBit = 1n << last; return ui => { // normalize `u` const u = m & abs(ui); // const sign = u >> last; const x = sign !== 0n ? u : -(u ^ lastBit); return asNominal(x); }; }; /** * Creates an 8-bit vector from an unsigned integer. */ export const vec8 = vec(8n); /** * Returns the unsigned integer representation of the vector by clearing the stop bit. * * @example * * ```js * const vector = vec(8n)(0x5n) // -0x85n * const result = uint(vector); // result is 0x5n * ``` */ export const uint = (v) => { const b = asBase(v); if (b >= 0n) { return b; } const u = -b; const len = bitLength(u); return u ^ (1n << (len - 1n)); }; /** * Extracts the logical length and unsigned integer from the vector. */ export const unpack = (v) => ({ length: length(v), uint: uint(v), }); /** * Packs an unpacked representation back into a vector. */ export const pack = ({ length, uint }) => vec(length)(uint); const lsbNorm = ({ length: al, uint: a }) => ({ length: bl, uint: b }) => (len) => ({ a, b }); const msbNorm = ({ length: al, uint: a }) => ({ length: bl, uint: b }) => (len) => ({ a: a << (len - al), b: b << (len - bl) }); /** * Normalizes two vectors to the same length before applying a bigint reducer. */ const op = (norm) => (op) => ap => bp => { const au = unpack(ap); const bu = unpack(bp); const len = max(au.length)(bu.length); const { a, b } = norm(au)(bu)(len); return vec(len)(op(a)(b)); }; /** * Implements operations for handling vectors in a least-significant-bit (LSb) first order. * * https://en.wikipedia.org/wiki/Bit_numbering#LSb_0_bit_numbering * * Usually associated with Little-Endian (LE) byte order. */ export const lsb = { front: len => { const m = mask(len); return v => uint(v) & m; }, removeFront: len => v => { const { length, uint } = unpack(v); return vec(length - len)(uint >> len); }, popFront: len => { const m = mask(len); return v => { const { length, uint } = unpack(v); return [uint & m, vec(length - len)(uint >> len)]; }; }, concat: (a) => (b) => { const { length: al, uint: au } = unpack(a); const { length: bl, uint: bu } = unpack(b); return vec(al + bl)((bu << al) | au); }, xor: op(lsbNorm)(xor) }; /** * Implements operations for handling vectors in a most-significant-bit (MSb) first order. * * https://en.wikipedia.org/wiki/Bit_numbering#MSb_0_bit_numbering * * Usually associated with Big-Endian (BE) byte order. */ export const msb = { front: len => { const m = mask(len); return v => { const { length, uint } = unpack(v); return (uint >> (length - len)) & m; }; }, removeFront: len => v => { const { length, uint } = unpack(v); return vec(length - len)(uint); }, popFront: len => { const m = mask(len); return v => { const { length, uint } = unpack(v); const d = length - len; return [(uint >> d) & m, vec(d)(uint)]; }; }, concat: flip(lsb.concat), xor: op(msbNorm)(xor) }; const appendU8 = ({ concat }) => (u8) => (a) => concat(a)(vec8(BigInt(u8))); /** * Converts a list of unsigned 8-bit integers to a bit vector using the provided bit order. * * @param bo The bit order for the conversion * @param list The list of unsigned 8-bit integers to be converted. * @returns The resulting vector based on the provided bit order. */ export const u8ListToVec = (bo) => fold(appendU8(bo))(empty); /** * Converts a bit vector to a list of unsigned 8-bit integers based on the provided bit order. * * @param bitOrder The bit order for the conversion. * @param v The vector to be converted. * @returns A thunk that produces a list of unsigned 8-bit integers. */ export const u8List = ({ popFront }) => { const f = (v) => () => { if (v === empty) { return null; } const [first, tail] = popFront(8n)(v); return { first: Number(first), tail: f(tail) }; }; return f; }; /** * Concatenates a list of vectors using the provided bit order. */ export const listToVec = ({ concat }) => fold(flip(concat))(empty); /** * Repeats a vector to create a padded block of the desired length. */ export const repeat = mRepeat({ identity: empty, operation: lsb.concat });