petcarescript
Version:
PetCareScript - A modern, expressive programming language designed for humans
333 lines (277 loc) • 11.1 kB
JavaScript
/**
* PetCareScript Standard Library - Math
* Mathematical functions - VERSÃO COMPLETA E CORRIGIDA
*/
const MathLib = {
// Constants
PI: Math.PI,
E: Math.E,
LN2: Math.LN2,
LN10: Math.LN10,
LOG2E: Math.LOG2E,
LOG10E: Math.LOG10E,
SQRT1_2: Math.SQRT1_2,
SQRT2: Math.SQRT2,
// Basic functions
abs: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.abs(x);
},
max: (...args) => {
if (args.length === 0) return -Infinity;
for (const arg of args) {
if (typeof arg !== 'number') throw new Error('All arguments must be numbers');
}
return Math.max(...args);
},
min: (...args) => {
if (args.length === 0) return Infinity;
for (const arg of args) {
if (typeof arg !== 'number') throw new Error('All arguments must be numbers');
}
return Math.min(...args);
},
// Rounding
round: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.round(x);
},
ceil: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.ceil(x);
},
floor: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.floor(x);
},
trunc: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.trunc(x);
},
// Powers and roots
pow: (base, exponent) => {
if (typeof base !== 'number' || typeof exponent !== 'number') {
throw new Error('Both arguments must be numbers');
}
return Math.pow(base, exponent);
},
sqrt: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x < 0) throw new Error('Cannot calculate square root of negative number');
return Math.sqrt(x);
},
cbrt: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.cbrt(x);
},
exp: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.exp(x);
},
// Logarithms
log: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x <= 0) throw new Error('Logarithm argument must be positive');
return Math.log(x);
},
log10: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x <= 0) throw new Error('Logarithm argument must be positive');
return Math.log10(x);
},
log2: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x <= 0) throw new Error('Logarithm argument must be positive');
return Math.log2(x);
},
// Trigonometry
sin: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.sin(x);
},
cos: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.cos(x);
},
tan: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.tan(x);
},
asin: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x < -1 || x > 1) throw new Error('Argument must be between -1 and 1');
return Math.asin(x);
},
acos: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x < -1 || x > 1) throw new Error('Argument must be between -1 and 1');
return Math.acos(x);
},
atan: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.atan(x);
},
atan2: (y, x) => {
if (typeof x !== 'number' || typeof y !== 'number') {
throw new Error('Both arguments must be numbers');
}
return Math.atan2(y, x);
},
// Hyperbolic functions
sinh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.sinh(x);
},
cosh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.cosh(x);
},
tanh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.tanh(x);
},
asinh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
return Math.asinh(x);
},
acosh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x < 1) throw new Error('Argument must be >= 1');
return Math.acosh(x);
},
atanh: (x) => {
if (typeof x !== 'number') throw new Error('Argument must be a number');
if (x <= -1 || x >= 1) throw new Error('Argument must be between -1 and 1');
return Math.atanh(x);
},
// Random functions
random: () => Math.random(),
randomBetween: (min, max) => {
if (typeof min !== 'number' || typeof max !== 'number') {
throw new Error('Both arguments must be numbers');
}
return Math.random() * (max - min) + min;
},
randomInt: (min, max) => {
if (typeof min !== 'number' || typeof max !== 'number') {
throw new Error('Both arguments must be numbers');
}
return Math.floor(Math.random() * (max - min + 1)) + min;
},
randomChoice: (array) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
if (array.length === 0) throw new Error('Array cannot be empty');
return array[Math.floor(Math.random() * array.length)];
},
// Precision functions
toFixed: (number, decimals) => {
if (typeof number !== 'number') throw new Error('First argument must be a number');
if (typeof decimals !== 'number') throw new Error('Second argument must be a number');
return Number(number.toFixed(decimals));
},
toPrecision: (number, precision) => {
if (typeof number !== 'number') throw new Error('First argument must be a number');
if (typeof precision !== 'number') throw new Error('Second argument must be a number');
return Number(number.toPrecision(precision));
},
// Checks
isNaN: (x) => isNaN(x),
isFinite: (x) => isFinite(x),
isInteger: (x) => Number.isInteger(x),
// Conversions
degreesToRadians: (degrees) => {
if (typeof degrees !== 'number') throw new Error('Argument must be a number');
return degrees * (Math.PI / 180);
},
radiansToDegrees: (radians) => {
if (typeof radians !== 'number') throw new Error('Argument must be a number');
return radians * (180 / Math.PI);
},
// Statistical functions
sum: (array) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
return array.reduce((sum, val) => {
if (typeof val !== 'number') throw new Error('All array elements must be numbers');
return sum + val;
}, 0);
},
average: (array) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
if (array.length === 0) throw new Error('Array cannot be empty');
return MathLib.sum(array) / array.length;
},
median: (array) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
if (array.length === 0) throw new Error('Array cannot be empty');
const sorted = [...array].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
return sorted.length % 2 === 0
? (sorted[mid - 1] + sorted[mid]) / 2
: sorted[mid];
},
mode: (array) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
if (array.length === 0) throw new Error('Array cannot be empty');
const frequency = {};
let maxFreq = 0;
let modes = [];
for (const num of array) {
frequency[num] = (frequency[num] || 0) + 1;
if (frequency[num] > maxFreq) {
maxFreq = frequency[num];
modes = [num];
} else if (frequency[num] === maxFreq && !modes.includes(num)) {
modes.push(num);
}
}
return modes.length === 1 ? modes[0] : modes;
},
variance: (array, population = false) => {
if (!Array.isArray(array)) throw new Error('Argument must be an array');
if (array.length === 0) throw new Error('Array cannot be empty');
const mean = MathLib.average(array);
const squaredDiffs = array.map(val => Math.pow(val - mean, 2));
const divisor = population ? array.length : array.length - 1;
return MathLib.sum(squaredDiffs) / divisor;
},
standardDeviation: (array, population = false) => {
return Math.sqrt(MathLib.variance(array, population));
},
// Utility functions
clamp: (value, min, max) => {
if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
throw new Error('All arguments must be numbers');
}
return Math.max(min, Math.min(max, value));
},
lerp: (start, end, factor) => {
if (typeof start !== 'number' || typeof end !== 'number' || typeof factor !== 'number') {
throw new Error('All arguments must be numbers');
}
return start + (end - start) * factor;
},
map: (value, fromMin, fromMax, toMin, toMax) => {
if (typeof value !== 'number' || typeof fromMin !== 'number' || typeof fromMax !== 'number' ||
typeof toMin !== 'number' || typeof toMax !== 'number') {
throw new Error('All arguments must be numbers');
}
const factor = (value - fromMin) / (fromMax - fromMin);
return MathLib.lerp(toMin, toMax, factor);
},
// Distance functions
distance2D: (x1, y1, x2, y2) => {
if (typeof x1 !== 'number' || typeof y1 !== 'number' ||
typeof x2 !== 'number' || typeof y2 !== 'number') {
throw new Error('All arguments must be numbers');
}
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
},
distance3D: (x1, y1, z1, x2, y2, z2) => {
if (typeof x1 !== 'number' || typeof y1 !== 'number' || typeof z1 !== 'number' ||
typeof x2 !== 'number' || typeof y2 !== 'number' || typeof z2 !== 'number') {
throw new Error('All arguments must be numbers');
}
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2));
}
};
module.exports = MathLib;