import bit, { Bit } from "./bit" import { range, zip } from "lodash" import { Add, Max } from "math-types" export interface Word { concat(other: Word): Word> toNumber(): number toString(): string plus: (other: Word) => { sum: Word>, carry: Bit } bits: Bit[] & { length: Width } subword: (start: number, length?: Width) => Word equals: (word?: Word) => boolean } type FixedLengthArray = T[] & { length: Length } export function wordFromBits>(bits: BitArray): Word { function plus(other: Word) { const newBits = addBits(bits, other.bits) return { sum: wordFromBits(newBits.bits as FixedLengthArray>) as Word>, carry: newBits.carry } } function subword(start: number, length?: number) { return wordFromBits(bits.slice(start, length !== undefined ? start + length : undefined)) } function concat(other: Word) { return wordFromBits([...bits, ...other.bits]) } function equals(other?: Word) { return other !== undefined && other.bits.length === bits.length && other.bits.every((otherBit, index) => bits[index].equals(otherBit)) } function toNumber() { return Number.parseInt(bits.map(bit => bit.value).join(""), 2) } function toString() { return bits.map(bit => bit.value).join("") } return { plus, subword, concat, bits, equals, toNumber, toString } as Word } export function wordFromNumber(source: number, length?: number) { const bitArray = source.toString(2).split("").map(char => bit(Number.parseInt(char) as 0 | 1)) const paddedBitArray = length === undefined ? bitArray : zeroPad(bitArray, length) return wordFromBits(paddedBitArray) } export function wordFromString(source: string) { const bitArray = source.split("").map(char => bit(Number.parseInt(char) as 0 | 1)) return wordFromBits(bitArray) } function addBits(bits: Bit[], otherBits: Bit[]) { const resultLength = Math.max(bits.length, otherBits.length) const paddedBits = zeroPad(bits, resultLength) const paddedOtherBits = zeroPad(otherBits, resultLength) const pairedBits = zip(paddedBits, paddedOtherBits) as [Bit, Bit][] const sumResults = pairedBits.reduceRight(({bits, carry}, [a, b]) => { const aPlusB = a.add(b) const aPlusBPlusCarry = aPlusB.ones.add(carry) const thisCarry = aPlusB.carry.add(aPlusBPlusCarry.carry).ones bits.splice(0, 0, aPlusBPlusCarry.ones) return sumResult(bits, thisCarry) }, sumResult([], bit(0))) return sumResults } function zeroPad(array: Bit[], length: number) { return [...Array(length - array.length).fill(bit(0)), ...array] as Bit[] } interface SumResult { bits: Bit[] carry: Bit } function sumResult(bits: Bit[], carry: Bit) { return { bits: bits, carry } } export function emptyWord(width: Width): Word { const bits = range(0, width).map(() => bit(0)) as Bit[] & { length: Width } return wordFromBits(bits) }