two-bit/word.ts

97 lines
3.0 KiB
TypeScript

import bit, { Bit } from "./bit"
import { range, zip } from "lodash"
type GreaterThan<Threshold extends number> = number
type LessOrEqual<Threshold extends number> = number
export interface Word<Width extends number> {
plus: <OtherWidth extends number, ResultWidth = >(other: Word<OtherWidth>) => Word<GreaterOf<Width, OtherWidth>>
bits: Bit[]
length: Width
}
// function greaterOf<Value extends number, Other extends number>(value: Value, other: Other){
// return value > other ? value as Value : other as Other
// }
type LessThan = number
type GreaterOrEqual = number
function isLessThan(value: number, other: number): value is LessThan {
return value < other
}
function isGreaterThanOrEqual(value: number, other: number): value is GreaterOrEqual {
return value >= other
}
function greaterOf<A extends LessThan, B>(value: LessThan, other: B): LessThan
function greaterOf<A extends GreaterOrEqual, B>(value: GreaterOrEqual, other: B): GreaterOrEqual
function greaterOf<A extends number, B extends number>(value: A, other: B): LessThan | GreaterOrEqual {
const result = Math.max(value, other)
return isLessThan(value, other) ? value as GreaterThan : other as LessThan
}
const duh = wordFromBits([bit(0)] as const).plus(wordFromBits([bit(0), bit(0)]))
type FixedLengthArray<T, Length extends number> = Readonly<Array<T> & { length: Length}>
export function wordFromBits<Width extends Readonly<number>>(bits: readonly FixedLengthArray<Bit, Width>) {
function plus<OtherWidth extends GreaterThan<Width>>(other: Word<OtherWidth>): Word<OtherWidth>
function plus<OtherWidth extends LessOrEqual<Width>>(other: Word<OtherWidth>): Word<Width> {
const newBits = addBits(bits, other.bits)
return wordFromBits(newBits.bits)
}
function subword(start: number, length?: number) {
return wordFromBits(bits.slice(start, length !== undefined ? start + length : undefined))
}
return {
plus,
subword,
bits,
length: bits.length
}
}
function addBits(bits: Bit[], otherBits: Bit[]) {
const bigEndianBits = [...bits].reverse()
const bigEndianOtherBits = [...otherBits].reverse()
const pairedBits = zip(bigEndianBits, bigEndianOtherBits).reverse()
const denulledBits = pairedBits.map(([a, b]) => [a ?? bit(0), b ?? bit(0)] as const)
const sumResults = denulledBits.reduce<SumResult>(({bits, carry}, [a, b]) => {
const aPlusB = a.add(b)
const aPlusBPlusCarry = aPlusB.ones.add(carry)
const thisCarry = aPlusB.carry.add(aPlusBPlusCarry.carry).ones
bits.push(aPlusBPlusCarry.ones)
return sumResult(bits, thisCarry)
}, sumResult([], bit(0)))
return sumResults
}
interface SumResult {
bits: Bit[]
carry: Bit
}
function sumResult(bits: Bit[], carry: Bit) {
return {
bits,
carry}
}
}
export function emptyWord<Width extends number>(width: Width): Word<Width> {
const bits = range(0, width).map(() => bit(0))
return wordFromBits(bits)
}