ddb_core/functions/
math.rs1use super::Value;
3use crate::error::{DdbError, Result};
4
5pub 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
14pub fn ceil(value: &Value) -> Result<Value> {
16 Ok(Value::Integer(value.as_f64()?.ceil() as i64))
17}
18
19pub fn floor(value: &Value) -> Result<Value> {
21 Ok(Value::Integer(value.as_f64()?.floor() as i64))
22}
23
24pub 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
37pub 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
46pub 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
53pub fn exp(value: &Value) -> Result<Value> {
55 Ok(Value::Float(value.as_f64()?.exp()))
56}
57
58pub 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
67pub 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
76pub 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
96pub 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
102pub 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
115pub 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
128pub 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}