UNPKG

avl-tree-typed

Version:
354 lines (353 loc) 17.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isTrampolineThunk = exports.makeTrampolineThunk = exports.roundFixed = exports.calcMinUnitsRequired = exports.isWeakKey = exports.throwRangeError = exports.rangeCheck = exports.getMSB = exports.arrayRemove = exports.uuidV4 = void 0; exports.isComparable = isComparable; exports.trampoline = trampoline; exports.makeTrampoline = makeTrampoline; exports.asyncTrampoline = asyncTrampoline; exports.makeAsyncTrampoline = makeAsyncTrampoline; /** * The function generates a random UUID (Universally Unique Identifier) in TypeScript. * @returns A randomly generated UUID (Universally Unique Identifier) in the format * 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' where each 'x' is replaced with a random hexadecimal * character. */ const uuidV4 = function () { return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) { const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); }; exports.uuidV4 = uuidV4; /** * The `arrayRemove` function removes elements from an array based on a specified predicate function * and returns the removed elements. * @param {T[]} array - An array of elements that you want to filter based on the provided predicate * function. * @param predicate - The `predicate` parameter is a function that takes three arguments: * @returns The `arrayRemove` function returns an array containing the elements that satisfy the given * `predicate` function. */ const arrayRemove = function (array, predicate) { let i = -1, len = array ? array.length : 0; const result = []; while (++i < len) { const value = array[i]; if (predicate(value, i, array)) { result.push(value); Array.prototype.splice.call(array, i--, 1); len--; } } return result; }; exports.arrayRemove = arrayRemove; /** * The function `getMSB` returns the most significant bit of a given number. * @param {number} value - The `value` parameter is a number for which we want to find the position of * the Most Significant Bit (MSB). The function `getMSB` takes this number as input and calculates the * position of the MSB in its binary representation. * @returns The function `getMSB` returns the most significant bit (MSB) of the input `value`. If the * input value is less than or equal to 0, it returns 0. Otherwise, it calculates the position of the * MSB using the `Math.clz32` function and bitwise left shifts 1 to that position. */ const getMSB = (value) => { if (value <= 0) { return 0; } return 1 << (31 - Math.clz32(value)); }; exports.getMSB = getMSB; /** * The `rangeCheck` function in TypeScript is used to validate if an index is within a specified range * and throws a `RangeError` with a custom message if it is out of bounds. * @param {number} index - The `index` parameter represents the value that you want to check if it * falls within a specified range. * @param {number} min - The `min` parameter represents the minimum value that the `index` should be * compared against in the `rangeCheck` function. * @param {number} max - The `max` parameter in the `rangeCheck` function represents the maximum value * that the `index` parameter is allowed to have. If the `index` is greater than this `max` value, a * `RangeError` will be thrown. * @param [message=Index out of bounds.] - The `message` parameter is a string that represents the * error message to be thrown if the index is out of bounds. By default, if no message is provided when * calling the `rangeCheck` function, the message "Index out of bounds." will be used. */ const rangeCheck = (index, min, max, message = 'Index out of bounds.') => { if (index < min || index > max) throw new RangeError(message); }; exports.rangeCheck = rangeCheck; /** * The function `throwRangeError` throws a RangeError with a custom message if called. * @param [message=The value is off-limits.] - The `message` parameter is a string that represents the * error message to be displayed when a `RangeError` is thrown. If no message is provided, the default * message is 'The value is off-limits.'. */ const throwRangeError = (message = 'The value is off-limits.') => { throw new RangeError(message); }; exports.throwRangeError = throwRangeError; /** * The function `isWeakKey` checks if the input is an object or a function in TypeScript. * @param {unknown} input - The `input` parameter in the `isWeakKey` function is of type `unknown`, * which means it can be any type. The function checks if the `input` is an object (excluding `null`) * or a function, and returns a boolean indicating whether the `input` is a weak * @returns The function `isWeakKey` returns a boolean value indicating whether the input is an object * or a function. */ const isWeakKey = (input) => { const inputType = typeof input; return (inputType === 'object' && input !== null) || inputType === 'function'; }; exports.isWeakKey = isWeakKey; /** * The function `calcMinUnitsRequired` calculates the minimum number of units required to accommodate a * given total quantity based on a specified unit size. * @param {number} totalQuantity - The `totalQuantity` parameter represents the total quantity of items * that need to be processed or handled. * @param {number} unitSize - The `unitSize` parameter represents the size of each unit or package. It * is used in the `calcMinUnitsRequired` function to calculate the minimum number of units required to * accommodate a total quantity of items. */ const calcMinUnitsRequired = (totalQuantity, unitSize) => Math.floor((totalQuantity + unitSize - 1) / unitSize); exports.calcMinUnitsRequired = calcMinUnitsRequired; /** * The `roundFixed` function in TypeScript rounds a number to a specified number of decimal places. * @param {number} num - The `num` parameter is a number that you want to round to a certain number of * decimal places. * @param {number} [digit=10] - The `digit` parameter in the `roundFixed` function specifies the number * of decimal places to round the number to. By default, it is set to 10 if not provided explicitly. * @returns The function `roundFixed` returns a number that is rounded to the specified number of * decimal places (default is 10 decimal places). */ const roundFixed = (num, digit = 10) => { const multiplier = Math.pow(10, digit); return Math.round(num * multiplier) / multiplier; }; exports.roundFixed = roundFixed; /** * The function `isPrimitiveComparable` checks if a value is a primitive type that can be compared. * @param {unknown} value - The `value` parameter in the `isPrimitiveComparable` function is of type * `unknown`, which means it can be any type. The function checks if the `value` is a primitive type * that can be compared, such as number, bigint, string, or boolean. * @returns The function `isPrimitiveComparable` returns a boolean value indicating whether the input * `value` is a primitive value that can be compared using standard comparison operators (<, >, <=, * >=). */ function isPrimitiveComparable(value) { const valueType = typeof value; if (valueType === 'number') return true; // if (valueType === 'number') return !Number.isNaN(value); return valueType === 'bigint' || valueType === 'string' || valueType === 'boolean'; } /** * The function `tryObjectToPrimitive` attempts to convert an object to a comparable primitive value by * first checking the `valueOf` method and then the `toString` method. * @param {object} obj - The `obj` parameter in the `tryObjectToPrimitive` function is an object that * you want to convert to a primitive value. The function attempts to convert the object to a primitive * value by first checking if the object has a `valueOf` method. If the `valueOf` method exists, it * @returns The function `tryObjectToPrimitive` returns a value of type `ComparablePrimitive` if a * primitive comparable value is found within the object, or a string value if the object has a custom * `toString` method that does not return `'[object Object]'`. If neither condition is met, the * function returns `null`. */ function tryObjectToPrimitive(obj) { if (typeof obj.valueOf === 'function') { const valueOfResult = obj.valueOf(); if (valueOfResult !== obj) { if (isPrimitiveComparable(valueOfResult)) return valueOfResult; if (typeof valueOfResult === 'object' && valueOfResult !== null) return tryObjectToPrimitive(valueOfResult); } } if (typeof obj.toString === 'function') { const stringResult = obj.toString(); if (stringResult !== '[object Object]') return stringResult; } return null; } /** * The function `isComparable` in TypeScript checks if a value is comparable, handling primitive values * and objects with optional force comparison. * @param {unknown} value - The `value` parameter in the `isComparable` function represents the value * that you want to check if it is comparable. It can be of any type (`unknown`), and the function will * determine if it is comparable based on certain conditions. * @param [isForceObjectComparable=false] - The `isForceObjectComparable` parameter in the * `isComparable` function is a boolean flag that determines whether to treat non-primitive values as * comparable objects. When set to `true`, it forces the function to consider non-primitive values as * comparable objects, regardless of their type. * @returns The function `isComparable` returns a boolean value indicating whether the `value` is * considered comparable or not. */ function isComparable(value, isForceObjectComparable = false) { if (value === null || value === undefined) return false; if (isPrimitiveComparable(value)) return true; if (typeof value !== 'object') return false; if (value instanceof Date) return true; // if (value instanceof Date) return !Number.isNaN(value.getTime()); if (isForceObjectComparable) return true; const comparableValue = tryObjectToPrimitive(value); if (comparableValue === null || comparableValue === undefined) return false; return isPrimitiveComparable(comparableValue); } /** * Creates a trampoline thunk object. * * A "thunk" is a deferred computation — instead of performing a recursive call immediately, * it wraps the next step of the computation in a function. This allows recursive processes * to be executed iteratively, preventing stack overflows. * * @template T - The type of the final computation result. * @param computation - A function that, when executed, returns the next trampoline step. * @returns A TrampolineThunk object containing the deferred computation. */ const makeTrampolineThunk = (computation) => ({ isThunk: true, // Marker indicating this is a thunk fn: computation // The deferred computation function }); exports.makeTrampolineThunk = makeTrampolineThunk; /** * Type guard to check whether a given value is a TrampolineThunk. * * This function is used to distinguish between a final computation result (value) * and a deferred computation (thunk). * * @template T - The type of the value being checked. * @param value - The value to test. * @returns True if the value is a valid TrampolineThunk, false otherwise. */ const isTrampolineThunk = (value) => typeof value === 'object' && // Must be an object value !== null && // Must not be null 'isThunk' in value && // Must have the 'isThunk' property value.isThunk; // The flag must be true exports.isTrampolineThunk = isTrampolineThunk; /** * Executes a trampoline computation until a final (non-thunk) result is obtained. * * The trampoline function repeatedly invokes the deferred computations (thunks) * in an iterative loop. This avoids deep recursive calls and prevents stack overflow, * which is particularly useful for implementing recursion in a stack-safe manner. * * @template T - The type of the final result. * @param initial - The initial Trampoline value or thunk to start execution from. * @returns The final result of the computation (a non-thunk value). */ function trampoline(initial) { let current = initial; // Start with the initial trampoline value while ((0, exports.isTrampolineThunk)(current)) { // Keep unwrapping while we have thunks current = current.fn(); // Execute the deferred function to get the next step } return current; // Once no thunks remain, return the final result } /** * Wraps a recursive function inside a trampoline executor. * * This function transforms a potentially recursive function (that returns a Trampoline<Result>) * into a *stack-safe* function that executes iteratively using the `trampoline` runner. * * In other words, it allows you to write functions that look recursive, * but actually run in constant stack space. * * @template Args - The tuple type representing the argument list of the original function. * @template Result - The final return type after all trampoline steps are resolved. * * @param fn - A function that performs a single step of computation * and returns a Trampoline (either a final value or a deferred thunk). * * @returns A new function with the same arguments, but which automatically * runs the trampoline process and returns the *final result* instead * of a Trampoline. * * @example * // Example: Computing factorial in a stack-safe way * const factorial = makeTrampoline(function fact(n: number, acc: number = 1): Trampoline<number> { * return n === 0 * ? acc * : makeTrampolineThunk(() => fact(n - 1, acc * n)); * }); * * console.log(factorial(100000)); // Works without stack overflow */ function makeTrampoline(fn // A function that returns a trampoline step ) { // Return a wrapped function that automatically runs the trampoline execution loop return (...args) => trampoline(fn(...args)); } /** * Executes an asynchronous trampoline computation until a final (non-thunk) result is obtained. * * This function repeatedly invokes asynchronous deferred computations (thunks) * in an iterative loop. Each thunk may return either a Trampoline<T> or a Promise<Trampoline<T>>. * * It ensures that asynchronous recursive functions can run without growing the call stack, * making it suitable for stack-safe async recursion. * * @template T - The type of the final result. * @param initial - The initial Trampoline or Promise of Trampoline to start execution from. * @returns A Promise that resolves to the final result (a non-thunk value). */ function asyncTrampoline(initial) { return __awaiter(this, void 0, void 0, function* () { let current = yield initial; // Wait for the initial step to resolve if it's a Promise // Keep executing thunks until we reach a non-thunk (final) value while ((0, exports.isTrampolineThunk)(current)) { current = yield current.fn(); // Execute the thunk function (may be async) } // Once the final value is reached, return it return current; }); } /** * Wraps an asynchronous recursive function inside an async trampoline executor. * * This helper transforms a recursive async function that returns a Trampoline<Result> * (or Promise<Trampoline<Result>>) into a *stack-safe* async function that executes * iteratively via the `asyncTrampoline` runner. * * @template Args - The tuple type representing the argument list of the original function. * @template Result - The final return type after all async trampoline steps are resolved. * * @param fn - An async or sync function that performs a single step of computation * and returns a Trampoline (either a final value or a deferred thunk). * * @returns An async function with the same arguments, but which automatically * runs the trampoline process and resolves to the *final result*. * * @example * // Example: Async factorial using trampoline * const asyncFactorial = makeAsyncTrampoline(async function fact( * n: number, * acc: number = 1 * ): Promise<Trampoline<number>> { * return n === 0 * ? acc * : makeTrampolineThunk(() => fact(n - 1, acc * n)); * }); * * asyncFactorial(100000).then(console.log); // Works without stack overflow */ function makeAsyncTrampoline(fn) { // Return a wrapped async function that runs through the async trampoline loop return (...args) => __awaiter(this, void 0, void 0, function* () { return asyncTrampoline(fn(...args)); }); }