diff options
Diffstat (limited to 'src/app/utils/math_utils.js')
| -rw-r--r-- | src/app/utils/math_utils.js | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/app/utils/math_utils.js b/src/app/utils/math_utils.js new file mode 100644 index 0000000..f5482d3 --- /dev/null +++ b/src/app/utils/math_utils.js @@ -0,0 +1,94 @@ +/** + * Mathematical utilities. + * @module app/utils/math_utils + */ + +/** + * RegExp to check if a string contains only hexadecimal values. + * The string should only contain the characters [0-9] or [a-f], case-insensitive. + * @type {RegExp} + */ +const HEXADECIMAL_REGEXP = new RegExp("^[a-f0-9]+$", "i"); + +/** + * Check if a string is a valid hexadecimal number. + * @param {String} text the string to check + * @return {Boolean} true if the string is a valid hexadecimal number + */ +export const isHexadecimal = (text) => !!text?.match(HEXADECIMAL_REGEXP); + +/** + * Constrain a number between an upper or lower bound. + * @param {Number} value the value to clamp + * @param {Number} low the lower bound + * @param {Number} high the upper bound + * @return {Number} the clamped value + */ +export const clamp = (value, low = 0, high = 1) => + value < low ? low : value < high ? value : high; + +/** + * Implementation of mod that supports negative numbers (unlike JavaScript % operator) + * @param {Number} numerator the modulo numerator + * @param {Number} denominator the modulo denominator + * @return {Number} `numerator mod denominator` + */ +export const mod = (numerator, denominator) => + numerator - denominator * Math.floor(numerator / denominator); + +/** + * Get the mean of a list of numbers (where non-null) + * @param {Array} numbers list of numbers + * @return {number} arithmetic mean + */ +export const arrayMean = (numbers) => { + const nonZero = (numbers || []).filter((number) => !!number); + if (!nonZero.length) return 0; + const sum = nonZero.reduce((a, b) => { + return a + b; + }, 0); + return sum / nonZero.length; +}; + +/** + * Find the (planar) centroid of a set of points + * @param {Array} items list of location-like items having { lat, lng } + * @return {Object} object with averaged lat/lng + */ +export const centroid = (items) => ({ + lat: arrayMean(items.map((item) => item.lat)), + lng: arrayMean(items.map((item) => item.lng)), +}); + +/** + * Returns a gaussian (normal) random function with the given mean and stdev. + * @param {Number} mean center value + * @param {Number} stdev standard deviation (radius around mean) + * @return {Function} function generating numbers with a normal distribution + */ +export function gaussian(mean, stdev, random = Math.random) { + let y2; + let use_last = false; + return () => { + let y1; + if (use_last) { + y1 = y2; + use_last = false; + } else { + let x1, x2, w; + do { + x1 = 2.0 * random() - 1.0; + x2 = 2.0 * random() - 1.0; + w = x1 * x1 + x2 * x2; + } while (w >= 1.0); + w = Math.sqrt((-2.0 * Math.log(w)) / w); + y1 = x1 * w; + y2 = x2 * w; + use_last = true; + } + + return mean + stdev * y1; + // if (retval > 0) return retval; + // return -retval; + }; +} |
