hkt-toolbelt
Version:
Functional and composable type utilities
150 lines (149 loc) • 5.53 kB
TypeScript
import { Digit, DigitList, Kind, Type } from '..';
type _$add2<
/**
* The first digit list to add.
*/
A extends DigitList.DigitList,
/**
* The second digit list to add. This can be swapped with `A` without changing
* the result, since addition is commutative.
*/
B extends DigitList.DigitList,
/**
* The carry digit. This is initially set to `Digit.Zero`, by default, but is
* set on recursive calls to the result of the tens place addition. This has
* parity with the classical 'full adder' circuit, where the carry digit is
* the carry-over from the tens place addition.
*
* In the next place (since addition is computed from right-to-left), this
* digit is added to the sum of the digits in the next place.
*
* Ths digit will either be "0" or "1".
*/
CARRY extends Digit.Digit = Digit.Zero,
/**
* The output digit list. This is initially set to the empty digit list, by
* default, but is set on recursive calls to the result of the addition of the
* current place.
*/
OUTPUT extends DigitList.DigitList = [],
/**
* The last digit of the first digit list. This is used to compute the sum of
* the current place - the sum of the current place is the sum of the last
* digits of the first and second digit lists, plus the carry digit.
*/
A_LAST extends Digit.Digit = DigitList._$last<A>,
/**
* The last digit of the second digit list. This is used to compute the sum of
* the current place.
*/
B_LAST extends Digit.Digit = DigitList._$last<B>,
/**
* The next copy of the first digit list that we pass to the recursive call.
* This has the last digit removed, as it has been processed.
*/
A_NEXT extends DigitList.DigitList = DigitList._$pop<A>,
/**
* The next copy of the second digit list that we pass to the recursive call.
* This has the last digit removed, as it has been processed.
*/
B_NEXT extends DigitList.DigitList = DigitList._$pop<B>,
/**
* The 'ones place' digit sum of the current place, without the carry digit.
*/
SUM extends Digit.Digit = Digit._$add<A_LAST, B_LAST>,
/**
* The 'tens place' digit sum of the current place, without the carry digit.
*/
SUM_TENS extends Digit.Digit = Digit._$addTens<A_LAST, B_LAST>,
/**
* The 'ones place' digit sum of the current place, with the carry digit.
*/
SUM_CARRY extends Digit.Digit = Digit._$add<SUM, CARRY>,
/**
* The 'tens place' digit sum of the current place, with the carry digit.
*/
SUM_CARRY_TENS extends Digit.Digit = Digit._$addTens<SUM, CARRY>,
/**
* The carry digit to pass to the next recursive call. This is the 'tens
* place' digit sum of the current place, with the carry digit.
*
* Essentially we are saying that the carry digit is '1' if _either_ of the
* tens place digit sums are '1'. This is essentially a logical OR operation.
*/
CARRY_NEXT extends Digit.Digit = SUM_TENS extends '1' ? '1' : SUM_CARRY_TENS,
/**
* The output digit list to pass to the next recursive call. This is the
* result of the current 'ones place' digit sum, with the carry digit taken
* into account.
*
* We prepend this digit result to the front of the output digit list.
*/
OUTPUT_NEXT extends DigitList.DigitList = [SUM_CARRY, ...OUTPUT],
/**
* A boolean flag that indicates whether or not we are done adding the two
* digit lists. We are done if both digit lists are empty.
*/
DONE = A extends [] ? (B extends [] ? true : false) : false,
/**
* The result of the addition. This is the output digit list, with the carry
* digit prepended to the front if it is '1'.
*/
RESULT = CARRY extends '1' ? [CARRY, ...OUTPUT] : OUTPUT
/**
* The core computation loop of the addition algorithm. This is a recursive
* function that takes in the two digit lists to add, and returns the result
* of the addition.
*
* If we are done adding the two digit lists, we return the result of the
* addition - otherwise we continue to add the two digit lists based on the
* parameters that were computed.
*/
> = DONE extends true ? RESULT : _$add2<A_NEXT, B_NEXT, CARRY_NEXT, OUTPUT_NEXT>;
/**
* `_$add` is a type-level function that takes in two digit lists `A` and `B`,
* and returns the sum of the two digit lists as a new digit list.
*
* @template A - A digit list.
* @template B - A digit list.
*
* @example
* For example, we can use `_$add` to add two digit lists representing the
* numbers 123 and 456:
*
* ```ts
* import { DigitList } from "hkt-toolbelt"
*
* type Result = DigitList._$add<["1", "2", "3"], ["4", "5", "6"]> // ["5",
* "7", "9"]
* ```
*/
export type _$add<A extends DigitList.DigitList, B extends DigitList.DigitList, SUM = _$add2<A, B>, RESULT = SUM extends [] ? [Digit.Zero] : SUM> = RESULT;
interface Add_T<X extends DigitList.DigitList> extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): _$add<X, typeof x>;
}
/**
* `Add` is a type-level function that takes in two digit lists `A` and `B`,
* and returns the sum of the two digit lists as a new digit list.
*
* @template A - A digit list.
* @template B - A digit list.
*
* @example
* For example, we can use `Add` to add two digit lists representing the
* numbers 123 and 456:
*
* We apply `Add` to `["1", "2", "3"]` and `["4", "5", "6"]` respectively using
* the `$` type-level applicator:
*
* ```ts
* import { $, DigitList } from "hkt-toolbelt"
*
* type Result = $<$<DigitList.Add, ["1", "2", "3"]>, ["4", "5", "6"]> // ["5",
* "7", "9"]
* ```
*/
export interface Add extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): Add_T<typeof x>;
}
export {};