ddb_core/functions/
math.rs

1// Math functions
2use super::Value;
3use crate::error::{DdbError, Result};
4
5/// ABS(x) - Absolute value
6pub fn abs(value: &Value) -> Result<Value> {
7    match value {
8        Value::Integer(i) => Ok(Value::Integer(i.abs())),
9        Value::Float(f) => Ok(Value::Float(f.abs())),
10        _ => Ok(Value::Float(value.as_f64()?.abs())),
11    }
12}
13
14/// CEIL(x) / CEILING(x) - Round up to nearest integer
15pub fn ceil(value: &Value) -> Result<Value> {
16    Ok(Value::Integer(value.as_f64()?.ceil() as i64))
17}
18
19/// FLOOR(x) - Round down to nearest integer
20pub fn floor(value: &Value) -> Result<Value> {
21    Ok(Value::Integer(value.as_f64()?.floor() as i64))
22}
23
24/// ROUND(x, [decimals]) - Round to specified decimal places
25pub fn round(value: &Value, decimals: Option<&Value>) -> Result<Value> {
26    let num = value.as_f64()?;
27    let places = decimals.map(|v| v.as_i64()).transpose()?.unwrap_or(0);
28
29    if places == 0 {
30        Ok(Value::Integer(num.round() as i64))
31    } else {
32        let multiplier = 10_f64.powi(places as i32);
33        Ok(Value::Float((num * multiplier).round() / multiplier))
34    }
35}
36
37/// SQRT(x) - Square root
38pub fn sqrt(value: &Value) -> Result<Value> {
39    let num = value.as_f64()?;
40    if num < 0.0 {
41        return Err(DdbError::FunctionError("Cannot take square root of negative number".to_string()));
42    }
43    Ok(Value::Float(num.sqrt()))
44}
45
46/// POW(x, y) / POWER(x, y) - x raised to power y
47pub fn pow(base: &Value, exponent: &Value) -> Result<Value> {
48    let b = base.as_f64()?;
49    let e = exponent.as_f64()?;
50    Ok(Value::Float(b.powf(e)))
51}
52
53/// EXP(x) - e^x
54pub fn exp(value: &Value) -> Result<Value> {
55    Ok(Value::Float(value.as_f64()?.exp()))
56}
57
58/// LN(x) / LOG(x) - Natural logarithm
59pub fn ln(value: &Value) -> Result<Value> {
60    let num = value.as_f64()?;
61    if num <= 0.0 {
62        return Err(DdbError::FunctionError("Logarithm of non-positive number".to_string()));
63    }
64    Ok(Value::Float(num.ln()))
65}
66
67/// LOG10(x) - Base-10 logarithm
68pub fn log10(value: &Value) -> Result<Value> {
69    let num = value.as_f64()?;
70    if num <= 0.0 {
71        return Err(DdbError::FunctionError("Logarithm of non-positive number".to_string()));
72    }
73    Ok(Value::Float(num.log10()))
74}
75
76/// MOD(x, y) - Modulo operation
77pub fn modulo(dividend: &Value, divisor: &Value) -> Result<Value> {
78    match (dividend, divisor) {
79        (Value::Integer(a), Value::Integer(b)) => {
80            if *b == 0 {
81                return Err(DdbError::FunctionError("Division by zero".to_string()));
82            }
83            Ok(Value::Integer(a % b))
84        }
85        _ => {
86            let a = dividend.as_f64()?;
87            let b = divisor.as_f64()?;
88            if b == 0.0 {
89                return Err(DdbError::FunctionError("Division by zero".to_string()));
90            }
91            Ok(Value::Float(a % b))
92        }
93    }
94}
95
96/// SIGN(x) - Returns -1, 0, or 1 depending on sign
97pub fn sign(value: &Value) -> Result<Value> {
98    let num = value.as_f64()?;
99    Ok(Value::Integer(if num > 0.0 { 1 } else if num < 0.0 { -1 } else { 0 }))
100}
101
102/// TRUNC(x, [decimals]) - Truncate to decimal places
103pub fn trunc(value: &Value, decimals: Option<&Value>) -> Result<Value> {
104    let num = value.as_f64()?;
105    let places = decimals.map(|v| v.as_i64()).transpose()?.unwrap_or(0);
106
107    if places == 0 {
108        Ok(Value::Integer(num.trunc() as i64))
109    } else {
110        let multiplier = 10_f64.powi(places as i32);
111        Ok(Value::Float((num * multiplier).trunc() / multiplier))
112    }
113}
114
115/// RAND() / RANDOM() - Random number between 0 and 1
116pub fn rand() -> Value {
117    use std::hash::{Hash, Hasher};
118    use std::collections::hash_map::DefaultHasher;
119    use std::time::SystemTime;
120
121    let mut hasher = DefaultHasher::new();
122    SystemTime::now().hash(&mut hasher);
123    let hash = hasher.finish();
124
125    Value::Float((hash % 1000000) as f64 / 1000000.0)
126}
127
128/// PI() - Returns pi constant
129pub fn pi() -> Value {
130    Value::Float(std::f64::consts::PI)
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_abs() {
139        assert_eq!(abs(&Value::Integer(-5)).unwrap(), Value::Integer(5));
140        assert_eq!(abs(&Value::Float(-3.14)).unwrap(), Value::Float(3.14));
141    }
142
143    #[test]
144    fn test_ceil_floor() {
145        assert_eq!(ceil(&Value::Float(3.2)).unwrap(), Value::Integer(4));
146        assert_eq!(floor(&Value::Float(3.8)).unwrap(), Value::Integer(3));
147    }
148
149    #[test]
150    fn test_round() {
151        assert_eq!(round(&Value::Float(3.456), None).unwrap(), Value::Integer(3));
152        assert_eq!(round(&Value::Float(3.456), Some(&Value::Integer(2))).unwrap(), Value::Float(3.46));
153    }
154
155    #[test]
156    fn test_sqrt() {
157        assert_eq!(sqrt(&Value::Integer(16)).unwrap(), Value::Float(4.0));
158        assert!(sqrt(&Value::Integer(-1)).is_err());
159    }
160
161    #[test]
162    fn test_pow() {
163        assert_eq!(pow(&Value::Integer(2), &Value::Integer(3)).unwrap(), Value::Float(8.0));
164    }
165}