UNPKG

hkt-toolbelt

Version:

Functional and composable type utilities

167 lines (166 loc) 6.64 kB
import { Digit, DigitList, Kind, Type } from '..'; /** * For each digit in the multiplicand, from right to left, we perform a digit- * wise subtraction of the minued minus the subtrahend digit, and prepend the * result to the output digit list. * * At each step, we account for the carry digit from the previous iteration. * * The case whereby A and B are of unequal length is handled implicitly via the * definition of the '_$last' utility, which returns a 'zero' digit if the list * is empty. */ type _$subtract2< /** * The minuend, i.e. the amount to subtract from, i.e. A in A - B */ A extends DigitList.DigitList, /** * The subtrahend, i.e. the amount to subtract by, i.e. B in A - B */ B extends DigitList.DigitList, /** * The carry digit, which is either '0' or '1'. This is percolated through the * subtraction algorithm, to account for borrowing. This is set to one on the * next iteration if the current digit-wise subtraction would result in a * negative number. (i.e. if the minuend digit is less than the subtrahend) */ CARRY extends Digit.Digit = '0', /** * The output digit list, which is the result of the subtraction. This is * built up as we proceed through the subtraction algorithm from right to * left. * * Each iteration of the subtraction algorithm, we prepend the result of the * digit-wise subtraction to the output digit list. */ OUTPUT extends DigitList.DigitList = [], /** * The last digit of the minuend. The last element is the rightmost digit, * which is the least significant digit - and thus the next digit to be * subtracted. */ A_LAST extends Digit.Digit = DigitList._$last<A>, /** * The last digit of the subtrahend. */ B_LAST extends Digit.Digit = DigitList._$last<B>, /** * The next instance of A, with the last digit popped off. We consume one * digit of the minuend each iteration. */ A_NEXT extends DigitList.DigitList = DigitList._$pop<A>, /** * The next instance of B, with the last digit popped off. */ B_NEXT extends DigitList.DigitList = DigitList._$pop<B>, /** * The result of the digit-wise subtraction of the last digit of the minuend * and the last digit of the subtrahend. The result of digit-wise subtraction * wraps around via modulus rules, i.e. (A[i] - B[i]) % 10 */ SUB extends Digit.Digit = Digit._$subtract<A_LAST, B_LAST>, /** * The borrow digit from the current digit-wise subtraction. This is either * '0' or '1', depending on whether we need to borrow from the next digit, * i.e. if A[i] < B[i]. * * Note: this doesn't take into account the carry digit from the previous * iteration. This is handled by the `SUB_CARRY` type parameter. */ SUB_TENS extends Digit.Digit = Digit._$subtractTens<A_LAST, B_LAST>, /** * We subtract the carry digit from the digit-wise subtraction, which is * either '0' or '1', depending on whether we borrowed from the current digit * during the previous iteration. * * This is subject to modulo rules. */ SUB_CARRY extends Digit.Digit = Digit._$subtract<SUB, CARRY>, /** * We subtract the carry digit from the digit-wise subtraction, which is * either '0' or '1', and capture the tens-place digit of the result. * * This digit corresponds to whether accounting for the previous borrow, if * one existed, would cause us to 'run out' of value in the current digit. * * Notably, because we are only performing digit-wise subtraction, and not * taking into account SUB_TENS in this calculation, it does not quite * represent whether or not we need to borrow from the next digit. This is * instead handled by the `CARRY_NEXT` type parameter. */ SUB_CARRY_TENS extends Digit.Digit = Digit._$subtractTens<SUB, SUB_CARRY>, /** * The carry digit for the next iteration. This is either '0' or '1'. We base * this off of whether either `SUB_TENS` or `SUB_CARRY_TENS` is '1'. * * Basically, C[i + 1] = 1 if A[i] < B[i] or if A[i] < B[i] + C[i], otherwise * C[i + 1] = 0. We check this separately for performance reasons - otherwise * we would need to perform an additional full subtraction step. * * This is passed as the `CARRY` type parameter to the next iteration. */ CARRY_NEXT extends Digit.Digit = SUB_TENS extends '1' ? '1' : SUB_CARRY_TENS, /** * The output digit list for the next iteration. We prepend the result of the * digit-wise subtraction to the output digit list, with the carry digit * accounted for. */ OUTPUT_NEXT extends DigitList.DigitList = [SUB_CARRY, ...OUTPUT], /** * We are done when we have no more digits to subtract, i.e. when both A and * B are empty. */ DONE = A extends [] ? (B extends [] ? true : false) : false, /** * The result of the subtraction. Notably, if we have a carry digit at the * end, this implies that the result is negative, and so we return zero. */ RESULT = CARRY extends '1' ? ['0'] : OUTPUT> = DONE extends true ? RESULT : _$subtract2<A_NEXT, B_NEXT, CARRY_NEXT, OUTPUT_NEXT>; /** * `_$subtract` is a type-level function that subtracts one digit list from * another. It returns the result of the subtraction. * * @template A - A digit list representing a number to subtract. * @template B - A digit list representing a number to subtract by. * * @example * For example, we can use `_$subtract` to subtract one digit list from another: * * ```ts * import { DigitList } from "hkt-toolbelt"; * * type Result = DigitList._$subtract<["1", "2", "5"], ["1", "2", "1"]>; * // ["4"] * ``` * * In this example, `Result` is a type that represents ["4"], which is the * result of subtracting ["1", "2", "5"] from ["1", "2", "1"]. */ export type _$subtract<A extends DigitList.DigitList, B extends DigitList.DigitList> = DigitList._$trim<_$subtract2<A, B>>; interface Subtract_T<X extends DigitList.DigitList> extends Kind.Kind { f(x: Type._$cast<this[Kind._], DigitList.DigitList>): _$subtract<X, typeof x>; } /** * `Subtract` is a type-level function that subtracts one digit list from * another. It returns the result of the subtraction. * * @template A - A digit list representing a number to subtract. * @template B - A digit list representing a number to subtract by. * * @example * For example, we can use `Subtract` to subtract one digit list from another: * * ```ts * import { $, DigitList } from "hkt-toolbelt"; * * type Result = $<DigitList.Subtract, ["5", "0"], ["2", "5"]>; // ["2", "5"] * ``` * * In this example, `Result` is a type that represents ["2", "5"], which is the * result of subtracting ["2", "5"] from ["5", "0"]. */ export interface Subtract extends Kind.Kind { f(x: Type._$cast<this[Kind._], DigitList.DigitList>): Subtract_T<typeof x>; } export {};