aether/builtins/
precise.rs

1// src/builtins/precise.rs
2use crate::evaluator::RuntimeError;
3use crate::value::Value;
4use num_bigint::BigInt;
5use num_rational::Ratio;
6use num_traits::{One, ToPrimitive, Zero};
7
8/// 将数字转换为分数
9///
10/// 参数:
11/// - args[0]: 数字或分数值
12///
13/// 返回:
14/// - 转换后的分数值
15pub fn to_fraction(args: &[Value]) -> Result<Value, RuntimeError> {
16    if args.len() != 1 {
17        return Err(RuntimeError::WrongArity {
18            expected: 1,
19            got: args.len(),
20        });
21    }
22    match &args[0] {
23        Value::Number(n) => {
24            let s = format!("{}", n);
25            if let Some(dot_pos) = s.find('.') {
26                let decimal_places = s.len() - dot_pos - 1;
27                let denominator = 10_i64.pow(decimal_places as u32);
28                let numerator = (n * denominator as f64).round() as i64;
29                let frac = Ratio::new(BigInt::from(numerator), BigInt::from(denominator));
30                Ok(Value::Fraction(frac))
31            } else {
32                let frac = Ratio::new(BigInt::from(*n as i64), BigInt::one());
33                Ok(Value::Fraction(frac))
34            }
35        }
36        Value::Fraction(f) => Ok(Value::Fraction(f.clone())),
37        _ => Err(RuntimeError::TypeErrorDetailed {
38            expected: "Number or Fraction".to_string(),
39            got: format!("{:?}", args[0]),
40        }),
41    }
42}
43
44/// 将分数转换为浮点数
45///
46/// 参数:
47/// - args[0]: 分数或数字值
48///
49/// 返回:
50/// - 转换后的浮点数值
51pub fn to_float(args: &[Value]) -> Result<Value, RuntimeError> {
52    if args.len() != 1 {
53        return Err(RuntimeError::WrongArity {
54            expected: 1,
55            got: args.len(),
56        });
57    }
58    match &args[0] {
59        Value::Fraction(f) => {
60            let num = f.numer().to_f64().ok_or_else(|| {
61                RuntimeError::InvalidOperation("Failed to convert numerator".to_string())
62            })?;
63            let den = f.denom().to_f64().ok_or_else(|| {
64                RuntimeError::InvalidOperation("Failed to convert denominator".to_string())
65            })?;
66            Ok(Value::Number(num / den))
67        }
68        Value::Number(n) => Ok(Value::Number(*n)),
69        _ => Err(RuntimeError::TypeErrorDetailed {
70            expected: "Fraction or Number".to_string(),
71            got: format!("{:?}", args[0]),
72        }),
73    }
74}
75
76/// 化简分数(约分)
77///
78/// 参数:
79/// - args[0]: 要化简的分数
80///
81/// 返回:
82/// - 化简后的最简分数
83pub fn simplify(args: &[Value]) -> Result<Value, RuntimeError> {
84    if args.len() != 1 {
85        return Err(RuntimeError::WrongArity {
86            expected: 1,
87            got: args.len(),
88        });
89    }
90    match &args[0] {
91        Value::Fraction(f) => Ok(Value::Fraction(f.reduced())),
92        _ => Err(RuntimeError::TypeErrorDetailed {
93            expected: "Fraction".to_string(),
94            got: format!("{:?}", args[0]),
95        }),
96    }
97}
98
99/// 将 Value 类型转换为 Ratio<BigInt> 分数类型
100///
101/// 参数:
102/// - value: 待转换的值(数字或分数)
103///
104/// 返回:
105/// - Ratio<BigInt> 类型的分数
106fn value_to_fraction(value: &Value) -> Result<Ratio<BigInt>, RuntimeError> {
107    match value {
108        Value::Fraction(f) => Ok(f.clone()),
109        Value::Number(n) => {
110            let s = format!("{}", n);
111            if let Some(dot_pos) = s.find('.') {
112                let decimal_places = s.len() - dot_pos - 1;
113                let denominator = 10_i64.pow(decimal_places as u32);
114                let numerator = (n * denominator as f64).round() as i64;
115                Ok(Ratio::new(
116                    BigInt::from(numerator),
117                    BigInt::from(denominator),
118                ))
119            } else {
120                Ok(Ratio::new(BigInt::from(*n as i64), BigInt::one()))
121            }
122        }
123        _ => Err(RuntimeError::TypeErrorDetailed {
124            expected: "Fraction or Number".to_string(),
125            got: format!("{:?}", value),
126        }),
127    }
128}
129
130/// 分数加法运算
131///
132/// 参数:
133/// - args[0]: 第一个加数(数字或分数)
134/// - args[1]: 第二个加数(数字或分数)
135///
136/// 返回:
137/// - 两个分数相加的结果
138pub fn frac_add(args: &[Value]) -> Result<Value, RuntimeError> {
139    if args.len() != 2 {
140        return Err(RuntimeError::WrongArity {
141            expected: 2,
142            got: args.len(),
143        });
144    }
145    let frac1 = value_to_fraction(&args[0])?;
146    let frac2 = value_to_fraction(&args[1])?;
147    Ok(Value::Fraction(frac1 + frac2))
148}
149
150/// 分数减法运算
151///
152/// 参数:
153/// - args[0]: 被减数(数字或分数)
154/// - args[1]: 减数(数字或分数)
155///
156/// 返回:
157/// - 两个分数相减的结果
158pub fn frac_sub(args: &[Value]) -> Result<Value, RuntimeError> {
159    if args.len() != 2 {
160        return Err(RuntimeError::WrongArity {
161            expected: 2,
162            got: args.len(),
163        });
164    }
165    let frac1 = value_to_fraction(&args[0])?;
166    let frac2 = value_to_fraction(&args[1])?;
167    Ok(Value::Fraction(frac1 - frac2))
168}
169
170/// 分数乘法运算
171///
172/// 参数:
173/// - args[0]: 第一个乘数(数字或分数)
174/// - args[1]: 第二个乘数(数字或分数)
175///
176/// 返回:
177/// - 两个分数相乘的结果
178pub fn frac_mul(args: &[Value]) -> Result<Value, RuntimeError> {
179    if args.len() != 2 {
180        return Err(RuntimeError::WrongArity {
181            expected: 2,
182            got: args.len(),
183        });
184    }
185    let frac1 = value_to_fraction(&args[0])?;
186    let frac2 = value_to_fraction(&args[1])?;
187    Ok(Value::Fraction(frac1 * frac2))
188}
189
190/// 分数除法运算
191///
192/// 参数:
193/// - args[0]: 被除数(数字或分数)
194/// - args[1]: 除数(数字或分数)
195///
196/// 返回:
197/// - 两个分数相除的结果
198///
199/// 注意:除数不能为零
200pub fn frac_div(args: &[Value]) -> Result<Value, RuntimeError> {
201    if args.len() != 2 {
202        return Err(RuntimeError::WrongArity {
203            expected: 2,
204            got: args.len(),
205        });
206    }
207    let frac1 = value_to_fraction(&args[0])?;
208    let frac2 = value_to_fraction(&args[1])?;
209    if frac2.is_zero() {
210        return Err(RuntimeError::InvalidOperation(
211            "Division by zero".to_string(),
212        ));
213    }
214    Ok(Value::Fraction(frac1 / frac2))
215}
216
217/// 获取分数的分子
218///
219/// 参数:
220/// - args[0]: 分数值
221///
222/// 返回:
223/// - 分数的分子(转换为浮点数)
224pub fn numerator(args: &[Value]) -> Result<Value, RuntimeError> {
225    if args.len() != 1 {
226        return Err(RuntimeError::WrongArity {
227            expected: 1,
228            got: args.len(),
229        });
230    }
231    match &args[0] {
232        Value::Fraction(f) => {
233            let num = f.numer().to_f64().ok_or_else(|| {
234                RuntimeError::InvalidOperation("Failed to convert numerator".to_string())
235            })?;
236            Ok(Value::Number(num))
237        }
238        _ => Err(RuntimeError::TypeErrorDetailed {
239            expected: "Fraction".to_string(),
240            got: format!("{:?}", args[0]),
241        }),
242    }
243}
244
245/// 获取分数的分母
246///
247/// 参数:
248/// - args[0]: 分数值
249///
250/// 返回:
251/// - 分数的分母(转换为浮点数)
252pub fn denominator(args: &[Value]) -> Result<Value, RuntimeError> {
253    if args.len() != 1 {
254        return Err(RuntimeError::WrongArity {
255            expected: 1,
256            got: args.len(),
257        });
258    }
259    match &args[0] {
260        Value::Fraction(f) => {
261            let den = f.denom().to_f64().ok_or_else(|| {
262                RuntimeError::InvalidOperation("Failed to convert denominator".to_string())
263            })?;
264            Ok(Value::Number(den))
265        }
266        _ => Err(RuntimeError::TypeErrorDetailed {
267            expected: "Fraction".to_string(),
268            got: format!("{:?}", args[0]),
269        }),
270    }
271}
272
273/// 计算两个整数的最大公约数(Greatest Common Divisor)
274///
275/// 参数:
276/// - args[0]: 第一个整数
277/// - args[1]: 第二个整数
278///
279/// 返回:
280/// - 两个数的最大公约数
281pub fn gcd(args: &[Value]) -> Result<Value, RuntimeError> {
282    if args.len() != 2 {
283        return Err(RuntimeError::WrongArity {
284            expected: 2,
285            got: args.len(),
286        });
287    }
288    let a = match &args[0] {
289        Value::Number(n) => *n as i64,
290        _ => {
291            return Err(RuntimeError::TypeErrorDetailed {
292                expected: "Number".to_string(),
293                got: format!("{:?}", args[0]),
294            });
295        }
296    };
297    let b = match &args[1] {
298        Value::Number(n) => *n as i64,
299        _ => {
300            return Err(RuntimeError::TypeErrorDetailed {
301                expected: "Number".to_string(),
302                got: format!("{:?}", args[1]),
303            });
304        }
305    };
306    // 欧几里得算法实现最大公约数计算
307    fn gcd_impl(mut a: i64, mut b: i64) -> i64 {
308        while b != 0 {
309            let temp = b;
310            b = a % b;
311            a = temp;
312        }
313        a.abs()
314    }
315    Ok(Value::Number(gcd_impl(a, b) as f64))
316}
317
318/// 计算两个整数的最小公倍数(Least Common Multiple)
319///
320/// 参数:
321/// - args[0]: 第一个整数
322/// - args[1]: 第二个整数
323///
324/// 返回:
325/// - 两个数的最小公倍数
326pub fn lcm(args: &[Value]) -> Result<Value, RuntimeError> {
327    if args.len() != 2 {
328        return Err(RuntimeError::WrongArity {
329            expected: 2,
330            got: args.len(),
331        });
332    }
333    let a = match &args[0] {
334        Value::Number(n) => *n as i64,
335        _ => {
336            return Err(RuntimeError::TypeErrorDetailed {
337                expected: "Number".to_string(),
338                got: format!("{:?}", args[0]),
339            });
340        }
341    };
342    let b = match &args[1] {
343        Value::Number(n) => *n as i64,
344        _ => {
345            return Err(RuntimeError::TypeErrorDetailed {
346                expected: "Number".to_string(),
347                got: format!("{:?}", args[1]),
348            });
349        }
350    };
351    // 使用公式:lcm(a,b) = |a*b| / gcd(a,b)
352    fn gcd_impl(mut a: i64, mut b: i64) -> i64 {
353        while b != 0 {
354            let temp = b;
355            b = a % b;
356            a = temp;
357        }
358        a.abs()
359    }
360    let result = (a.abs() * b.abs()) / gcd_impl(a, b);
361    Ok(Value::Number(result as f64))
362}