use crate::evaluator::RuntimeError;
use crate::value::Value;
use std::f64::consts;
pub fn abs(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.abs())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn floor(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.floor())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn ceil(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.ceil())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn round(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.round())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn sqrt(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n < 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot take square root of negative number: {}",
n
)));
}
Ok(Value::Number(n.sqrt()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn pow(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Number(base), Value::Number(exp)) => Ok(Value::Number(base.powf(*exp))),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn sin(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.sin())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn cos(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.cos())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn tan(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.tan())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn log(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot take logarithm of non-positive number: {}",
n
)));
}
Ok(Value::Number(n.log10()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn ln(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot take natural logarithm of non-positive number: {}",
n
)));
}
Ok(Value::Number(n.ln()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn exp(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.exp())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn asin(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n < -1.0 || *n > 1.0 {
return Err(RuntimeError::InvalidOperation(format!(
"asin domain error: argument must be in [-1, 1], got {}",
n
)));
}
Ok(Value::Number(n.asin()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn acos(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n < -1.0 || *n > 1.0 {
return Err(RuntimeError::InvalidOperation(format!(
"acos domain error: argument must be in [-1, 1], got {}",
n
)));
}
Ok(Value::Number(n.acos()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn atan(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.atan())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn atan2(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Number(y), Value::Number(x)) => Ok(Value::Number(y.atan2(*x))),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn sinh(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.sinh())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn cosh(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.cosh())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn tanh(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.tanh())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn log2(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Cannot take log2 of non-positive number: {}",
n
)));
}
Ok(Value::Number(n.log2()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn exp2(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.exp2())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn expm1(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => Ok(Value::Number(n.exp_m1())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn log1p(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n <= -1.0 {
return Err(RuntimeError::InvalidOperation(format!(
"log1p domain error: argument must be > -1, got {}",
n
)));
}
Ok(Value::Number(n.ln_1p()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn factorial(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n < 0.0 || n.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Factorial requires non-negative integer, got {}",
n
)));
}
let n_int = *n as u32;
if n_int > 170 {
return Err(RuntimeError::InvalidOperation(format!(
"Factorial overflow: {} is too large",
n_int
)));
}
let mut result = 1.0;
for i in 2..=n_int {
result *= i as f64;
}
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn gamma(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
if *n <= 0.0 && n.fract() == 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Gamma function undefined for non-positive integers: {}",
n
)));
}
if n.fract() == 0.0 && *n > 0.0 {
return factorial(&[Value::Number(*n - 1.0)]);
}
let x = *n;
let result = (2.0 * consts::PI / x).sqrt() * (x / consts::E).powf(x);
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn erf(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(x) => {
let a1 = 0.254829592;
let a2 = -0.284496736;
let a3 = 1.421413741;
let a4 = -1.453152027;
let a5 = 1.061405429;
let p = 0.3275911;
let sign = if *x < 0.0 { -1.0 } else { 1.0 };
let x_abs = x.abs();
let t = 1.0 / (1.0 + p * x_abs);
let y =
1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x_abs * x_abs).exp();
Ok(Value::Number(sign * y))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn hypot(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Number(x), Value::Number(y)) => Ok(Value::Number(x.hypot(*y))),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn sign(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Number(n) => {
let result = if *n > 0.0 {
1.0
} else if *n < 0.0 {
-1.0
} else {
0.0
};
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn clamp(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::Number(x), Value::Number(min), Value::Number(max)) => {
if min > max {
return Err(RuntimeError::InvalidOperation(format!(
"Clamp: min ({}) must be <= max ({})",
min, max
)));
}
Ok(Value::Number(x.clamp(*min, *max)))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn mean(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(arr) => {
if arr.is_empty() {
return Err(RuntimeError::InvalidOperation(
"Cannot compute mean of empty array".to_string(),
));
}
let mut sum = 0.0;
for val in arr {
match val {
Value::Number(n) => sum += n,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
Ok(Value::Number(sum / arr.len() as f64))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn median(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(arr) => {
if arr.is_empty() {
return Err(RuntimeError::InvalidOperation(
"Cannot compute median of empty array".to_string(),
));
}
let mut numbers: Vec<f64> = Vec::new();
for val in arr {
match val {
Value::Number(n) => numbers.push(*n),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
numbers.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mid = numbers.len() / 2;
let result = if numbers.len().is_multiple_of(2) {
(numbers[mid - 1] + numbers[mid]) / 2.0
} else {
numbers[mid]
};
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn variance(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(arr) => {
if arr.len() < 2 {
return Err(RuntimeError::InvalidOperation(
"Variance requires at least 2 values".to_string(),
));
}
let mean_result = mean(args)?;
let mean_val = match mean_result {
Value::Number(n) => n,
_ => unreachable!(),
};
let mut sum_sq_diff = 0.0;
for val in arr {
match val {
Value::Number(n) => {
let diff = n - mean_val;
sum_sq_diff += diff * diff;
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
Ok(Value::Number(sum_sq_diff / (arr.len() - 1) as f64))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn std(args: &[Value]) -> Result<Value, RuntimeError> {
let var = variance(args)?;
match var {
Value::Number(v) => Ok(Value::Number(v.sqrt())),
_ => unreachable!(),
}
}
pub fn quantile(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(arr), Value::Number(q)) => {
if arr.is_empty() {
return Err(RuntimeError::InvalidOperation(
"Cannot compute quantile of empty array".to_string(),
));
}
if *q < 0.0 || *q > 1.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Quantile must be in [0, 1], got {}",
q
)));
}
let mut numbers: Vec<f64> = Vec::new();
for val in arr {
match val {
Value::Number(n) => numbers.push(*n),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
numbers.sort_by(|a, b| a.partial_cmp(b).unwrap());
let index = q * (numbers.len() - 1) as f64;
let lower = index.floor() as usize;
let upper = index.ceil() as usize;
let result = if lower == upper {
numbers[lower]
} else {
let weight = index - lower as f64;
numbers[lower] * (1.0 - weight) + numbers[upper] * weight
};
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn dot(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(a), Value::Array(b)) => {
if a.len() != b.len() {
return Err(RuntimeError::InvalidOperation(format!(
"Dot product requires equal length vectors: {} vs {}",
a.len(),
b.len()
)));
}
let mut result = 0.0;
for (val_a, val_b) in a.iter().zip(b.iter()) {
match (val_a, val_b) {
(Value::Number(na), Value::Number(nb)) => result += na * nb,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Arrays containing {:?} and {:?}", val_a, val_b),
});
}
}
}
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Array".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn norm(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(arr) => {
let mut sum_sq = 0.0;
for val in arr {
match val {
Value::Number(n) => sum_sq += n * n,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
Ok(Value::Number(sum_sq.sqrt()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn cross(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(a), Value::Array(b)) => {
if a.len() != 3 || b.len() != 3 {
return Err(RuntimeError::InvalidOperation(
"Cross product requires 3D vectors".to_string(),
));
}
let (a1, a2, a3) = match (&a[0], &a[1], &a[2]) {
(Value::Number(x), Value::Number(y), Value::Number(z)) => (*x, *y, *z),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: "Array containing non-numbers".to_string(),
});
}
};
let (b1, b2, b3) = match (&b[0], &b[1], &b[2]) {
(Value::Number(x), Value::Number(y), Value::Number(z)) => (*x, *y, *z),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: "Array containing non-numbers".to_string(),
});
}
};
Ok(Value::Array(vec![
Value::Number(a2 * b3 - a3 * b2),
Value::Number(a3 * b1 - a1 * b3),
Value::Number(a1 * b2 - a2 * b1),
]))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Array".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn distance(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(a), Value::Array(b)) => {
if a.len() != b.len() {
return Err(RuntimeError::InvalidOperation(format!(
"Distance requires equal length vectors: {} vs {}",
a.len(),
b.len()
)));
}
let mut sum_sq = 0.0;
for (val_a, val_b) in a.iter().zip(b.iter()) {
match (val_a, val_b) {
(Value::Number(na), Value::Number(nb)) => {
let diff = na - nb;
sum_sq += diff * diff;
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Arrays containing {:?} and {:?}", val_a, val_b),
});
}
}
}
Ok(Value::Number(sum_sq.sqrt()))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Array".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn normalize(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
let norm_result = norm(args)?;
let norm_val = match norm_result {
Value::Number(n) => n,
_ => unreachable!(),
};
if norm_val == 0.0 {
return Err(RuntimeError::InvalidOperation(
"Cannot normalize zero vector".to_string(),
));
}
match &args[0] {
Value::Array(arr) => {
let normalized: Vec<Value> = arr
.iter()
.map(|v| {
match v {
Value::Number(n) => Value::Number(n / norm_val),
_ => unreachable!(), }
})
.collect();
Ok(Value::Array(normalized))
}
_ => unreachable!(), }
}
pub fn matmul(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(a), Value::Array(b)) => {
let rows_a = a.len();
if rows_a == 0 {
return Err(RuntimeError::InvalidOperation(
"Matrix A is empty".to_string(),
));
}
let cols_a = match &a[0] {
Value::Array(row) => row.len(),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array (Array of Arrays)".to_string(),
got: format!("Array containing {:?}", a[0]),
});
}
};
let rows_b = b.len();
if rows_b == 0 {
return Err(RuntimeError::InvalidOperation(
"Matrix B is empty".to_string(),
));
}
let cols_b = match &b[0] {
Value::Array(row) => row.len(),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array (Array of Arrays)".to_string(),
got: format!("Array containing {:?}", b[0]),
});
}
};
if cols_a != rows_b {
return Err(RuntimeError::InvalidOperation(format!(
"Matrix dimensions incompatible: ({}, {}) × ({}, {})",
rows_a, cols_a, rows_b, cols_b
)));
}
let mut result = Vec::new();
#[allow(clippy::needless_range_loop)]
for i in 0..rows_a {
let row_a = match &a[i] {
Value::Array(r) => r,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array".to_string(),
got: "Non-uniform array structure".to_string(),
});
}
};
let mut result_row = Vec::new();
for j in 0..cols_b {
let mut sum = 0.0;
for k in 0..cols_a {
let a_val = match &row_a[k] {
Value::Number(n) => *n,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", row_a[k]),
});
}
};
let row_b = match &b[k] {
Value::Array(r) => r,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array".to_string(),
got: "Non-uniform array structure".to_string(),
});
}
};
let b_val = match &row_b[j] {
Value::Number(n) => *n,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", row_b[j]),
});
}
};
sum += a_val * b_val;
}
result_row.push(Value::Number(sum));
}
result.push(Value::Array(result_row));
}
Ok(Value::Array(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Array".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn transpose(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(matrix) => {
if matrix.is_empty() {
return Ok(Value::Array(vec![]));
}
let rows = matrix.len();
let cols = match &matrix[0] {
Value::Array(row) => row.len(),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array".to_string(),
got: format!("Array containing {:?}", matrix[0]),
});
}
};
if cols == 0 {
return Ok(Value::Array(vec![]));
}
let mut result = vec![vec![Value::Null; rows]; cols];
for i in 0..rows {
let row = match &matrix[i] {
Value::Array(r) => r,
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array".to_string(),
got: "Non-uniform array structure".to_string(),
});
}
};
if row.len() != cols {
return Err(RuntimeError::InvalidOperation(
"All rows must have same length".to_string(),
));
}
for j in 0..cols {
result[j][i] = row[j].clone();
}
}
let result_arrays: Vec<Value> = result.into_iter().map(Value::Array).collect();
Ok(Value::Array(result_arrays))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn determinant(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(matrix) => {
let n = matrix.len();
if n == 0 {
return Err(RuntimeError::InvalidOperation(
"Matrix is empty".to_string(),
));
}
for row in matrix {
match row {
Value::Array(r) => {
if r.len() != n {
return Err(RuntimeError::InvalidOperation(
"Determinant requires square matrix".to_string(),
));
}
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "2D Array".to_string(),
got: format!("Array containing {:?}", row),
});
}
}
}
match n {
1 => {
match &matrix[0] {
Value::Array(row) => match &row[0] {
Value::Number(n) => Ok(Value::Number(*n)),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", row[0]),
}),
},
_ => unreachable!(),
}
}
2 => {
let a = get_matrix_element(matrix, 0, 0)?;
let b = get_matrix_element(matrix, 0, 1)?;
let c = get_matrix_element(matrix, 1, 0)?;
let d = get_matrix_element(matrix, 1, 1)?;
Ok(Value::Number(a * d - b * c))
}
3 => {
let a = get_matrix_element(matrix, 0, 0)?;
let b = get_matrix_element(matrix, 0, 1)?;
let c = get_matrix_element(matrix, 0, 2)?;
let d = get_matrix_element(matrix, 1, 0)?;
let e = get_matrix_element(matrix, 1, 1)?;
let f = get_matrix_element(matrix, 1, 2)?;
let g = get_matrix_element(matrix, 2, 0)?;
let h = get_matrix_element(matrix, 2, 1)?;
let i = get_matrix_element(matrix, 2, 2)?;
let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
Ok(Value::Number(det))
}
_ => {
determinant_recursive(matrix)
}
}
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
fn determinant_recursive(matrix: &[Value]) -> Result<Value, RuntimeError> {
let n = matrix.len();
if n == 1 {
return match &matrix[0] {
Value::Array(row) => match &row[0] {
Value::Number(val) => Ok(Value::Number(*val)),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: "Non-numeric value in matrix".to_string(),
}),
},
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: "Invalid matrix structure".to_string(),
}),
};
}
let mut det = 0.0;
#[allow(clippy::needless_range_loop)]
for j in 0..n {
let element = get_matrix_element(matrix, 0, j)?;
let mut minor = Vec::new();
#[allow(clippy::needless_range_loop)]
for i in 1..n {
let mut row = Vec::new();
match &matrix[i] {
Value::Array(matrix_row) =>
{
#[allow(clippy::needless_range_loop)]
for k in 0..n {
if k != j {
row.push(matrix_row[k].clone());
}
}
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: "Invalid matrix row".to_string(),
});
}
}
minor.push(Value::Array(row));
}
let minor_det = determinant_recursive(&minor)?;
let minor_val = match minor_det {
Value::Number(v) => v,
_ => unreachable!(),
};
let sign = if j % 2 == 0 { 1.0 } else { -1.0 };
det += sign * element * minor_val;
}
Ok(Value::Number(det))
}
pub fn matrix_inverse(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::Array(matrix) => {
let n = matrix.len();
if n == 0 {
return Err(RuntimeError::InvalidOperation(
"Matrix is empty".to_string(),
));
}
let mut mat: Vec<Vec<f64>> = Vec::new();
for row_val in matrix {
match row_val {
Value::Array(row) => {
if row.len() != n {
return Err(RuntimeError::InvalidOperation(
"Matrix must be square".to_string(),
));
}
let mut num_row = Vec::new();
for val in row {
match val {
Value::Number(num) => num_row.push(*num),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", val),
});
}
}
}
mat.push(num_row);
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", row_val),
});
}
}
}
let mut aug = vec![vec![0.0; 2 * n]; n];
for i in 0..n {
for j in 0..n {
aug[i][j] = mat[i][j];
aug[i][n + j] = if i == j { 1.0 } else { 0.0 };
}
}
for i in 0..n {
let mut max_row = i;
for k in (i + 1)..n {
if aug[k][i].abs() > aug[max_row][i].abs() {
max_row = k;
}
}
aug.swap(i, max_row);
if aug[i][i].abs() < 1e-10 {
return Err(RuntimeError::InvalidOperation(
"Matrix is singular (not invertible)".to_string(),
));
}
let pivot = aug[i][i];
#[allow(clippy::needless_range_loop)]
for j in 0..(2 * n) {
aug[i][j] /= pivot;
}
#[allow(clippy::needless_range_loop)]
for k in 0..n {
if k != i {
let factor = aug[k][i];
#[allow(clippy::needless_range_loop)]
for j in 0..(2 * n) {
aug[k][j] -= factor * aug[i][j];
}
}
}
}
let mut result = Vec::new();
#[allow(clippy::needless_range_loop)]
for i in 0..n {
let mut row = Vec::new();
#[allow(clippy::needless_range_loop)]
for j in n..(2 * n) {
row.push(Value::Number(aug[i][j]));
}
result.push(Value::Array(row));
}
Ok(Value::Array(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
fn get_matrix_element(matrix: &[Value], i: usize, j: usize) -> Result<f64, RuntimeError> {
match &matrix[i] {
Value::Array(row) => match &row[j] {
Value::Number(n) => Ok(*n),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", row[j]),
}),
},
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array".to_string(),
got: format!("{:?}", matrix[i]),
}),
}
}
pub fn pi(_args: &[Value]) -> Result<Value, RuntimeError> {
Ok(Value::Number(consts::PI))
}
pub fn e(_args: &[Value]) -> Result<Value, RuntimeError> {
Ok(Value::Number(consts::E))
}
pub fn tau(_args: &[Value]) -> Result<Value, RuntimeError> {
Ok(Value::Number(consts::TAU))
}
pub fn phi(_args: &[Value]) -> Result<Value, RuntimeError> {
Ok(Value::Number((1.0 + 5.0_f64.sqrt()) / 2.0))
}
pub fn linear_regression(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(x_arr), Value::Array(y_arr)) => {
if x_arr.len() != y_arr.len() {
return Err(RuntimeError::InvalidOperation(format!(
"X and Y arrays must have same length: {} vs {}",
x_arr.len(),
y_arr.len()
)));
}
if x_arr.len() < 2 {
return Err(RuntimeError::InvalidOperation(
"Linear regression requires at least 2 data points".to_string(),
));
}
let mut x_vals = Vec::new();
let mut y_vals = Vec::new();
for val in x_arr {
match val {
Value::Number(n) => x_vals.push(*n),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
for val in y_arr {
match val {
Value::Number(n) => y_vals.push(*n),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
let n = x_vals.len() as f64;
let x_mean = x_vals.iter().sum::<f64>() / n;
let y_mean = y_vals.iter().sum::<f64>() / n;
let mut numerator = 0.0;
let mut denominator = 0.0;
for i in 0..x_vals.len() {
let x_diff = x_vals[i] - x_mean;
let y_diff = y_vals[i] - y_mean;
numerator += x_diff * y_diff;
denominator += x_diff * x_diff;
}
if denominator == 0.0 {
return Err(RuntimeError::InvalidOperation(
"Cannot compute regression: X values have no variance".to_string(),
));
}
let slope = numerator / denominator;
let intercept = y_mean - slope * x_mean;
let mut ss_res = 0.0; let mut ss_tot = 0.0;
for i in 0..x_vals.len() {
let y_pred = slope * x_vals[i] + intercept;
let residual = y_vals[i] - y_pred;
ss_res += residual * residual;
let total_diff = y_vals[i] - y_mean;
ss_tot += total_diff * total_diff;
}
let r_squared = if ss_tot == 0.0 {
1.0 } else {
1.0 - (ss_res / ss_tot)
};
Ok(Value::Array(vec![
Value::Number(slope),
Value::Number(intercept),
Value::Number(r_squared),
]))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Array".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn normal_pdf(args: &[Value]) -> Result<Value, RuntimeError> {
let (x, mean, std) = match args.len() {
1 => match &args[0] {
Value::Number(x) => (*x, 0.0, 1.0),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
});
}
},
3 => match (&args[0], &args[1], &args[2]) {
(Value::Number(x), Value::Number(m), Value::Number(s)) => {
if *s <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Standard deviation must be positive, got {}",
s
)));
}
(*x, *m, *s)
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
});
}
},
n => {
return Err(RuntimeError::WrongArity {
expected: 1,
got: n,
});
}
};
let z = (x - mean) / std;
let coefficient = 1.0 / (std * (2.0 * consts::PI).sqrt());
let exponent = -0.5 * z * z;
let pdf = coefficient * exponent.exp();
Ok(Value::Number(pdf))
}
pub fn normal_cdf(args: &[Value]) -> Result<Value, RuntimeError> {
let (x, mean, std) = match args.len() {
1 => match &args[0] {
Value::Number(x) => (*x, 0.0, 1.0),
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", args[0]),
});
}
},
3 => match (&args[0], &args[1], &args[2]) {
(Value::Number(x), Value::Number(m), Value::Number(s)) => {
if *s <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Standard deviation must be positive, got {}",
s
)));
}
(*x, *m, *s)
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
});
}
},
n => {
return Err(RuntimeError::WrongArity {
expected: 1,
got: n,
});
}
};
let z = (x - mean) / (std * 2.0_f64.sqrt());
let erf_result = erf(&[Value::Number(z)])?;
match erf_result {
Value::Number(erf_val) => {
let cdf = 0.5 * (1.0 + erf_val);
Ok(Value::Number(cdf))
}
_ => unreachable!(),
}
}
pub fn poisson_pmf(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Number(k), Value::Number(lambda)) => {
if *k < 0.0 || k.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"k must be a non-negative integer, got {}",
k
)));
}
if *lambda <= 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Lambda must be positive, got {}",
lambda
)));
}
let fact_result = factorial(&[Value::Number(*k)])?;
let k_factorial = match fact_result {
Value::Number(f) => f,
_ => unreachable!(),
};
let numerator = lambda.powf(*k) * (-lambda).exp();
let pmf = numerator / k_factorial;
Ok(Value::Number(pmf))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn round_to(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Number(x), Value::Number(digits)) => {
if *digits < 0.0 || digits.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Digits must be a non-negative integer, got {}",
digits
)));
}
let multiplier = 10_f64.powi(*digits as i32);
let result = (x * multiplier).round() / multiplier;
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn add_with_precision(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::Number(a), Value::Number(b), Value::Number(precision)) => {
if *precision < 0.0 || precision.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Precision must be a non-negative integer, got {}",
precision
)));
}
let multiplier = 10_f64.powi(*precision as i32);
let a_rounded = (a * multiplier).round() / multiplier;
let b_rounded = (b * multiplier).round() / multiplier;
let result = ((a_rounded + b_rounded) * multiplier).round() / multiplier;
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn sub_with_precision(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::Number(a), Value::Number(b), Value::Number(precision)) => {
if *precision < 0.0 || precision.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Precision must be a non-negative integer, got {}",
precision
)));
}
let multiplier = 10_f64.powi(*precision as i32);
let a_rounded = (a * multiplier).round() / multiplier;
let b_rounded = (b * multiplier).round() / multiplier;
let result = ((a_rounded - b_rounded) * multiplier).round() / multiplier;
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn mul_with_precision(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::Number(a), Value::Number(b), Value::Number(precision)) => {
if *precision < 0.0 || precision.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Precision must be a non-negative integer, got {}",
precision
)));
}
let multiplier = 10_f64.powi(*precision as i32);
let a_rounded = (a * multiplier).round() / multiplier;
let b_rounded = (b * multiplier).round() / multiplier;
let result = ((a_rounded * b_rounded) * multiplier).round() / multiplier;
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn div_with_precision(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::Number(a), Value::Number(b), Value::Number(precision)) => {
if *precision < 0.0 || precision.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Precision must be a non-negative integer, got {}",
precision
)));
}
if *b == 0.0 {
return Err(RuntimeError::InvalidOperation(
"Division by zero".to_string(),
));
}
let multiplier = 10_f64.powi(*precision as i32);
let a_rounded = (a * multiplier).round() / multiplier;
let b_rounded = (b * multiplier).round() / multiplier;
let result = ((a_rounded / b_rounded) * multiplier).round() / multiplier;
Ok(Value::Number(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn set_precision(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::Array(arr), Value::Number(precision)) => {
if *precision < 0.0 || precision.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Precision must be a non-negative integer, got {}",
precision
)));
}
let multiplier = 10_f64.powi(*precision as i32);
let mut result = Vec::new();
for val in arr {
match val {
Value::Number(n) => {
let rounded = (n * multiplier).round() / multiplier;
result.push(Value::Number(rounded));
}
_ => {
return Err(RuntimeError::TypeErrorDetailed {
expected: "Array of Numbers".to_string(),
got: format!("Array containing {:?}", val),
});
}
}
}
Ok(Value::Array(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Array, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}