hkt-toolbelt
Version:
Functional and composable type utilities
134 lines (133 loc) • 5.25 kB
TypeScript
import { Digit, DigitList, Kind, NaturalNumber, Type } from '../';
type _$compare3<
/**
* The first digit list to compare. This implements an 'is greater than'
* operation, such that A > B --> 1, A = B --> 0, A < B --> -1.
*/
A extends DigitList.DigitList,
/**
* The second digit list to compare.
*/
B extends DigitList.DigitList,
/**
* The length of list A, as a digit list. We use this as a shortcut, in that
* if Len(A) > Len(B), then A > B, and if Len(A) < Len(B), then A < B.
*
* Interestingly, we also compare the lengths using this method, which makes
* this approach a wonderful little example of recursion.
*/
A_LENGTH extends DigitList.DigitList = NaturalNumber._$toList<A['length']>,
/**
* The length of list B, as a digit list.
*/
B_LENGTH extends DigitList.DigitList = NaturalNumber._$toList<B['length']>,
/**
* Whether or not the lengths of the two digit lists are the same. If they
* aren't the same length, then we have made a lot of progress in determining
* which digit list is greater than the other.
*/
IS_SAME_LENGTH extends boolean = A_LENGTH extends B_LENGTH ? true : false,
/**
* Whether or not both digit lists are single digits. If they are, then we can
* compare them directly. This is the base case of the recursion.
*/
BOTH_SINGLE_DIGIT extends boolean = A['length'] extends 1 ? B['length'] extends 1 ? true : false : false,
/**
* The first digit of the first digit list. This is used to compare the first
* digits of the two digit lists. We do this if the lists are the same length,
* or if they are both single digits.
*
* This is because _if_ Len(A) = Len(B), then A[0] > B[0] --> A > B, and
* A[0] < B[0] --> A < B. In other words, the "left-most" digit is the most
* significant digit.
*/
A_FIRST extends Digit.Digit = DigitList._$first<A>,
/**
* The first digit of the second digit list. This is the 'most significant'
* digit of B.
*/
B_FIRST extends Digit.Digit = DigitList._$first<B>,
/**
* The next copy of the first digit list that we pass to the recursive call.
*/
A_NEXT extends DigitList.DigitList = DigitList._$shift<A>,
/**
* The next copy of the second digit list that we pass to the recursive call.
*/
B_NEXT extends DigitList.DigitList = DigitList._$shift<B>,
/**
* The comparison result of A[0] vs. B[0]. This calls out to the `Digit`
* compare function, which is based on a 10x10 lookup table.
*/
COMP extends 1 | 0 | -1 = Digit._$compare<A_FIRST, B_FIRST>,
/**
* The final result of the comparison. This is the result of the recursive
* call.
*
* First of all, if the lists are different lengths, then we can just invoke
* ourselves with the respective _length_ of each list. This is because if
* Len(A) > Len(B), then A > B, and if Len(A) < Len(B), then A < B.
*
* If the lists are both empty, this shows that the digits are equal.
*/
RESULT extends 1 | 0 | -1 = IS_SAME_LENGTH extends false ? _$compare3<A_LENGTH, B_LENGTH> : A extends [] ? B extends [] ? 0 : -1 : B extends [] ? 1 : BOTH_SINGLE_DIGIT extends true ? COMP : COMP extends 0 ? _$compare3<A_NEXT, B_NEXT> : COMP> = RESULT;
type _$compare2<A extends DigitList.DigitList, B extends DigitList.DigitList> = A extends B ? 0 : _$compare3<A, B>;
/**
* `_$compare` is a type-level function that takes in two digit lists `A` and
* `B`, and returns the comparison result as a number type. The result will be
* 1 if `A` is greater than `B`, 0 if `A` is equal to `B`, and -1 if `A` is
* less than `B`.
*
* @template A - A digit list type.
* @template B - A digit list type.
*
* @example
* For example, we can use `_$compare` to compare two digit lists. In this
* example, we compare the digit lists ["1", "2", "3"] and ["3", "2", "1"]:
*
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = DigitList._$compare<["1", "2", "3"], ["3", "2", "1"]>; // -1
* ```
*
* We can also use the `Compare` higher-order type with the `$` type-level
* applicator to achieve the same result:
*
* ```ts
* import { $, DigitList } from "hkt-toolbelt";
*
* type Result = $<$<DigitList.Compare, ["1", "2", "3"]>, ["3", "2", "1"]>; // -1
* ```
*/
export type _$compare<A extends DigitList.DigitList, B extends DigitList.DigitList, RESULT extends 1 | 0 | -1 = _$compare2<A, B>> = RESULT;
interface Compare_T<X extends DigitList.DigitList> extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): _$compare<X, typeof x>;
}
/**
* `Compare` is a type-level function that takes in two digit lists `A` and
* `B`, and returns the comparison result as a number type. The result will be
* 1 if `A` is greater than `B`, 0 if `A` is equal to `B`, and -1 if `A` is
* less than `B`.
*
* @template A - A digit list type.
* @template B - A digit list type.
*
* @example
*
* For example, we can use the `$` type-level applicator to apply `Compare` to
* two digit lists.
*
* In this example, we compare the digit lists ["1", "2", "3"] and ["3", "2",
* "1"]:
*
* ```ts
* import { $, DigitList } from "hkt-toolbelt"
*
* type Result = $<$<DigitList.Compare, ["1", "2", "3"]>, ["3", "2", "1"]> // -1
* ```
*/
export interface Compare extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): Compare_T<typeof x>;
}
export {};