@calmhive/calmhive-cli
Version:
🐝 Intelligent Claude CLI wrapper with rule injection, AFk background processing, voice interface, and adaptive retry
232 lines (219 loc) • 7.5 kB
JavaScript
/**
* Mathematical utility functions for common operations
* Provides safe, tested mathematical methods with proper validation
*
* MODERNIZED: Now using lodash for battle-tested implementations
* Maintains 100% API compatibility with previous version
*/
const _ = require('lodash');
class MathUtils {
/**
* Clamps a number within the inclusive lower and upper bounds
* @param {number} value - The number to clamp
* @param {number} min - The lower bound
* @param {number} max - The upper bound
* @returns {number} The clamped number
* @throws {Error} If inputs are not numbers or bounds are invalid
*/
static clamp(value, min, max) {
if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
throw new Error('All inputs must be numbers');
}
if (isNaN(value) || isNaN(min) || isNaN(max)) {
throw new Error('Inputs cannot be NaN');
}
if (min > max) {
throw new Error('Min cannot be greater than max');
}
return _.clamp(value, min, max);
}
/**
* Linear interpolation between two values
* @param {number} start - The start value
* @param {number} end - The end value
* @param {number} t - The interpolation factor (0 to 1)
* @returns {number} The interpolated value
* @throws {Error} If inputs are not numbers
*/
static lerp(start, end, t) {
if (typeof start !== 'number' || typeof end !== 'number' || typeof t !== 'number') {
throw new Error('All inputs must be numbers');
}
if (isNaN(start) || isNaN(end) || isNaN(t)) {
throw new Error('Inputs cannot be NaN');
}
// No direct lodash equivalent, keep the original implementation
return start + (end - start) * t;
}
/**
* Rounds a number to specified decimal places
* @param {number} value - The number to round
* @param {number} decimals - Number of decimal places (default: 0)
* @returns {number} The rounded number
* @throws {Error} If inputs are not numbers or decimals is negative
*/
static roundTo(value, decimals = 0) {
if (typeof value !== 'number') {
throw new Error('Value must be a number');
}
if (typeof decimals !== 'number' || !Number.isInteger(decimals)) {
throw new Error('Decimals must be an integer');
}
if (isNaN(value)) {
throw new Error('Value cannot be NaN');
}
if (decimals < 0) {
throw new Error('Decimals cannot be negative');
}
return _.round(value, decimals);
}
/**
* Checks if a number is within the specified range (inclusive)
* @param {number} value - The number to check
* @param {number} min - The minimum value
* @param {number} max - The maximum value
* @returns {boolean} True if value is within range
* @throws {Error} If inputs are not numbers or bounds are invalid
*/
static isInRange(value, min, max) {
if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
throw new Error('All inputs must be numbers');
}
if (isNaN(value) || isNaN(min) || isNaN(max)) {
throw new Error('Inputs cannot be NaN');
}
if (min > max) {
throw new Error('Min cannot be greater than max');
}
// lodash inRange is exclusive on upper bound, but we want inclusive
return value >= min && value <= max;
}
/**
* Calculates the average of an array of numbers
* @param {number[]} numbers - Array of numbers
* @returns {number} The average value
* @throws {Error} If input is not an array or contains non-numbers
*/
static average(numbers) {
if (!Array.isArray(numbers)) {
throw new Error('Input must be an array');
}
if (numbers.length === 0) {
throw new Error('Array cannot be empty');
}
for (let i = 0; i < numbers.length; i++) {
if (typeof numbers[i] !== 'number') {
throw new Error('All array elements must be numbers');
}
if (isNaN(numbers[i])) {
throw new Error('Array elements cannot be NaN');
}
}
return _.mean(numbers);
}
/**
* Calculates the sum of an array of numbers
* @param {number[]} numbers - Array of numbers
* @returns {number} The sum of all numbers
* @throws {Error} If input is not an array or contains non-numbers
*/
static sum(numbers) {
if (!Array.isArray(numbers)) {
throw new Error('Input must be an array');
}
if (numbers.length === 0) {
return 0;
}
for (let i = 0; i < numbers.length; i++) {
if (typeof numbers[i] !== 'number') {
throw new Error('All array elements must be numbers');
}
if (isNaN(numbers[i])) {
throw new Error('Array elements cannot be NaN');
}
}
return _.sum(numbers);
}
/**
* Finds the maximum value in an array of numbers
* @param {number[]} numbers - Array of numbers
* @returns {number} The maximum value
* @throws {Error} If input is not an array or contains non-numbers
*/
static max(numbers) {
if (!Array.isArray(numbers)) {
throw new Error('Input must be an array');
}
if (numbers.length === 0) {
throw new Error('Array cannot be empty');
}
for (let i = 0; i < numbers.length; i++) {
if (typeof numbers[i] !== 'number') {
throw new Error('All array elements must be numbers');
}
if (isNaN(numbers[i])) {
throw new Error('Array elements cannot be NaN');
}
}
return _.max(numbers);
}
/**
* Finds the minimum value in an array of numbers
* @param {number[]} numbers - Array of numbers
* @returns {number} The minimum value
* @throws {Error} If input is not an array or contains non-numbers
*/
static min(numbers) {
if (!Array.isArray(numbers)) {
throw new Error('Input must be an array');
}
if (numbers.length === 0) {
throw new Error('Array cannot be empty');
}
for (let i = 0; i < numbers.length; i++) {
if (typeof numbers[i] !== 'number') {
throw new Error('All array elements must be numbers');
}
if (isNaN(numbers[i])) {
throw new Error('Array elements cannot be NaN');
}
}
return _.min(numbers);
}
/**
* Generates a random number between min and max (inclusive)
* @param {number} min - The minimum value
* @param {number} max - The maximum value
* @returns {number} A random number between min and max
* @throws {Error} If inputs are not numbers or bounds are invalid
*/
static randomBetween(min, max) {
if (typeof min !== 'number' || typeof max !== 'number') {
throw new Error('Both inputs must be numbers');
}
if (isNaN(min) || isNaN(max)) {
throw new Error('Inputs cannot be NaN');
}
if (min > max) {
throw new Error('Min cannot be greater than max');
}
return _.random(min, max, true); // true for floating point
}
/**
* Generates a random integer between min and max (inclusive)
* @param {number} min - The minimum value
* @param {number} max - The maximum value
* @returns {number} A random integer between min and max
* @throws {Error} If inputs are not integers or bounds are invalid
*/
static randomIntBetween(min, max) {
if (!Number.isInteger(min) || !Number.isInteger(max)) {
throw new Error('Both inputs must be integers');
}
if (min > max) {
throw new Error('Min cannot be greater than max');
}
return _.random(min, max); // default is integer
}
}
module.exports = MathUtils;