/** * Type-system arithmetic. * Copyright (c) 2025 James Prevett * * Notes on implementation: * * Snake case is used for internal types to reduce visual conflict/confusion with type variables. * Type naming convention: * `w_*` => internals that operate on whole numbers (i.e. n >= 0) * `i_*` => internals that operate on integers * `_*` => general purpose internals * */ import type { Length } from './array.js'; import type { ToNumber } from './numbers.js'; import type { Repeat, StringLength } from './string.js'; import type { $drain } from './types.js'; /** * Maps a numeric literal type to a tuple type with that length */ type __tuple = T extends { length: L; } ? T : __tuple; /** * Sum A and B (both must be whole numbers) */ type w_add = Length<[...__tuple, ...__tuple]>; /** * Subtracts B from A (both must be whole numbers) */ export type w_subtract = __tuple extends [...infer U, ...__tuple] ? Length : never; /** * Is A strictly equal to B? */ export type Equal = A extends B ? (B extends A ? true : false) : false; /** * Is A or B zero? */ type _either_zero = A extends 0 ? true : B extends 0 ? true : false; /** * Is A less than B? (whole numbers) */ type w_less = _either_zero extends true ? Equal extends true ? false : A extends 0 ? true : false : w_less, w_subtract>; /** * Negate *any* number. */ export type Negate = N extends 0 ? 0 : `-${N}` extends `${infer V extends number}` ? V : `${N}` extends `-${infer V extends number}` ? V : never; export type Absolute = N extends 0 ? 0 : `${N}` extends `-${infer V extends number}` ? V : N; export type Is_Negative = `${N}` extends `-${number}` ? true : false; export type Is_Positive = `-${N}` extends `${number}` ? true : false; type _is_int = `${N}` extends `${number}.${number}` ? false : true; type _are_ints = _is_int extends true ? (_is_int extends true ? true : false) : false; /** * While `i_sum` is just as convenient and also more robust, this has a depth of 1 instead of 3. */ type i_increment = `${N}` extends `-${infer Abs extends number}` ? Negate> : w_add; /** * While `i_sum` is just as convenient and also more robust, this has a depth of 1 instead of 3. */ type i_decrement = `${N}` extends `-${infer Abs extends number}` ? Negate> : w_subtract; /** * Sum integers A and B. * * Does not perform integer validation! * @internal */ type i_sum = `${A}` extends `-${infer A_abs extends number}` ? `${B}` extends `-${infer B_abs extends number}` ? Negate> : w_less extends true ? w_subtract : Negate> : `${B}` extends `-${infer B_abs extends number}` ? w_less extends true ? Negate> : w_subtract : w_add; /** * Add integers A and B. * @internal */ export type $Add = _are_ints extends true ? i_sum : never; /** * Subtract integer B from A. * @internal */ export type $Subtract = _are_ints extends true ? i_sum> : never; /** * Is A less than B? * @internal */ type i_less = $drain<_either_zero extends true ? Equal extends true ? false : A extends 0 ? true : false : i_less, i_decrement>>; /** * Is A less than B? * @internal */ export type $LessThan = _are_ints extends true ? i_less : never; type i_max = i_less extends true ? B : A; export type $Max = _are_ints extends true ? i_max : never; /** * Accumulative subtraction: * N: initial value * D: divisor * Q: quotient */ type w_divide = $drain extends true ? Q : w_divide>, D, i_increment>>; /** * Integer division */ type i_divide = `${A}` extends `-${infer A_abs extends number}` ? `${B}` extends `-${infer B_abs extends number}` ? w_divide : Negate> : `${B}` extends `-${infer B_abs extends number}` ? Negate> : w_divide; /** * Integer division */ export type Divide = _are_ints extends true ? i_divide : never; /** * Modulo */ export type Modulo = _are_ints extends true ? (i_less extends true ? A : Modulo>, B>) : never; /** * Get the integer part of a number */ export type Integer = `${N}` extends `${infer I extends number}.${string}` ? I : N; /** * Get the fractional part of a number */ export type Fraction = `${N}` extends `${number}.${infer S extends string}` ? `0.${S}` extends `${infer F extends number}` ? F : never : 0; type _f_sum_str, _B_len extends number = StringLength> = $drain>, A_s>> extends `${infer A_f extends number}` ? $drain>, B_s>> extends `${infer B_f extends number}` ? i_sum<_A_neg extends true ? Negate : A_f, _B_neg extends true ? Negate : B_f> : never : never; /** * Sum the fractional parts A and B of two numbers */ type f_sum = `${A}` extends `0.${infer A_s extends string}` ? `${B}` extends `0.${infer B_s extends string}` ? `0.${_f_sum_str}` extends `${infer F extends number}` ? F : never : never : never; type _sum_with_f = `${F}` extends `${infer C extends number}.${infer S}` ? `${i_sum, Integer>, C>}.${S}` extends `${infer V extends number}` ? V : never : never; export type Add, __fB extends number = Fraction> = __fA extends 0 ? __fB extends 0 ? i_sum : _sum_with_f : __fB extends 0 ? _sum_with_f : _sum_with_f, Is_Negative>>; export type AddBigInt = i_sum, ToNumber>; export type Subtract = Add>; export type SubtractBigInt = i_sum, Negate>>; /** * Accumulative addition: * N: increment * I: iterations (must be a whole number) * A: sum */ type _mul_accumulate = $drain, Add>>; /** * Multiplication. * * Current limitation: B must be an integer. */ export type Multiply = `${B}` extends `${number}.${number}` ? never : `${B}` extends `-${infer B_abs extends number}` ? Negate<_mul_accumulate> : _mul_accumulate; export {};