'use strict';
//TODO: Merge these into the f lib, once that's integrated.
/**
* @module Utils
*/
/**
* Finds the mean of a set of numbers. Array must not be empty.
* @param {Array<number>} array
* @return {number}
*/
const ArrayCalculateAverage = (array) => {
if (array.length === 0) {
throw new Error('Cannot calculate average. Array is empty.');
}
//TODO: Move into EpochStats, where these can be done more efficiently.
//PERF: Don't walk the whole set. Instead, discount a running average (which
// we'll keep separately and pass in), by (droppedSample / total), then
// add (addedSample / total).
const SUM = array.reduce((previous, current) => {
return previous + current;
}, 0);
return SUM / array.length;
};
/**
* Finds the largest value in an array of numbers, and returns the index of that
* value, e.g. [1, 3, 2] returns 1. Array must not be empty.
* @param {Array<number>} values
* @return {number}
*/
const ArrayFindIndexOfHighestValue = (values) => {
if (values.length === 0) {
throw new Error('Cannot find highest. Array is empty.');
}
let indexOfHighest = 0;
let highestValue = Number.MIN_VALUE;
for (let p = 0; p < values.length; ++p) {
if (values[p] < highestValue) {
continue;
}
indexOfHighest = p;
highestValue = values[p];
}
return indexOfHighest;
};
/**
* Standard assertion. Throws if condition is false.<br>
* Note: Todo: To better merge w/ Jest, I'll propagate this throughout, and
* build in a preprocessor switch, driven by Node launch arg.
* @param {boolean} condition
* @return {void}
*/
const Assert = (condition) => {
if (condition) {
return;
}
throw new Error('assertion failed');
};
/**
* Returns true if x is in the range { 0 < x < 1 }.
* @param {number} x
* @return {boolean}
*/
const CheckFloat0to1Exclusive = (x) => {
if (x <= 0) {
return false;
}
if (x >= 1) {
return false;
}
return true;
};
/**
* Returns true if x is a positive integer or zero.
* @param {number} x
* @return {boolean}
*/
const CheckNonNegativeInteger = (x) => {
if (x < 0) {
return false;
}
if (x !== Math.floor(x)) {
return false;
}
return true;
};
/**
* Returns true if x is an integer greater than zero.
* @param {number} x
* @return {boolean}
*/
const CheckPositiveInteger = (x) => {
if (!CheckNonNegativeInteger(x)) {
return false;
}
if (x < 1) {
return false;
}
return true;
};
/**
* Fills a queue with numbers up to a length limit. Once that limit has been
* reached, it drops the oldest first (does a dequeue before the enqueue).
* @param {Array<number>} queue The set of numbers.
* @param {number} newSample The number to add.
* @param {number} count The queue max-size limit.
*/
const QueueRotate = (queue, newSample, count) => {
if (count < 1) {
throw new Error('queue length limit must be >= 1 :' + count);
}
queue.push(newSample);
if (queue.length <= count) {
return;
}
queue.shift();
};
/**
* Throws a relayed exception, with logic that checks the type of the
* caught object in order produce a cleaner error message.<br>
* Note: This is done because TypeScript does not yet have typed catch().
* @param {string} messagePrefix A short message to prepend to the thrown error.
* @param {unknown} errorOrException The object originally caught.
*/
const ThrowCaughtUnknown = (messagePrefix, errorOrException) => {
if (typeof errorOrException === 'string') {
throw new Error(messagePrefix + errorOrException);
}
if (errorOrException instanceof Error) {
throw new Error(messagePrefix + errorOrException.message);
}
throw new Error(messagePrefix + 'unknown exception type');
};
/**
* Takes arguments in a variety of types, converts them to strings, and checks
* whether those string representations will break CSV formatting. Specifically,
* it throws in the event it finds a comma or newline. Otherwise it returns
* silently.
* @param {(string | number | boolean)} x
*/
const ValidateTextForCSV = (x) => {
//NOTE: Add whichever (just not TS any) input type. That's the point, here.
// We're looking at the argument after it's been cast to string, to ensure
// we have cleanly CSV-able information for file write().
//
//UPDATE: Now that this takes primitives only, it should likely be string-only.
// There is a growing case to toss it entirely. Stay tuned for the
// complex axes (e.g. activation functions, etc...).
const AS_STRING = x.toString();
if (AS_STRING.indexOf(',') === -1 && AS_STRING.indexOf('\n') === -1) {
return;
}
throw new Error('Value contains comma or newline (which interferes with '
+ 'CSV): ' + x + ', ' + AS_STRING);
};
/**
* Creates a string that represents a duration in milliseconds, and variations
* of the same in seconds, minutes and hours.
* @param {number} durationMS The time in milliseconds
* @return {string} Example: "15000 ms / 15.00 sec / 0.25 min / 0.0 hr"
*/
const WriteDurationReport = (durationMS) => {
Assert(durationMS >= 0);
//TODO: (low-pri) Bring in time-reporting from the f lib, which has smart
// duration-category picking.
return durationMS + ' ms'
+ ' / '
+ (durationMS / 1000).toFixed(1) + ' sec'
+ ' / '
+ (durationMS / 60 / 1000).toFixed(2) + ' min'
+ ' / '
+ (durationMS / 60 / 60 / 1000).toFixed(3) + ' hr'
+ ' / '
+ (durationMS / 24 / 60 / 60 / 1000).toFixed(4) + ' dy';
};
export { ArrayCalculateAverage, ArrayFindIndexOfHighestValue, Assert, CheckNonNegativeInteger, CheckFloat0to1Exclusive, CheckPositiveInteger, QueueRotate, ThrowCaughtUnknown, ValidateTextForCSV, WriteDurationReport };
//# sourceMappingURL=Utils.js.map