hkt-toolbelt
Version:
Functional and composable type utilities
125 lines (124 loc) • 5.2 kB
TypeScript
import { Type, Kind, Digit, DigitList } from '..';
type _$divide2<A extends DigitList.DigitList, B extends DigitList.DigitList,
/**
* The operation to perform. Either "DIVIDE" or "MODULO". This only controls
* what we return at the end of the division.
*/
OPERATION extends 'DIVIDE' | 'MODULO' = 'DIVIDE',
/**
* The list of active digits to process, from left to right.
*/
DIVIDEND extends DigitList.DigitList = A,
/**
* The current quotient, which we append to as we go.
*/
QUOTIENT extends DigitList.DigitList = [],
/**
* The current remainder, which we append to and subtract from according to
* the rules of long division. This corresponds to the 'bottom' of the long
* division algorithm.
*/
REMAINDER extends DigitList.DigitList = [],
/**
* The leftmost digit of the remaining dividend.
*/
ACTIVE extends Digit.Digit = DIVIDEND[0],
/**
* In the long division algorithm, we append the active digit to the remainder
* of the previous step. This is where you 'draw an arrow down' in the long
* division paper algorithm.
*/
ARROW_DOWN extends DigitList.DigitList = DigitList._$trim<[
...REMAINDER,
ACTIVE
]>,
/**
* A sub-division step, computed via repeated subtraction. This results in the
* next quotient value.
*
* The quotient in this step is guaranteed to be an integer from 0 to 9.
* Therefore, the repeated subtraction will only take at max 9 steps.
*/
SUB_DIV extends DigitList.DigitList = DigitList._$divideBySubtraction<ARROW_DOWN, B>,
/**
* The next value of DIVIDEND on each step. We shift the current value, i.e.
* remove the leftmost digit. Once this value is empty, we have reached the
* end of the division.
*/
NEXT_DIVIDEND extends DigitList.DigitList = DigitList._$shift<DIVIDEND>,
/**
* The next value of QUOTIENT on each step. We append the quotient from the
* sub-division step to the current value.
*/
NEXT_QUOTIENT extends DigitList.DigitList = Type._$cast<[
...QUOTIENT,
...SUB_DIV
], DigitList.DigitList>,
/**
* The next value of REMAINDER on each step. This corresponds to the step in
* the long division algorithm where you perform a subtraction step at the
* bottom of the paper.
*
* For efficiency, we use the remainder from the sub-division step, which is
* equivalent to the remainder from the subtraction step.
*/
NEXT_REMAINDER extends DigitList.DigitList = Type._$cast<DigitList._$divideBySubtraction<ARROW_DOWN, B, 'MODULO'>, DigitList.DigitList>,
/**
* We have reached the end of the division when the next dividend is empty.
*/
DONE extends boolean = NEXT_DIVIDEND extends [] ? true : false,
/**
* The result of the division is the quotient and remainder, as a pair.
*/
RESULT extends DigitList.DigitList = OPERATION extends 'DIVIDE' ? DigitList._$trim<NEXT_QUOTIENT> : NEXT_REMAINDER> = DONE extends true ? RESULT : _$divide2<A, B, OPERATION, NEXT_DIVIDEND, NEXT_QUOTIENT, NEXT_REMAINDER>;
/**
* `_$divide` is a type-level function that performs the division or modulo operation.
* It takes in two digit lists `A` and `B` representing the dividend and divisor respectively, and an operation type
* which can be either "DIVIDE" or "MODULO". It checks if the divisor is 0 or 1 and returns the appropriate result.
* If the divisor is neither 0 nor 1, it calls the `_$divide2` function to perform the division.
*
* @template A - A digit list representing a number to divide.
* @template B - A digit list representing a number to divide by.
* @template OPERATION - A string type representing the operation to be performed. Can be either "DIVIDE" or "MODULO".
*
* @example
* For example, we can use `_$divide` to divide a digit list representing the number 10 by 2:
*
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = DigitList._$divide<["1", "0"], ["2"], "DIVIDE">; // ["5"]
* ```
*
* @example
* We can also use `_$divide` to find the remainder when a digit list representing the number 123 is divided by 17:
*
* ```ts
* import { DigitList } from "hkt-toolbelt";
*
* type Result = DigitList._$divide<["1", "2", "3"], ["1", "7"], "MODULO">; // ["4"]
* ```
*/
export type _$divide<A extends DigitList.DigitList, B extends DigitList.DigitList, OPERATION extends 'DIVIDE' | 'MODULO' = 'DIVIDE'> = B extends [Digit.Zero] ? [Digit.Zero] : B extends ['1'] ? OPERATION extends 'DIVIDE' ? A : [Digit.Zero] : _$divide2<A, B, OPERATION>;
interface Divide_T<A extends DigitList.DigitList> extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): _$divide<A, typeof x>;
}
/**
* `Divide` is a type-level function that performs a division operation.
* It returns the result of the division operation.
*
* @example
* For example, we can use `Divide` to create a division operation that divides a digit list representing the number 10 by 2:
*
* ```ts
* import { $, DigitList } from "hkt-toolbelt";
*
* type Result = $<$<DigitList.Divide, ["1", "0"]>, ["2"]>; // ["5"]
* ```
*
* In this example, `Result` is a type that represents the digit list ["5"], which is the result of dividing 10 by 2.
*/
export interface Divide extends Kind.Kind {
f(x: Type._$cast<this[Kind._], DigitList.DigitList>): Divide_T<typeof x>;
}
export {};