UNPKG

@oazmi/kitchensink

Version:

a collection of personal utility functions

365 lines (364 loc) 11.5 kB
/** utility functions for numeric array manipulation and array math functions. * * @module */ import "./_dnt.polyfills.js"; import { resolveRange } from "./array1d.js"; import { constructorOf } from "./struct.js"; /** transpose a 2d array (matrix). * * if you're wondering what's the difference between this and {@link transposeArray2D} from the {@link "array2d"} submodule, * then be my guest, because I am just as puzzled as you. * perhaps I forgot that I made one or the other, resulting in duplication. * * however, there are a few implementation differences between the these two functions: * - {@link transpose2D} works by calling the array `map` method. * - {@link transposeArray2D} works by assigning values to the new array's transposed indexes. * * similarities between the two functions: * - both transpose sparse 2d arrays to full 2d arrays with the sparse entries replaced with `undefined`. * - both use the first element (the first row) to determine the number of columns your 2d array has. * this can mean disastrous cropped results if a sparse 2d array is provided where the first row's * number of elements (i.e. number of columns) is not the greatest out of all the other rows. * * @example * ```ts * import { assertEquals } from "jsr:@std/assert" * * const sparse_arr2d: Array<number[]> = [ * [1 , 2 , 3 , 4 , 5 ], * [6 , 7 , 8 , 9], * [11, 12, 13], * [16, 17, 18, 19, 20], * ] * * const sparse_arr2d_transposed = transpose2D(sparse_arr2d) * * sparse_arr2d_transposed satisfies Array<number>[] * * assertEquals(sparse_arr2d_transposed, [ * [1 , 6 , 11 , 16], * [2 , 7 , 12 , 17], * [3 , 8 , 13 , 18], * [4 , 9 , undefined , 19], * [5 , undefined , undefined , 20], * ]) * ``` */ export const transpose2D = (matrix) => (matrix[0].map((_row_0_col_i, i) => matrix.map(row_arr => row_arr[i]))); /** compute the left-to-right running difference between successive elements. * * the returned array's length is decremented by one. * as a result, a single element array will turn into an empty array. * * > [!note] * > be careful when using with unsigned typed arrays. * * @example * ```ts * import { assertEquals } from "jsr:@std/assert" * * // basic example * const arr1 = [1, 2, 3, 4, 5, 99, 88] * const arr1_diff = diff(arr1) * assertEquals(arr1_diff, [1, 1, 1, 1, 94, -11]) * * // subarray slicing example * const arr2 = [1, 2, 3, 4, 5, 99, 88] * const arr2_diff = diff(arr2, 2, -1) * assertEquals(arr2_diff, [1, 1, 94]) * ``` */ export const diff = (arr, start, end) => { [start, end] = resolveRange(start, end, arr.length); const diff_arr = arr.slice(start + 1, end); for (let i = 0; i < diff_arr.length; i++) { diff_arr[i] -= arr[start + i]; } return diff_arr; }; /** compute the right-to-left (ie reverse) running difference between preceding elements. * * the returned array's length is decremented by one. * as a result, a single element array will turn into an empty array. * * > [!note] * > be careful when using with unsigned typed arrays. * * @example * ```ts * import { assertEquals } from "jsr:@std/assert" * * // basic example * const arr1 = [1, 2, 3, 4, 5, 99, 88] * const arr1_diff = diff_right(arr1) * assertEquals(arr1_diff, [-1, -1, -1, -1, -94, 11]) * * // subarray slicing example * const arr2 = [1, 2, 3, 4, 5, 99, 88] * const arr2_diff = diff_right(arr2, 2, -1) * assertEquals(arr2_diff, [-1, -1, -94]) * ``` */ export const diff_right = (arr, start, end) => { [start, end] = resolveRange(start, end, arr.length); const diff_arr = arr.slice(start, end - 1); for (let i = 0; i < diff_arr.length; i++) { diff_arr[i] -= arr[start + i + 1]; } return diff_arr; }; /** cumulative summation of an array. * * the returned array has its length increased by one. * * @example * ```ts * import { assertEquals } from "jsr:@std/assert" * * // basic example * const arr1 = [10, 20, 30, 40, 50] * const arr1_cum_sum = cumulativeSum(arr1) * assertEquals(arr1_cum_sum, [0, 10, 30, 60, 100, 150]) * ``` */ export const cumulativeSum = (arr) => { const len = arr.length, cum_sum = new (constructorOf(arr))(len + 1).fill(0); for (let i = 0; i < len; i++) { cum_sum[i + 1] = cum_sum[i] + arr[i]; } return cum_sum; }; /** conduct in-place unary arithmetic operations on numeric arrays * TODO: find out why I'm not exporting this function. did I simply forget? * @category inplace */ const unaryArithmetic = (operation, arr, start, end) => { const [xs, xe] = resolveRange(start, end, arr.length); switch (operation) { case "abs": return abs(arr, xs, xe); case "neg": return neg(arr, xs, xe); case "comp": return bcomp(arr, xs, xe); } }; /** conduct in-place scalar arithmetic operations on numeric arrays * TODO: find out why I'm not exporting this function. did I simply forget? * @category inplace */ const scalarArithmetic = (operation, arr, value, start, end) => { const [xs, xe] = resolveRange(start, end, arr.length); switch (operation) { case "add": return add(arr, value, xs, xe); case "sub": return sub(arr, value, xs, xe); case "mult": return mult(arr, value, xs, xe); case "div": return div(arr, value, xs, xe); case "pow": return pow(arr, value, xs, xe); case "rem": return rem(arr, value, xs, xe); case "mod": return mod(arr, value, xs, xe); case "and": return band(arr, value, xs, xe); case "or": return bor(arr, value, xs, xe); case "xor": return bxor(arr, value, xs, xe); case "<<": return blsh(arr, value, xs, xe); case ">>": return brsh(arr, value, xs, xe); case ">>>": return bursh(arr, value, xs, xe); } }; /// UNARY OPERATIONS /** mutate array in-place to get **absolute** value of elements. * * @category unaryOperator * @category inplace */ export const abs = (arr, start = 0, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] *= arr[i] < 0 ? -1 : 1; } return arr; }; /** mutate array in-place to get **negative** value of elements. * * @category unaryOperator * @category inplace */ export const neg = (arr, start = 0, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] *= -1; } return arr; }; /** mutate array in-place to get **bitwise complement** value of elements. * * @category unaryOperator * @category inplace */ export const bcomp = (arr, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] = ~arr[i]; } return arr; }; /// SCALAR OPERATIONS /// TODO consider replacing some functions entirely with a linear function `y = ax + b`. /** mutate array in-place to get **bitwise and** against a scalar `value`. * * @category scalarOperator * @category inplace */ export const band = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] &= value; } return arr; }; /** mutate array in-place to get **bitwise or** against a scalar `value`. * * @category scalarOperator * @category inplace */ export const bor = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] |= value; } return arr; }; /** mutate array in-place to get **bitwise xor** against a scalar `value`. * * @category scalarOperator * @category inplace */ export const bxor = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] ^= value; } return arr; }; /** mutate array in-place to get **bitwise left-shift** (`<<`) against a scalar `value`. * * @category scalarOperator * @category inplace */ export const blsh = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] <<= value; } return arr; }; /** mutate array in-place to get **bitwise right-shift** (`>>`) against a scalar `value`. * * @category scalarOperator * @category inplace */ export const brsh = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] >>= value; } return arr; }; /** mutate array in-place to get **bitwise unsigned right-shift** (`>>>`) against a scalar `value`. * * @category scalarOperator * @category inplace */ export const bursh = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] >>>= value; } return arr; }; /** mutate array in-place to **add** a scalar `value`. * * @category scalarOperator * @category inplace */ export const add = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] += value; } return arr; }; /** mutate array in-place to **subtract** a scalar `value`. * * @category scalarOperator * @category inplace */ export const sub = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] -= value; } return arr; }; /** mutate array in-place to **multiply** by a scalar `value`. * * @category scalarOperator * @category inplace */ export const mult = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] *= value; } return arr; }; /** mutate array in-place to **divide** by a scalar `value`. * * @category scalarOperator * @category inplace */ export const div = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] /= value; } return arr; }; /** mutate array in-place to raise it to the **power** of a scalar `value`. * * @category scalarOperator * @category inplace */ export const pow = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] **= value; } return arr; }; /** mutate array in-place to get the **remainder** (`%`) when divided by scalar `value`. * * note that this is slightly different from the modulo {@link mod} operator, as this can have a negative sign. * * @category scalarOperator * @category inplace */ export const rem = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] %= value; } return arr; }; /** mutate array in-place to get the **modulo** when divided by scalar `value`. * * note that this is slightly different from the remainder {@link rem} operator, as this always returns a positive number. * * @category scalarOperator * @category inplace */ export const mod = (arr, value, start, end) => { [start, end] = resolveRange(start, end, arr.length); for (let i = start; i < end; i++) { arr[i] = ((arr[i] % value) + value) % value; } return arr; };