/** * 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; }; }