UNPKG

type-plus

Version:
657 lines (619 loc) 22.2 kB
import type { Tail } from '../tuple/tail.js' import type { PadStart } from '../tuple/tuple_plus.pad_start.js' import type { Add as NumericAdd } from './add.js' import type { ToNegative } from './math_plus.to_negative.js' /** * Internal numeric representation to perform math operations. * * NumericStruct: `[Type, DigitsStruct]` * DigitsStruct: `[Sign, Digits, Exponent]` * * It is similar to the floating point representation with some minor differences. * * @template Type Type of the value, either `number` or `bigint`. * It captures the original type of the value, * so that at the end of the operation, * the value can be adjusted accordingly. * * @template Sign Sign of the value, either `+` or `-`. * * @template Digits Digits of the value. * It is a tuple of digits, * where each digits can range from 0 to 9 during input, * and range from -10 to 89 during operation. * The max 89 comes from multiplication: * * ``` * 99 * 9 * => [[ 9, 9], 0] * * [ [9], 0] * => [[ 81, 81], 0] * => [[ 89, 1], 0] * => [[ 8, 9, 1], 0] * ``` * * While -10 comes from subtraction: * * ``` * 100 - 99 = 1 * => [[ 1, 0, 0], 0] * - [[ 9, 9], 0] * => [[ 1, -9, -9], 0] * => [[ 1,-10, 1], 0] // here, as in intermediate step * => [[ 0, 0, 1], 0] * => [[1], 0] * => 1 * ``` * * * Unlike floating point numbers, the digits length is not limited. * * @template Exponent Exponent is the negative exponent of the number. * * ``` * 1.23 = 123e^-2 = [[1, 2, 3], 2] * 0.0123 = 123e^-4 = [[1, 2, 3], 4] * ``` * * There are 2 kinds of `NumericStruct`: * - Normalized: The `NumericStruct` is clean and can be converted to/from `number` or `bigint` * - Not normalized: The `DigitsStruct` within the `NumericStruct` is normalized, * but may need to convert between `number` and `bigint`.\ * e.g. bigint + float => float (if possible), number + number => bigint (too big) * * During operations, the `DigitsStruct` can be not normalized, * but each operation should always normalize the `DigitsStruct` before returning. * Each operations assume the input `DigitsStruct` is normalized. * * All operations are performed in similar way: * * value -> NormalizedNumericStruct -> operation(NormalizedDigitsStruct) -> NormalizedNumericStruct -> Value * * This allows the operations to be composable. * */ export type NumericStruct = ['bigint' | 'number', DigitsStruct] // These are used to reference the NumericStruct values export type TYPE = 0 export type DIGITS_STRUCT = 1 export namespace NumericStruct { /** * Creates a `NumericStruct` from number or bigint `N`. */ export type FromNumeric<N extends number | bigint, Fail = never> = N extends number ? number extends N ? Fail : ['number', DigitsStruct.FromNumber<N>] : N extends bigint ? bigint extends N ? Fail : ['bigint', DigitsStruct.FromBigint<N>] : never /** * Converts a `NumericStruct` to a number or bigint. * * It includes the normalization of the `NumericStruct`. */ export type ToNumeric<M extends NumericStruct> = DigitsStruct.ToString< M[DIGITS_STRUCT] > extends infer S extends string ? M[TYPE] extends 'bigint' ? StringToBigint<S, StringToNumber<S, `The value '${S}' cannot be represented as bigint or number`>> : StringToNumber<S, StringToBigint<S, `The value '${S}' cannot be represented as bigint or number`>> : never export type Add<A extends NumericStruct, B extends NumericStruct> = [ A[TYPE], DigitsStruct.Add<A[DIGITS_STRUCT], B[DIGITS_STRUCT]> ] export type Subtract<A extends NumericStruct, B extends NumericStruct> = [ A[TYPE], DigitsStruct.Subtract<A[DIGITS_STRUCT], B[DIGITS_STRUCT]> ] export type Multiply<A extends NumericStruct, B extends NumericStruct> = [ A[TYPE], DigitsStruct.Multiply<A[DIGITS_STRUCT], B[DIGITS_STRUCT]> ] } // TODO: move into `NumericHelpers` type StringToBigint<S extends string, Fail> = S extends `${infer N extends bigint}` ? N : Fail // TODO: move into `NumericHelpers` export type StringToNumber<S extends string, Fail> = S extends `${infer N extends number}` ? number extends N ? Fail : N : Fail export type DigitsStruct = [Sign: '+' | '-', Digits: number[], Exponent: number] // These are used to reference the DigitsStruct values export type SIGN = 0 export type DIGITS = 1 export type EXPONENT = 2 export namespace DigitsStruct { /** * Creates a `DigitsStruct` from number `N`. * * @template N Number to create `DigitsStruct` from. */ export type FromNumber<N extends number> = `${N}` extends `-${infer R}` ? R extends `${infer W}.${infer F}` ? [DigitArray.FromString<W>, DigitArray.FromString<F>] extends [ infer WA extends number[], infer FA extends number[] ] ? ['-', DigitArray.TrimLeadingZeros<[...WA, ...FA]>, FA['length']] : never : ['-', DigitArray.FromString<R>, 0] : `${N}` extends `${infer W}.${infer F}` ? [DigitArray.FromString<W>, DigitArray.FromString<F>] extends [ infer WA extends number[], infer FA extends number[] ] ? ['+', DigitArray.TrimLeadingZeros<[...WA, ...FA]>, FA['length']] : never : ['+', DigitArray.FromString<`${N}`>, 0] /** * Creates a `DigitsStruct` from bigint `N`. * * @template N Number to create `DigitsStruct` from. */ export type FromBigint<N extends bigint> = `${N}` extends `-${infer R}` ? ['-', DigitArray.FromString<R>, 0] : ['+', DigitArray.FromString<`${N}`>, 0] /** * Converts a `DigitsStruct` to string. * * @template D A normalized `DigitsStruct`. */ export type ToString<D extends DigitsStruct> = ( PadStart<D[DIGITS], D[EXPONENT], 0> extends infer Padded extends number[] ? Padded['length'] extends D[EXPONENT] ? DigitArray.ToString<[0, '.', ...DigitArray.TrimTrailingZeros<Padded>]> : SplitFloat<Padded, D[EXPONENT]> extends [infer W extends number[], infer F extends number[]] ? F extends [] ? DigitArray.ToString<W> : DigitArray.ToString<[...W, '.', ...DigitArray.TrimTrailingZeros<F>]> : never : never ) extends infer R ? R extends '0' ? R : R extends string ? D[SIGN] extends '-' ? `-${R}` : R : never : never /** * Splits DigitArray into the whole number and the fractional part. * * @note cannot use `ArrayPlus.SplitAt` as it causes infinite loop. */ type SplitFloat<A extends number[], I extends number, F extends number[] = []> = I extends 0 ? [A, []] : F['length'] extends I ? [A, F] : A extends [...infer H extends number[], infer T extends number] ? SplitFloat<H, I, [T, ...F]> : never /** * Normalizes a `DigitsStruct`. * * The normalization two two things: * * - if the first digit is negative, it will flip the sign. * - carry digits if the digit is negative or greater than 9. */ export type Normalize< N extends DigitsStruct, R extends DigitsStruct = ['+', [], 0] > = `${N[DIGITS][0]}` extends `-${number}` ? Normalize<FlipSign<N>, R> : [N[SIGN], DigitArray.CarryDigits<N[DIGITS]>, N[EXPONENT]] type FlipSign< D extends DigitsStruct, I extends number[] = D[DIGITS], R extends number[] = [] > = I extends [] ? D[SIGN] extends '-' ? ['+', R, D[EXPONENT]] : ['-', R, D[EXPONENT]] : I extends [infer H extends number, ...infer T extends number[]] ? FlipSign<D, T, [...R, Digit.FlipSign<H>]> : never /** * Add `A` and `B`. * * @template A A normalized `DigitsStruct`. * @template B B normalized `DigitsStruct`. */ export type Add<A extends DigitsStruct, B extends DigitsStruct> = Balance<A, B> extends [ infer BA extends DigitsStruct, infer BB extends DigitsStruct ] ? [BA[SIGN], BB[SIGN]] extends ['+', '+'] ? Normalize<['+', DigitArray.Add<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['+', '-'] ? Normalize<['+', DigitArray.Subtract<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['-', '+'] ? Normalize<['+', DigitArray.Subtract<BB[DIGITS], BA[DIGITS]>, BB[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['-', '-'] ? Normalize<['-', DigitArray.Add<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : never : never export type Subtract<A extends DigitsStruct, B extends DigitsStruct> = Balance<A, B> extends [ infer BA extends DigitsStruct, infer BB extends DigitsStruct ] ? [BA[SIGN], BB[SIGN]] extends ['+', '+'] ? Normalize<['+', DigitArray.Subtract<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['+', '-'] ? Normalize<['+', DigitArray.Add<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['-', '+'] ? Normalize<['-', DigitArray.Add<BB[DIGITS], BA[DIGITS]>, BA[EXPONENT]]> : [BA[SIGN], BB[SIGN]] extends ['-', '-'] ? Normalize<['-', DigitArray.Subtract<BA[DIGITS], BB[DIGITS]>, BA[EXPONENT]]> : never : never export type Multiply<A extends DigitsStruct, B extends DigitsStruct> = NumericAdd< A[EXPONENT], B[EXPONENT] > extends infer Exp extends number ? [A[SIGN], B[SIGN]] extends ['+', '+'] ? Normalize<['+', DigitArray.Multiply<A[DIGITS], B[DIGITS]>, Exp]> : [A[SIGN], B[SIGN]] extends ['+', '-'] ? Normalize<['-', DigitArray.Multiply<A[DIGITS], B[DIGITS]>, Exp]> : [A[SIGN], B[SIGN]] extends ['-', '+'] ? Normalize<['-', DigitArray.Multiply<B[DIGITS], A[DIGITS]>, Exp]> : [A[SIGN], B[SIGN]] extends ['-', '-'] ? Normalize<['+', DigitArray.Multiply<A[DIGITS], B[DIGITS]>, Exp]> : never : never /** * Balance the two structs for add/subtract. */ export type Balance<A extends DigitsStruct, B extends DigitsStruct> = GetBalancePadding< A[EXPONENT], B[EXPONENT] > extends [infer Pads extends number[], infer Longer] ? Longer extends 'A' ? [A, [B[SIGN], DigitArray.TrimLeadingZeros<[...B[DIGITS], ...Pads]>, A[EXPONENT]]] : [[A[SIGN], DigitArray.TrimLeadingZeros<[...A[DIGITS], ...Pads]>, B[EXPONENT]], B] : never type GetBalancePadding< A extends number, B extends number, C extends number[] = [], R extends number[] = [] > = A extends B ? [[], 'A'] : R extends [] ? // have not reach min yet C['length'] extends A ? GetBalancePadding<A, B, [0, ...C], [0]> : C['length'] extends B ? GetBalancePadding<A, B, [0, ...C], [0, ...R]> : GetBalancePadding<A, B, [0, ...C], []> : C['length'] extends A ? [R, 'A'] : C['length'] extends B ? [R, 'B'] : GetBalancePadding<A, B, [0, ...C], [0, ...R]> /** * This is used to align the `NumberStruct` during `Add/Subtract`. */ export type GetMinPadEnd< A extends number, B extends number, R extends number[] = [] > = R['length'] extends A ? [R, 'B'] : R['length'] extends B ? [R, 'A'] : GetMinPadEnd<A, B, [0, ...R]> } export namespace DigitArray { export type FromString<S extends string> = S extends `1${infer L}` ? [1, ...FromString<L>] : S extends `2${infer L}` ? [2, ...FromString<L>] : S extends `3${infer L}` ? [3, ...FromString<L>] : S extends `4${infer L}` ? [4, ...FromString<L>] : S extends `5${infer L}` ? [5, ...FromString<L>] : S extends `6${infer L}` ? [6, ...FromString<L>] : S extends `7${infer L}` ? [7, ...FromString<L>] : S extends `8${infer L}` ? [8, ...FromString<L>] : S extends `9${infer L}` ? [9, ...FromString<L>] : S extends `0${infer L}` ? [0, ...FromString<L>] : S extends `-1${infer L}` ? [-1, ...FromString<L>] : S extends `-2${infer L}` ? [-2, ...FromString<L>] : S extends `-3${infer L}` ? [-3, ...FromString<L>] : S extends `-4${infer L}` ? [-4, ...FromString<L>] : S extends `-5${infer L}` ? [-5, ...FromString<L>] : S extends `-6${infer L}` ? [-6, ...FromString<L>] : S extends `-7${infer L}` ? [-7, ...FromString<L>] : S extends `-8${infer L}` ? [-8, ...FromString<L>] : S extends `-9${infer L}` ? [-9, ...FromString<L>] : S extends `-0${infer L}` ? [-0, ...FromString<L>] : [] export type ToString<A extends Array<number | string>> = number extends A['length'] ? '' : A['length'] extends 0 ? '' : `${A[0]}${ToString<Tail<A>>}` /** * [0, 0, -1] => [-1] * * This is used in various places so that there will be less computation, * and the sign bit can be handled properly. */ export type TrimLeadingZeros<T extends number[]> = T extends [0] ? T : T extends [0, ...infer Tail extends number[]] ? TrimLeadingZeros<Tail> : T export type TrimTrailingZeros<T extends number[]> = T extends [0] ? T : T extends [...infer Tail extends number[], 0] ? TrimTrailingZeros<Tail> : T export type Add<A extends number[], B extends number[], R extends number[] = []> = A extends [] ? B extends [] ? R : B extends [...infer BH extends number[], infer BL extends number] ? Add<[], BH, [BL, ...R]> : never : B extends [] ? A extends [...infer AH extends number[], infer AL extends number] ? Add<AH, [], [AL, ...R]> : never : [A, B] extends [ [...infer AH extends number[], infer AL extends number], [...infer BH extends number[], infer BL extends number] ] ? Add<AH, BH, [Digit.Add<AL, BL>, ...R]> : never export type Subtract<A extends number[], B extends number[], R extends number[] = []> = A extends [] ? B extends [] ? TrimLeadingZeros<R> : B extends [...infer BH extends number[], infer BL extends number] ? Subtract<[], BH, [ToNegative<BL>, ...R]> : never : B extends [] ? A extends [...infer AH extends number[], infer AL extends number] ? Subtract<AH, [], [AL, ...R]> : never : [A, B] extends [ [...infer AH extends number[], infer AL extends number], [...infer BH extends number[], infer BL extends number] ] ? Subtract<AH, BH, [Digit.Subtract<AL, BL>, ...R]> : never export type Multiply<A extends number[], B extends number[], R extends number[][] = []> = B extends [] ? RecursiveAdd<R> : B extends [infer Head extends number, ...infer Tail extends number[]] ? Multiply<A, Tail, [...R, CarryDigits<MultiplyArray<A, Head, Zeros<Tail['length']>>>]> : never type Zeros<N extends number, R extends number[] = []> = N extends unknown ? R['length'] extends N ? R : Zeros<N, [0, ...R]> : never type MultiplyArray< A extends number[], B extends number, Pad extends number[], R extends number[] = [] > = B extends 0 ? [0] : B extends 1 ? [...A, ...Pad] : A extends [] ? [...R, ...Pad] : A extends [infer H extends number, ...infer T extends number[]] ? MultiplyArray<T, B, Pad, [...R, Digit.Multiply<H, B>]> : never /** * Recursively add digit arrays. * * This is used in multiplication. */ type RecursiveAdd<E extends number[][], R extends number[] = []> = E extends [] ? R : E extends [infer H extends number[], ...infer T extends number[][]] ? RecursiveAdd<T, CarryDigits<Add<R, H>>> : never export type CarryDigits<N extends number[], R extends number[] = []> = N extends [] ? TrimLeadingZeros<R> : N extends [infer Tail extends number] ? `${Tail}` extends `${infer T1 extends number}${infer T2 extends number}` ? CarryDigits<[], [T1, T2, ...R]> : `${Tail}` extends `-${infer T1 extends number}${infer T2 extends number}` ? `-${T1}` extends `${infer NT extends number}` ? CarryDigits<[], [NT, T2, ...R]> : never : CarryDigits<[], [Tail, ...R]> : N extends [infer Head extends number, infer Tail extends number] ? `${Tail}` extends `${infer T1 extends number}${infer T2 extends number}` ? CarryDigits<[Digit.Add<Head, T1>], [T2, ...R]> : `${Tail}` extends `-${infer T1 extends number}${infer T2 extends number}` ? `-${T1}` extends `${infer NT extends number}` ? CarryDigits<[Digit.Add<Head, NT>], [T2, ...R]> : never : `${Tail}` extends `-${number}` ? CarryDigits<[Digit.Add<Head, -1>], [Digit.Plus10[Tail], ...R]> : CarryDigits<[Head], [Tail, ...R]> : N extends [...infer Heads extends number[], infer Head extends number, infer Tail extends number] ? `${Tail}` extends `${infer T1 extends number}${infer T2 extends number}` ? CarryDigits<[...Heads, Digit.Add<Head, T1>], [T2, ...R]> : `${Tail}` extends `-${infer T1 extends number}${infer T2 extends number}` ? `-${T1}` extends `${infer NT extends number}` ? CarryDigits<[...Heads, Digit.Add<Head, NT>], [T2, ...R]> : never : `${Tail}` extends `-${number}` ? CarryDigits<[...Heads, Digit.Add<Head, -1>], [Digit.Plus10[Tail], ...R]> : CarryDigits<[...Heads, Head], [Tail, ...R]> : never } export namespace Digit { /** * Adds two `Digit`. * * add: A: 0 - 9, B: 0 - 9 * normalize: [81, 81] -> [81 + 8, 1] */ export type Add<A extends number, B extends number> = `${A}` extends `-${infer AD extends number}` ? `${B}` extends `-${infer BD extends number}` ? ToNegative<PositiveEntryAdd<AD, BD>> : Subtract<B, AD> : `${B}` extends `-${infer BD extends number}` ? Subtract<A, BD> : PositiveEntryAdd<A, B> export type FlipSign<T extends number> = T extends 0 ? 0 : `${T}` extends `-${infer R extends number}` ? R : `-${T}` extends `${infer R extends number}` ? R : never type PositiveEntryAdd<A extends number, B extends number> = [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [5, 6, 7, 8, 9, 10, 11, 12, 13, 14], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [8, 9, 10, 11, 12, 13, 14, 15, 16, 17], [9, 10, 11, 12, 13, 14, 15, 16, 17, 18], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21], [13, 14, 15, 16, 17, 18, 19, 20, 21, 22], [14, 15, 16, 17, 18, 19, 20, 21, 22, 23], [15, 16, 17, 18, 19, 20, 21, 22, 23, 24], [16, 17, 18, 19, 20, 21, 22, 23, 24, 25], [17, 18, 19, 20, 21, 22, 23, 24, 25, 26], [18, 19, 20, 21, 22, 23, 24, 25, 26, 27], [19, 20, 21, 22, 23, 24, 25, 26, 27, 28], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [22, 23, 24, 25, 26, 27, 28, 29, 30, 31], [23, 24, 25, 26, 27, 28, 29, 30, 31, 32], [24, 25, 26, 27, 28, 29, 30, 31, 32, 33], [25, 26, 27, 28, 29, 30, 31, 32, 33, 34], [26, 27, 28, 29, 30, 31, 32, 33, 34, 35], [27, 28, 29, 30, 31, 32, 33, 34, 35, 36], [28, 29, 30, 31, 32, 33, 34, 35, 36, 37], [29, 30, 31, 32, 33, 34, 35, 36, 37, 38], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [31, 32, 33, 34, 35, 36, 37, 38, 39, 40], [32, 33, 34, 35, 36, 37, 38, 39, 40, 41], [33, 34, 35, 36, 37, 38, 39, 40, 41, 42], [34, 35, 36, 37, 38, 39, 40, 41, 42, 43], [35, 36, 37, 38, 39, 40, 41, 42, 43, 44], [36, 37, 38, 39, 40, 41, 42, 43, 44, 45], [37, 38, 39, 40, 41, 42, 43, 44, 45, 46], [38, 39, 40, 41, 42, 43, 44, 45, 46, 47], [39, 40, 41, 42, 43, 44, 45, 46, 47, 48], [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], [41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [42, 43, 44, 45, 46, 47, 48, 49, 50, 51], [43, 44, 45, 46, 47, 48, 49, 50, 51, 52], [44, 45, 46, 47, 48, 49, 50, 51, 52, 53], [45, 46, 47, 48, 49, 50, 51, 52, 53, 54], [46, 47, 48, 49, 50, 51, 52, 53, 54, 55], [47, 48, 49, 50, 51, 52, 53, 54, 55, 56], [48, 49, 50, 51, 52, 53, 54, 55, 56, 57], [49, 50, 51, 52, 53, 54, 55, 56, 57, 58], [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], [52, 53, 54, 55, 56, 57, 58, 59, 60, 61], [53, 54, 55, 56, 57, 58, 59, 60, 61, 62], [54, 55, 56, 57, 58, 59, 60, 61, 62, 63], [55, 56, 57, 58, 59, 60, 61, 62, 63, 64], [56, 57, 58, 59, 60, 61, 62, 63, 64, 65], [57, 58, 59, 60, 61, 62, 63, 64, 65, 66], [58, 59, 60, 61, 62, 63, 64, 65, 66, 67], [59, 60, 61, 62, 63, 64, 65, 66, 67, 68], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [61, 62, 63, 64, 65, 66, 67, 68, 69, 70], [62, 63, 64, 65, 66, 67, 68, 69, 70, 71], [63, 64, 65, 66, 67, 68, 69, 70, 71, 72], [64, 65, 66, 67, 68, 69, 70, 71, 72, 73], [65, 66, 67, 68, 69, 70, 71, 72, 73, 74], [66, 67, 68, 69, 70, 71, 72, 73, 74, 75], [67, 68, 69, 70, 71, 72, 73, 74, 75, 76], [68, 69, 70, 71, 72, 73, 74, 75, 76, 77], [69, 70, 71, 72, 73, 74, 75, 76, 77, 78], [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], [71, 72, 73, 74, 75, 76, 77, 78, 79, 80], [72, 73, 74, 75, 76, 77, 78, 79, 80, 81], [73, 74, 75, 76, 77, 78, 79, 80, 81, 82], [74, 75, 76, 77, 78, 79, 80, 81, 82, 83], [75, 76, 77, 78, 79, 80, 81, 82, 83, 84], [76, 77, 78, 79, 80, 81, 82, 83, 84, 85], [77, 78, 79, 80, 81, 82, 83, 84, 85, 86], [78, 79, 80, 81, 82, 83, 84, 85, 86, 87], [79, 80, 81, 82, 83, 84, 85, 86, 87, 88], [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], [81, 82, 83, 84, 85, 86, 87, 88, 89, 90] ][A][B] /** * Subtract two positive numbers. * The number range from 0 to 81. */ export type Subtract<A extends number, B extends number> = [`${A}`, `${B}`] extends [ `${number}${infer A2 extends number}`, `${number}${infer B2 extends number}` ] ? Subtract<A2, B2> : `${A}` extends `${number}${infer A2 extends number}` ? Subtract<A2, B> : `${B}` extends `${number}${infer B2 extends number}` ? Subtract<A, B2> : SingleDigitSubtract<A, B> export type SingleDigitSubtract<A extends number, B extends number> = [ [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], [1, 0, -1, -2, -3, -4, -5, -6, -7, -8], [2, 1, 0, -1, -2, -3, -4, -5, -6, -7], [3, 2, 1, 0, -1, -2, -3, -4, -5, -6], [4, 3, 2, 1, 0, -1, -2, -3, -4, -5], [5, 4, 3, 2, 1, 0, -1, -2, -3, -4], [6, 5, 4, 3, 2, 1, 0, -1, -2, -3], [7, 6, 5, 4, 3, 2, 1, 0, -1, -2], [8, 7, 6, 5, 4, 3, 2, 1, 0, -1], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] ][A][B] export type Plus10 = { [k in number]: number } & { '-1': 9, '-2': 8, '-3': 7, '-4': 6, '-5': 5, '-6': 4, '-7': 3, '-8': 2, '-9': 1 } export type Multiply<A extends number, B extends number> = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18], [0, 3, 6, 9, 12, 15, 18, 21, 24, 27], [0, 4, 8, 12, 16, 20, 24, 28, 32, 36], [0, 5, 10, 15, 20, 25, 30, 35, 40, 45], [0, 6, 12, 18, 24, 30, 36, 42, 48, 54], [0, 7, 14, 21, 28, 35, 42, 49, 56, 63], [0, 8, 16, 24, 32, 40, 48, 56, 64, 72], [0, 9, 18, 27, 36, 45, 54, 63, 72, 81] ][A][B] }