hkt-toolbelt
Version:
Functional and composable type utilities
167 lines (166 loc) • 6.64 kB
TypeScript
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 {};