UNPKG

hkt-toolbelt

Version:

Functional and composable type utilities

125 lines (124 loc) 5.2 kB
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 {};