ts-scikit
Version:
A scientific toolkit written in Typescript
556 lines • 15.3 kB
JavaScript
"use strict";
/**
* Utilities for arrays plus math methods
* @packageDocumentation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.binarySearch = exports.quickPartialSort = exports.ramp = exports.fill = exports.zero = exports.czero = exports.ccopy = exports.copy = exports.almostEqual = exports.isMonotonic = exports.isDecreasing = exports.isIncreasing = exports.toDegrees = exports.toRadians = exports.cosFromSin = exports.arrayDimensions = void 0;
///////////////////////////////////////////////////////////////////////////
// math methods
/** @internal */
const N_SMALL_SORT = 7;
/** @internal */
const N_LARGE_SORT = 40;
/**
* Returns the number of dimensions for the provided array.
* @param array the array.
* @param dim the starting dimension.
* @returns the number of dimensions.
*/
function arrayDimensions(array, dim = 0) {
return (array instanceof Array) ? arrayDimensions(array[0], dim + 1) : dim;
}
exports.arrayDimensions = arrayDimensions;
/**
* Takes the cosine of a number by using the sine and an additional angle.
* @param sin the sine of an angle.
* @param angle the angle.
* @returns the resulting cosine.
*/
function cosFromSin(sin, angle) {
// sin(x)^2 + cos(x)^2 = 1
const cos = Math.sqrt(1.0 - sin * sin);
const a = angle + (Math.PI / 2);
let b = a - Math.floor(a / (2 * Math.PI)) * (2 * Math.PI);
if (b < 0) {
b += (2 * Math.PI);
}
return (b >= Math.PI) ? -cos : cos;
}
exports.cosFromSin = cosFromSin;
/**
* Converts degrees to radians.
* @param degrees the angle in degrees
* @returns the angle in radians.
*/
function toRadians(degrees) {
return (degrees * Math.PI / 180.0);
}
exports.toRadians = toRadians;
/**
* Converts radians to degrees.
* @param radians the angle in radians.
* @returns the angle in degrees.
*/
function toDegrees(radians) {
return (radians * 180.0 / Math.PI);
}
exports.toDegrees = toDegrees;
/**
* Determines whether the specified array is increasing.
* The array is increasing if its elements a[i] increase with array index i,
* with no equal values.
* @param a the array.
* @returns true, if increasing (or a.length < 2); false, otherwise.
*/
function isIncreasing(a) {
const n = a.length;
if (n > 1) {
for (let i = 1; i < n; ++i) {
if (a[i - 1] > a[i]) {
return false;
}
}
}
return true;
}
exports.isIncreasing = isIncreasing;
/**
* Determines whether the specified array is decreasing.
* The array is decreasing if its elements a[i] decrease with array index i,
* with no equal values.
* @param a the array.
* @returns true, if decreasing (or a.length < 2); false, otherwise.
*/
function isDecreasing(a) {
const n = a.length;
if (n > 1) {
for (let i = 1; i < n; ++i) {
if (a[i - 1] < a[i]) {
return false;
}
}
}
return true;
}
exports.isDecreasing = isDecreasing;
/**
* Determines whether the specified array is monotonic.
* The array is monotonic if its elements[i] either increase or
* decrease (but not both) with array index i, with no equal values.
* @param a the array.
* @returns true, if monotonic (or a.length < 2); false, otherwise.
*/
function isMonotonic(a) {
return isIncreasing(a) || isDecreasing(a);
}
exports.isMonotonic = isMonotonic;
/**
* Determines if two values are almost equal given a provided tolerance.
* @param v1 a value.
* @param v2 a value.
* @param tiny the tolerance.
* @returns true, if almost equal; false, otherwise.
*/
function almostEqual(v1, v2, tiny) {
const diff = v2 - v1;
return (diff < 0.0) ? -diff < tiny : diff < tiny;
}
exports.almostEqual = almostEqual;
function copy(rx, p1, p2, p3, p4, p5, p6, p7, p8, p9) {
switch (arrayDimensions(rx)) {
case 1:
return _copy1d(rx, p1, p2, p3);
case 2:
return _copy2d(rx, p1, p2, p3, p4, p5, p6);
default:
return _copy3d(rx, p1, p2, p3, p4, p5, p6, p7, p8, p9);
}
}
exports.copy = copy;
function ccopy(cx, n1, p2, p3, p4, p5, p6, p7, p8, p9) {
if (!n1) {
n1 = cx.length;
}
switch (arrayDimensions(cx)) {
case 1:
return _copy1d(cx, n1 * 2, p2, p3, true);
case 2:
return _copy2d(cx, n1 * 2, p2, p3, p4, p5, p6, true);
default:
return _copy3d(cx, n1 * 2, p2, p3, p4, p5, p6, p7, p8, p9, true);
}
}
exports.ccopy = ccopy;
function czero(cxn1, n2, n3) {
switch (arrayDimensions(cxn1)) {
case 1:
_fill1D(cxn1, 0);
return;
case 2:
_fill2D(cxn1, 0);
return;
case 3:
_fill3D(cxn1, 0);
return;
}
if (n3) {
const rx = new Array(n3);
for (let i3 = 0; i3 < n3; ++i3) {
rx[i3] = new Array(n2);
for (let i2 = 0; i2 < n2; ++i2) {
rx[i3][i2] = Array.from({ length: 2 * cxn1 }, () => 0);
}
}
return rx;
}
else if (n2) {
const rx = new Array(n2);
for (let i2 = 0; i2 < n2; ++i2) {
rx[i2] = Array.from({ length: 2 * cxn1 }, () => 0);
}
return rx;
}
else {
return Array.from({ length: 2 * cxn1 }, () => 0);
}
}
exports.czero = czero;
function zero(rxn1, n2, n3) {
switch (arrayDimensions(rxn1)) {
case 1:
_fill1D(rxn1, 0);
return;
case 2:
_fill2D(rxn1, 0);
return;
case 3:
_fill3D(rxn1, 0);
return;
}
if (n3) {
const rx = new Array(n3);
for (let i3 = 0; i3 < n3; ++i3) {
rx[i3] = new Array(n2);
for (let i2 = 0; i2 < n2; ++i2) {
rx[i3][i2] = Array.from({ length: rxn1 }, () => 0);
}
}
return rx;
}
else if (n2) {
const rx = new Array(n2);
for (let i2 = 0; i2 < n2; ++i2) {
rx[i2] = Array.from({ length: rxn1 }, () => 0);
}
return rx;
}
else {
return Array.from({ length: rxn1 }, () => 0);
}
}
exports.zero = zero;
function fill(p1, p2, p3, p4) {
switch (arrayDimensions(p1)) {
case 1:
_fill1D(p1, p2);
return;
case 2:
_fill2D(p1, p2);
return;
case 3:
_fill3D(p1, p2);
return;
}
if (p4) {
const rx = new Array(p4);
for (let i3 = 0; i3 < p4; ++i3) {
rx[i3] = new Array(p3);
for (let i2 = 0; i2 < p3; ++i2) {
rx[i3][i2] = Array.from({ length: p2 }, () => p1);
}
}
return rx;
}
else if (p3) {
const rx = new Array(p3);
for (let i2 = 0; i2 < p3; ++i2) {
rx[i2] = Array.from({ length: p2 }, () => p1);
}
return rx;
}
else {
return Array.from({ length: p2 }, () => p1);
}
}
exports.fill = fill;
/** @internal */
function _fill1D(rx, fill) {
const n1 = rx.length;
for (let i1 = 0; i1 < n1; ++i1) {
rx[i1] = fill;
}
}
/** @internal */
function _fill2D(rx, fill) {
const n2 = rx.length;
for (let i2 = 0; i2 < n2; ++i2) {
_fill1D(rx[i2], fill);
}
}
/** @internal */
function _fill3D(rx, fill) {
const n3 = rx.length;
for (let i3 = 0; i3 < n3; ++i3) {
_fill2D(rx[i3], fill);
}
}
function ramp(rxa, p1, p2, p3, p4, p5, p6) {
// If passing in an array, return reference
switch (arrayDimensions(rxa)) {
case 1:
_ramp1d(rxa, p1, p2);
return;
case 2:
_ramp2d(rxa, p1, p2, p3);
return;
case 3:
_ramp3d(rxa, p1, p2, p3, p4);
return;
}
// Otherwise, create new and return
if (p6) {
const rx = zero(p4, p5, p6);
_ramp3d(rx, rxa, p1, p2, p3);
return rx;
}
else if (p4) {
const rx = zero(p3, p4);
_ramp2d(rx, rxa, p1, p2);
return rx;
}
else {
const rx = zero(p2);
_ramp1d(rx, rxa, p1);
return rx;
}
}
exports.ramp = ramp;
/** @internal */
function _ramp1d(rx, ra, rb1) {
const n1 = rx.length;
for (let i1 = 0; i1 < n1; ++i1) {
rx[i1] = ra + rb1 * i1;
}
}
/** @internal */
function _ramp2d(rx, ra, rb1, rb2) {
const n2 = rx.length;
for (let i2 = 0; i2 < n2; ++i2) {
_ramp1d(rx[i2], ra + rb2 * i2, rb1);
}
}
/** @internal */
function _ramp3d(rx, ra, rb1, rb2, rb3) {
const n3 = rx.length;
for (let i3 = 0; i3 < n3; ++i3) {
_ramp2d(rx[i3], ra + rb3 * i3, rb1, rb2);
}
}
///////////////////////////////////////////////////////////////////////////
// real-to-complex
///////////////////////////////////////////////////////////////////////////
/** @internal */
class RealToComplex {
apply(rx, ry, cz) {
const cdim = arrayDimensions(cz);
const rdim = arrayDimensions(rx);
switch (arrayDimensions(rx)) {
case 1:
case 2:
default:
}
}
}
///////////////////////////////////////////////////////////////////////////
// misc algorithms
///////////////////////////////////////////////////////////////////////////
/**
* Performs a quick partial sort.
* @param k
* @param arr
*/
function quickPartialSort(k, arr) {
const n = arr.length;
let p = 0;
let q = n - 1;
const m = (n > N_SMALL_SORT) ? new Array(2) : null;
while (q - p >= N_SMALL_SORT) {
m[0] = p;
m[1] = q;
quickPartition(arr, m);
if (k < m[0]) {
q = m[0] - 1;
}
else if (k > m[1]) {
p = m[1] + 1;
}
else {
return;
}
}
insertionSort(arr, p, q);
}
exports.quickPartialSort = quickPartialSort;
/**
* Performs a binary search in a monotonic array of values.
* <p>
* Values are assumed to increase or decrease monotonically, with no equal
* values.
* <p>
* Warning: this method does not ensure that the specified array is
* monotonic; that check would be more costly than this search.
* @param a the array of values, assumed to be monotonic.
* @param x the value for which to search.
* @param i the index at which to begin the search. If negative, this
* method interprets this index as if returned from a
* previous call.
* @returns the index at which the specified value is found, or, if not
* found, -(i + 1), where i equals the index at which the
* specified value would be located if it was inserted into
* the monotonic array.
*/
function binarySearch(a, x, i) {
if (!i) {
i = a.length;
}
const n = a.length;
const nm1 = n - 1;
let low = 0;
let high = nm1;
const increasing = n < 2 || a[0] < a[1];
if (i < n) {
high = (0 <= i) ? i : -(i + 1);
low = high - 1;
let step = 1;
if (increasing) {
for (; 0 < low && x < a[low]; low -= step, step += step) {
high = low;
}
for (; high < nm1 && a[high] < x; high += step, step += step) {
low = high;
}
}
else {
for (; 0 < low && x > a[low]; low -= step, step += step) {
high = low;
}
for (; high < nm1 && a[high] > x; high += step, step += step) {
low = high;
}
}
if (low < 0) {
low = 0;
}
if (high > nm1) {
high = nm1;
}
}
if (increasing) {
while (low <= high) {
const mid = (low + high) >> 1;
const amid = a[mid];
if (amid < x) {
low = mid + 1;
}
else if (amid > x) {
high = mid - 1;
}
else {
return mid;
}
}
}
else {
while (low <= high) {
const mid = (low + high) >> 1;
const amid = a[mid];
if (amid > x) {
low = mid + 1;
}
else if (amid < x) {
high = mid - 1;
}
else {
return mid;
}
}
}
return -(low + 1);
}
exports.binarySearch = binarySearch;
/** @internal */
function med3(a, i, j, k) {
return a[i] < a[j] ?
(a[j] < a[k] ? j : a[i] < a[k] ? k : i) :
(a[j] > a[k] ? j : a[i] > a[k] ? k : i);
}
/** @internal */
function swap(a, i, j, n = 1) {
while (n > 0) {
const ai = a[i];
a[i++] = a[j];
a[j++] = ai;
--n;
}
}
/** @internal */
function insertionSort(a, p, q) {
for (let i = p; i <= q; ++i) {
for (let j = i; j > p && a[j - 1] > a[j]; --j) {
swap(a, j, j - 1);
}
}
}
/** @internal */
function quickPartition(x, m) {
const p = m[0];
const q = m[1];
const n = q - p + 1;
let k = (p + q) / 2;
if (n > N_SMALL_SORT) {
let j = q;
let l = q;
if (n > N_LARGE_SORT) {
const s = n / 8;
j = med3(x, j, j + s, j + 2 * s);
k = med3(x, k - s, k, k + s);
l = med3(x, l - 2 * s, l - s, l);
}
k = med3(x, j, k, l);
}
const y = x[k];
let a = p, b = p;
let c = q, d = q;
while (true) {
while (b <= c && x[b] <= y) {
if (x[b] === y) {
swap(x, a++, b);
}
++b;
}
while (c >= b && x[c] >= y) {
if (x[c] === y) {
swap(x, c, d--);
}
--c;
}
if (b > c) {
break;
}
swap(x, b, c);
++b;
--c;
}
const r = Math.min(a - p, b - a);
const s = Math.min(d - c, q - d);
const t = q + 1;
swap(x, p, b - r, r);
swap(x, b, t - s, s);
m[0] = p + (b - a); // p --- m[0]-1 | m[0] --- m[1] | m[1]+1 --- q
m[1] = q - (d - c); // x<y x=y x>y
}
/** @internal */
function _copy1d(x, n1, j1 = 0, k1 = 1, complex = false) {
const m = (complex) ? 2.0 : 1.0;
const c = (complex) ? 1.0 : 0.0;
const k1xm = k1 * m;
const k1ym = (complex) ? 2.0 : 1.0;
const xdm = x.length / m;
n1 = (!n1) ? xdm : Math.min(xdm, Math.max(0, n1 / m));
const y = new Array(n1);
for (let i1 = 0, ix = m * j1, iy = 0; i1 < n1; ++i1, ix += k1xm, iy += k1ym) {
y[iy] = x[ix];
y[iy + c] = x[ix + c];
}
return y;
}
/** @internal */
function _copy2d(x, n1, n2, j1 = 0, j2 = 0, k1 = 1, k2 = 1, complex = false) {
n2 = (!n2) ? x.length : Math.min(x.length, Math.max(0, n2));
const y = new Array(n2);
for (let i2 = 0; i2 < n2; ++i2) {
y[i2] = _copy1d(x[j2 + i2 * k2], n1, j1, k1, complex);
}
return y;
}
/** @internal */
function _copy3d(x, n1, n2, n3, j1 = 0, j2 = 0, j3 = 0, k1 = 1, k2 = 1, k3 = 1, complex = false) {
n3 = (!n3) ? x.length : Math.min(x.length, Math.max(0, n3));
const y = new Array(n3);
for (let i3 = 0; i3 < n3; ++i3) {
y[i3] = _copy2d(x[j3 + i3 * k3], n1, n2, j1, j2, k1, k2, complex);
}
return y;
}
//# sourceMappingURL=array-math.js.map