export function todo(...args: unknown[]): T { const argsStr = args.map((a) => JSON.stringify(a)).join(", "); class NotImplemented extends Error { constructor() { super(`todo(${argsStr})`); this.name = "NotImplemented"; } } throw new NotImplemented(); } export function exhausted(...args: never[]): T { const argsStr = args.map((a) => JSON.stringify(a)).join(", "); class Unexhausted extends Error { constructor() { super(`exhausted(${argsStr})`); this.name = "Unexhausted"; } } throw new Unexhausted(); } export type Res = Ok | Err; export type Ok = { ok: true; val: V }; export type Err = { ok: false; val: E }; export const Ok = (val: V): Ok => ({ ok: true, val }); export const Err = (val: E): Err => ({ ok: false, val }); export const Res = { Ok, Err } as const; export type ControlFlow< R = undefined, V = undefined, > = Break | Continue; export type Break = { break: true; val: R }; export type Continue = { break: false; val: V }; export const ControlFlow = { Break: (val: R): Break => ({ break: true, val }), Continue: (val: V): Continue => ({ break: false, val }), } as const; export const range = (length: number) => (new Array(length).fill(0)); export const strictEq = (a: T, b: T): boolean => a === b; export function arrayEq( a: T[], b: T[], elemCmp: (a: T, b: T) => boolean = strictEq, ): boolean { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; ++i) { if (!elemCmp(a[i], b[i])) { return false; } } return true; }