1use crate::evaluator::RuntimeError;
3use crate::value::Value;
4use num_bigint::BigInt;
5use num_rational::Ratio;
6use num_traits::{One, ToPrimitive, Zero};
7
8pub 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
44pub 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
76pub 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
99fn 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
130pub 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
150pub 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
170pub 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
190pub 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
217pub 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
245pub 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
273pub 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 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
318pub 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 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}