summaryrefslogtreecommitdiff
path: root/src/app/utils/math_utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/utils/math_utils.js')
-rw-r--r--src/app/utils/math_utils.js94
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;
+ };
+}