atmos/os_lib/aura/builtins/
math.rs1use super::*;
4
5pub(crate) fn builtin_factorial(env: &mut Env, args: &[AST]) -> Result<Value, String> {
6 let vals = eval_args(env, args)?;
7 if vals.len() != 1 {
8 return Err(String::from("! requires exactly 1 argument"));
9 }
10 if let Value::Num(n) = &vals[0] {
11 if !n.sign {
12 return Err(String::from("! requires a non-negative integer"));
13 }
14 let num_i64 = n
15 .to_i64()
16 .ok_or(String::from("Number too large for factorial"))?;
17 let mut res = BigInt::one();
18 for i in 1..=num_i64 {
19 res = res.mul(&BigInt::from_i64(i));
20 }
21 Ok(Value::Num(res))
22 } else {
23 Err(String::from("! requires an integer argument"))
24 }
25}
26
27pub(crate) fn extract_i64(v: &Value) -> Result<i64, String> {
28 match v {
29 Value::Num(n) => n
30 .to_i64()
31 .ok_or(String::from("Number too large for bitwise operation")),
32 _ => Err(String::from("Bitwise operations require integer arguments")),
33 }
34}
35
36pub(crate) fn builtin_bit_and(env: &mut Env, args: &[AST]) -> Result<Value, String> {
37 let vals = eval_args(env, args)?;
38 if vals.len() < 2 {
39 return Err(String::from("bit-and requires at least 2 arguments"));
40 }
41 let mut res = extract_i64(&vals[0])?;
42 for val in vals.iter().skip(1) {
43 res &= extract_i64(val)?;
44 }
45 Ok(Value::Num(BigInt::from_i64(res)))
46}
47
48pub(crate) fn builtin_bit_or(env: &mut Env, args: &[AST]) -> Result<Value, String> {
49 let vals = eval_args(env, args)?;
50 if vals.len() < 2 {
51 return Err(String::from("bit-or requires at least 2 arguments"));
52 }
53 let mut res = extract_i64(&vals[0])?;
54 for val in vals.iter().skip(1) {
55 res |= extract_i64(val)?;
56 }
57 Ok(Value::Num(BigInt::from_i64(res)))
58}
59
60pub(crate) fn builtin_bit_xor(env: &mut Env, args: &[AST]) -> Result<Value, String> {
61 let vals = eval_args(env, args)?;
62 if vals.len() < 2 {
63 return Err(String::from("bit-xor requires at least 2 arguments"));
64 }
65 let mut res = extract_i64(&vals[0])?;
66 for val in vals.iter().skip(1) {
67 res ^= extract_i64(val)?;
68 }
69 Ok(Value::Num(BigInt::from_i64(res)))
70}
71
72pub(crate) fn builtin_bit_not(env: &mut Env, args: &[AST]) -> Result<Value, String> {
73 let vals = eval_args(env, args)?;
74 if vals.len() != 1 {
75 return Err(String::from("bit-not requires exactly 1 argument"));
76 }
77 let res = !extract_i64(&vals[0])?;
78 Ok(Value::Num(BigInt::from_i64(res)))
79}
80
81pub(crate) fn builtin_l_shift(env: &mut Env, args: &[AST]) -> Result<Value, String> {
82 let vals = eval_args(env, args)?;
83 if vals.len() != 2 {
84 return Err(String::from("l-shift requires exactly 2 arguments"));
85 }
86 let a = extract_i64(&vals[0])?;
87 let b = extract_i64(&vals[1])?;
88 let res = a << b;
89 Ok(Value::Num(BigInt::from_i64(res)))
90}
91
92pub(crate) fn builtin_r_shift(env: &mut Env, args: &[AST]) -> Result<Value, String> {
93 let vals = eval_args(env, args)?;
94 if vals.len() != 2 {
95 return Err(String::from("r-shift requires exactly 2 arguments"));
96 }
97 let a = extract_i64(&vals[0])?;
98 let b = extract_i64(&vals[1])?;
99 let res = a >> b;
100 Ok(Value::Num(BigInt::from_i64(res)))
101}
102
103pub(crate) fn builtin_pi(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
104 let num = BigInt::from_i64(3141592653589793);
105 let den = BigInt::from_i64(1000000000000000);
106 Ok(Value::Ratio(Ratio::new_or_zero(num, den)))
107}
108
109pub(crate) fn builtin_euler(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
110 let num = BigInt::from_i64(2718281828459045);
111 let den = BigInt::from_i64(1000000000000000);
112 Ok(Value::Ratio(Ratio::new_or_zero(num, den)))
113}
114
115pub(crate) fn to_f64(val: &Value) -> Result<f64, String> {
120 match val {
121 Value::Num(n) => Ok(n.to_i64().unwrap_or(0) as f64),
122 Value::Ratio(r) => Ok(r.to_f64()),
123 _ => Err(String::from("Argument must be a number or ratio")),
124 }
125}
126
127pub(crate) fn from_f64(f: f64) -> Result<Value, String> {
128 if let Some(r) = Ratio::from_f64(f) {
129 if r.den == BigInt::one() {
130 Ok(Value::Num(r.num))
131 } else {
132 Ok(Value::Ratio(r))
133 }
134 } else {
135 Err(String::from("Invalid float value"))
136 }
137}
138
139pub(crate) fn builtin_gcd(env: &mut Env, args: &[AST]) -> Result<Value, String> {
140 let vals = eval_args(env, args)?;
141 if vals.len() != 2 {
142 return Err(String::from(
143 "greatest-com-div requires exactly 2 arguments",
144 ));
145 }
146 let a = match &vals[0] {
147 Value::Num(n) => n,
148 _ => return Err(String::from("greatest-com-div args must be integers")),
149 };
150 let b = match &vals[1] {
151 Value::Num(n) => n,
152 _ => return Err(String::from("greatest-com-div args must be integers")),
153 };
154 Ok(Value::Num(a.gcd(b)))
155}
156
157pub(crate) fn builtin_lcm(env: &mut Env, args: &[AST]) -> Result<Value, String> {
158 let vals = eval_args(env, args)?;
159 if vals.len() != 2 {
160 return Err(String::from("least-com-mul requires exactly 2 arguments"));
161 }
162 let a = match &vals[0] {
163 Value::Num(n) => n,
164 _ => return Err(String::from("least-com-mul args must be integers")),
165 };
166 let b = match &vals[1] {
167 Value::Num(n) => n,
168 _ => return Err(String::from("least-com-mul args must be integers")),
169 };
170 Ok(Value::Num(a.lcm(b)))
171}
172
173pub(crate) fn builtin_abs(env: &mut Env, args: &[AST]) -> Result<Value, String> {
174 let vals = eval_args(env, args)?;
175 if vals.len() != 1 {
176 return Err(String::from("abs requires exactly 1 argument"));
177 }
178 match &vals[0] {
179 Value::Num(n) => {
180 let mut res = n.clone();
181 res.sign = true;
182 Ok(Value::Num(res))
183 }
184 Value::Ratio(r) => {
185 let mut res = r.clone();
186 res.num.sign = true;
187 Ok(Value::Ratio(res))
188 }
189 _ => Err(String::from("abs requires a number")),
190 }
191}
192
193pub(crate) fn builtin_sign(env: &mut Env, args: &[AST]) -> Result<Value, String> {
194 let vals = eval_args(env, args)?;
195 if vals.len() != 1 {
196 return Err(String::from("sign requires exactly 1 argument"));
197 }
198 let sign = match &vals[0] {
199 Value::Num(n) => {
200 if n.is_zero() {
201 0
202 } else if n.sign {
203 1
204 } else {
205 -1
206 }
207 }
208 Value::Ratio(r) => {
209 if r.num.is_zero() {
210 0
211 } else if r.num.sign {
212 1
213 } else {
214 -1
215 }
216 }
217 _ => return Err(String::from("sign requires a number")),
218 };
219 Ok(Value::Num(BigInt::from_i64(sign)))
220}
221
222pub(crate) fn builtin_real_part(env: &mut Env, args: &[AST]) -> Result<Value, String> {
223 let vals = eval_args(env, args)?;
224 if vals.len() != 1 {
225 return Err(String::from("real-part requires exactly 1 argument"));
226 }
227 match &vals[0] {
228 Value::Complex(c) => {
229 if c.real.den == BigInt::one() {
230 Ok(Value::Num(c.real.num.clone()))
231 } else {
232 Ok(Value::Ratio(c.real.clone()))
233 }
234 }
235 v => Ok(v.clone()),
236 }
237}
238
239pub(crate) fn builtin_imag_part(env: &mut Env, args: &[AST]) -> Result<Value, String> {
240 let vals = eval_args(env, args)?;
241 if vals.len() != 1 {
242 return Err(String::from("imag-part requires exactly 1 argument"));
243 }
244 match &vals[0] {
245 Value::Complex(c) => {
246 if c.imag.den == BigInt::one() {
247 Ok(Value::Num(c.imag.num.clone()))
248 } else {
249 Ok(Value::Ratio(c.imag.clone()))
250 }
251 }
252 _ => Ok(Value::Num(BigInt::zero())),
253 }
254}
255
256pub(crate) fn builtin_numerator(env: &mut Env, args: &[AST]) -> Result<Value, String> {
257 let vals = eval_args(env, args)?;
258 if vals.len() != 1 {
259 return Err(String::from("numerator requires exactly 1 argument"));
260 }
261 match &vals[0] {
262 Value::Ratio(r) => Ok(Value::Num(r.num.clone())),
263 Value::Num(n) => Ok(Value::Num(n.clone())),
264 _ => Err(String::from("numerator requires a number or ratio")),
265 }
266}
267
268pub(crate) fn builtin_denominator(env: &mut Env, args: &[AST]) -> Result<Value, String> {
269 let vals = eval_args(env, args)?;
270 if vals.len() != 1 {
271 return Err(String::from("denominator requires exactly 1 argument"));
272 }
273 match &vals[0] {
274 Value::Ratio(r) => Ok(Value::Num(r.den.clone())),
275 Value::Num(_) => Ok(Value::Num(BigInt::one())),
276 _ => Err(String::from("denominator requires a number or ratio")),
277 }
278}
279
280macro_rules! math_fn {
281 ($name:ident, $func:path, $name_str:expr) => {
282 pub(crate) fn $name(env: &mut Env, args: &[AST]) -> Result<Value, String> {
283 let vals = eval_args(env, args)?;
284 if vals.len() != 1 {
285 return Err(alloc::format!("{} requires exactly 1 argument", $name_str));
286 }
287 let f = to_f64(&vals[0])?;
288 from_f64($func(f))
289 }
290 };
291}
292
293math_fn!(builtin_exp, libm::exp, "exp");
294math_fn!(builtin_log2, libm::log2, "log2");
295math_fn!(builtin_log10, libm::log10, "log10");
296math_fn!(builtin_sqrt, libm::sqrt, "sqrt");
297math_fn!(builtin_sin, libm::sin, "sin");
298math_fn!(builtin_cos, libm::cos, "cos");
299math_fn!(builtin_tan, libm::tan, "tan");
300math_fn!(builtin_asin, libm::asin, "arc-sin");
301math_fn!(builtin_acos, libm::acos, "arc-cos");
302math_fn!(builtin_atan, libm::atan, "arc-tan");
303math_fn!(builtin_sinh, libm::sinh, "hyp-sin");
304math_fn!(builtin_cosh, libm::cosh, "hyp-cos");
305math_fn!(builtin_tanh, libm::tanh, "hyp-tan");
306math_fn!(builtin_asinh, libm::asinh, "hyp-arc-sin");
307math_fn!(builtin_acosh, libm::acosh, "hyp-arc-cos");
308math_fn!(builtin_atanh, libm::atanh, "hyp-arc-tan");
309
310pub(crate) static mut RAND_SEED: u32 = 123456789;
311
312pub(crate) fn builtin_random(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
313 unsafe {
314 let mut x = RAND_SEED;
315 x ^= x << 13;
316 x ^= x >> 17;
317 x ^= x << 5;
318 RAND_SEED = x;
319 let f = (x as f64) / (u32::MAX as f64);
320 from_f64(f)
321 }
322}
323
324