robinpath_modules/modules/
math_mod.rs1use 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}