Skip to main content

atmos/os_lib/aura/builtins/
math.rs

1// 分割: aura/builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ6)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use 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
115// ----------------------------------------------------
116// 新規数学関数群
117// ----------------------------------------------------
118
119pub(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// ====================================================================
325// OS File System Commands
326// ====================================================================
327