Skip to main content

robinpath_modules/modules/
math_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    rp.register_builtin("math.clamp", |args, _| {
5        let val = args.first().map(|v| v.to_number()).unwrap_or(0.0);
6        let min = args.get(1).map(|v| v.to_number()).unwrap_or(f64::NEG_INFINITY);
7        let max = args.get(2).map(|v| v.to_number()).unwrap_or(f64::INFINITY);
8        Ok(Value::Number(val.max(min).min(max)))
9    });
10
11    rp.register_builtin("math.round", |args, _| {
12        let val = args.first().map(|v| v.to_number()).unwrap_or(0.0);
13        let decimals = args.get(1).map(|v| v.to_number() as i32).unwrap_or(0);
14        let factor = 10f64.powi(decimals);
15        Ok(Value::Number((val * factor).round() / factor))
16    });
17
18    rp.register_builtin("math.randomInt", |args, _| {
19        let min = args.first().map(|v| v.to_number() as i64).unwrap_or(0);
20        let max = args.get(1).map(|v| v.to_number() as i64).unwrap_or(100);
21        let val = simple_random_range(min, max);
22        Ok(Value::Number(val as f64))
23    });
24
25    rp.register_builtin("math.randomFloat", |args, _| {
26        let min = args.first().map(|v| v.to_number()).unwrap_or(0.0);
27        let max = args.get(1).map(|v| v.to_number()).unwrap_or(1.0);
28        let r = simple_random_f64();
29        Ok(Value::Number(min + r * (max - min)))
30    });
31
32    rp.register_builtin("math.sum", |args, _| {
33        let arr = args.first().cloned().unwrap_or(Value::Null);
34        if let Value::Array(items) = &arr {
35            let sum: f64 = items.iter().map(|v| v.to_number()).sum();
36            Ok(Value::Number(sum))
37        } else {
38            let sum: f64 = args.iter().map(|v| v.to_number()).sum();
39            Ok(Value::Number(sum))
40        }
41    });
42
43    rp.register_builtin("math.avg", |args, _| {
44        let arr = args.first().cloned().unwrap_or(Value::Null);
45        if let Value::Array(items) = &arr {
46            if items.is_empty() {
47                return Ok(Value::Number(0.0));
48            }
49            let sum: f64 = items.iter().map(|v| v.to_number()).sum();
50            Ok(Value::Number(sum / items.len() as f64))
51        } else {
52            if args.is_empty() {
53                return Ok(Value::Number(0.0));
54            }
55            let sum: f64 = args.iter().map(|v| v.to_number()).sum();
56            Ok(Value::Number(sum / args.len() as f64))
57        }
58    });
59
60    rp.register_builtin("math.median", |args, _| {
61        let arr = args.first().cloned().unwrap_or(Value::Null);
62        let mut nums: Vec<f64> = if let Value::Array(items) = &arr {
63            items.iter().map(|v| v.to_number()).collect()
64        } else {
65            args.iter().map(|v| v.to_number()).collect()
66        };
67        if nums.is_empty() {
68            return Ok(Value::Number(0.0));
69        }
70        nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
71        let mid = nums.len() / 2;
72        if nums.len() % 2 == 0 {
73            Ok(Value::Number((nums[mid - 1] + nums[mid]) / 2.0))
74        } else {
75            Ok(Value::Number(nums[mid]))
76        }
77    });
78
79    rp.register_builtin("math.min", |args, _| {
80        let arr = args.first().cloned().unwrap_or(Value::Null);
81        let nums: Vec<f64> = if let Value::Array(items) = &arr {
82            items.iter().map(|v| v.to_number()).collect()
83        } else {
84            args.iter().map(|v| v.to_number()).collect()
85        };
86        let min = nums.iter().copied().fold(f64::INFINITY, f64::min);
87        Ok(Value::Number(min))
88    });
89
90    rp.register_builtin("math.max", |args, _| {
91        let arr = args.first().cloned().unwrap_or(Value::Null);
92        let nums: Vec<f64> = if let Value::Array(items) = &arr {
93            items.iter().map(|v| v.to_number()).collect()
94        } else {
95            args.iter().map(|v| v.to_number()).collect()
96        };
97        let max = nums.iter().copied().fold(f64::NEG_INFINITY, f64::max);
98        Ok(Value::Number(max))
99    });
100
101    rp.register_builtin("math.percentage", |args, _| {
102        let val = args.first().map(|v| v.to_number()).unwrap_or(0.0);
103        let total = args.get(1).map(|v| v.to_number()).unwrap_or(100.0);
104        if total == 0.0 {
105            return Ok(Value::Number(0.0));
106        }
107        Ok(Value::Number((val / total) * 100.0))
108    });
109
110    rp.register_builtin("math.factorial", |args, _| {
111        let n = args.first().map(|v| v.to_number() as u64).unwrap_or(0);
112        let mut result: f64 = 1.0;
113        for i in 2..=n {
114            result *= i as f64;
115        }
116        Ok(Value::Number(result))
117    });
118
119    rp.register_builtin("math.gcd", |args, _| {
120        let mut a = args.first().map(|v| v.to_number().abs() as u64).unwrap_or(0);
121        let mut b = args.get(1).map(|v| v.to_number().abs() as u64).unwrap_or(0);
122        while b != 0 {
123            let t = b;
124            b = a % b;
125            a = t;
126        }
127        Ok(Value::Number(a as f64))
128    });
129
130    rp.register_builtin("math.lcm", |args, _| {
131        let a = args.first().map(|v| v.to_number().abs() as u64).unwrap_or(0);
132        let b = args.get(1).map(|v| v.to_number().abs() as u64).unwrap_or(0);
133        if a == 0 || b == 0 {
134            return Ok(Value::Number(0.0));
135        }
136        let gcd = {
137            let (mut x, mut y) = (a, b);
138            while y != 0 {
139                let t = y;
140                y = x % y;
141                x = t;
142            }
143            x
144        };
145        Ok(Value::Number((a / gcd * b) as f64))
146    });
147
148    rp.register_builtin("math.isPrime", |args, _| {
149        let n = args.first().map(|v| v.to_number() as u64).unwrap_or(0);
150        if n < 2 {
151            return Ok(Value::Bool(false));
152        }
153        if n < 4 {
154            return Ok(Value::Bool(true));
155        }
156        if n % 2 == 0 || n % 3 == 0 {
157            return Ok(Value::Bool(false));
158        }
159        let mut i = 5u64;
160        while i * i <= n {
161            if n % i == 0 || n % (i + 2) == 0 {
162                return Ok(Value::Bool(false));
163            }
164            i += 6;
165        }
166        Ok(Value::Bool(true))
167    });
168
169    rp.register_builtin("math.lerp", |args, _| {
170        let start = args.first().map(|v| v.to_number()).unwrap_or(0.0);
171        let end = args.get(1).map(|v| v.to_number()).unwrap_or(1.0);
172        let t = args.get(2).map(|v| v.to_number()).unwrap_or(0.5);
173        Ok(Value::Number(start + (end - start) * t))
174    });
175}
176
177fn simple_random_range(min: i64, max: i64) -> i64 {
178    if min >= max {
179        return min;
180    }
181    let r = simple_random_f64();
182    min + (r * (max - min + 1) as f64) as i64
183}
184
185fn simple_random_f64() -> f64 {
186    use std::time::{SystemTime, UNIX_EPOCH};
187    let seed = SystemTime::now()
188        .duration_since(UNIX_EPOCH)
189        .unwrap_or_default()
190        .as_nanos();
191    let state = seed ^ (std::ptr::from_ref(&seed) as u128).wrapping_mul(6364136223846793005);
192    let mixed = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
193    ((mixed >> 33) as u64 as f64) / (u64::MAX as f64)
194}