uni_core/primitives/
divide.rs

1// RUST CONCEPT: Modular primitive organization
2// Each primitive gets its own file with implementation and tests
3use crate::compat::format;
4use crate::interpreter::Interpreter;
5use crate::primitives::numeric_promotion::promote_pair;
6use crate::value::{RuntimeError, Value};
7use num_rational::BigRational;
8use num_traits::Zero;
9
10// RUST CONCEPT: Division with zero checking and type promotion
11// Stack-based division: ( n1 n2 -- quotient )
12// Special case: Integer / Integer promotes to Rational for exact division
13pub fn div_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
14    let b = interp.pop()?;
15    let a = interp.pop()?;
16
17    // Check for division by zero first
18    let is_zero = match &b {
19        Value::Int32(i) => *i == 0,
20        Value::Integer(i) => i.is_zero(),
21        Value::Rational(r) => r.is_zero(),
22        Value::Number(n) => *n == 0.0,
23        _ => false,
24    };
25    if is_zero {
26        return Err(RuntimeError::DivisionByZero);
27    }
28
29    // Special case: Int32 / Int32 and Integer / Integer should promote to Rational
30    // This ensures exact division results (e.g., 1/2 = 1/2, not 0.5)
31    let result = if let (Value::Int32(ia), Value::Int32(ib)) = (&a, &b) {
32        // Int32 / Int32 -> Rational (then demote if denominator is 1)
33        let result = Value::Rational(BigRational::new(
34            num_bigint::BigInt::from(*ia),
35            num_bigint::BigInt::from(*ib),
36        ));
37        result.demote()
38    } else if let (Value::Integer(ia), Value::Integer(ib)) = (&a, &b) {
39        let result = Value::Rational(BigRational::new(ia.clone(), ib.clone()));
40        result.demote()
41    } else {
42        // For all other type combinations, use standard promotion
43        let (pa, pb) = promote_pair(&a, &b);
44
45        match (&pa, &pb) {
46            (Value::Rational(r1), Value::Rational(r2)) => {
47                let result = Value::Rational(r1 / r2);
48                result.demote()
49            }
50            (Value::Number(n1), Value::Number(n2)) => Value::Number(n1 / n2),
51            #[cfg(feature = "complex_numbers")]
52            (Value::Complex(c1), Value::Complex(c2)) => Value::Complex(c1 / c2),
53            _ => {
54                return Err(RuntimeError::TypeError(format!(
55                    "Cannot divide {:?} and {:?}",
56                    a, b
57                )))
58            }
59        }
60    };
61
62    interp.push(result);
63    Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::value::Value;
70    use num_bigint::BigInt;
71    use num_rational::BigRational;
72
73    fn setup_interpreter() -> Interpreter {
74        Interpreter::new()
75    }
76
77    #[test]
78    fn test_div_builtin_floats() {
79        let mut interp = setup_interpreter();
80
81        // Test basic division: 12.0 / 4.0 = 3.0
82        interp.push(Value::Number(12.0));
83        interp.push(Value::Number(4.0));
84        div_builtin(&mut interp).unwrap();
85
86        let result = interp.pop().unwrap();
87        assert!(matches!(result, Value::Number(n) if n == 3.0));
88
89        // Test with fractional result: 7.0 / 2.0 = 3.5
90        interp.push(Value::Number(7.0));
91        interp.push(Value::Number(2.0));
92        div_builtin(&mut interp).unwrap();
93
94        let result = interp.pop().unwrap();
95        assert!(matches!(result, Value::Number(n) if n == 3.5));
96
97        // Test with negative numbers: -8.0 / 2.0 = -4.0
98        interp.push(Value::Number(-8.0));
99        interp.push(Value::Number(2.0));
100        div_builtin(&mut interp).unwrap();
101
102        let result = interp.pop().unwrap();
103        assert!(matches!(result, Value::Number(n) if n == -4.0));
104    }
105
106    #[test]
107    fn test_div_builtin_integers_to_rational() {
108        let mut interp = setup_interpreter();
109
110        // Test Integer / Integer -> Rational: 1 / 2 = 1/2
111        interp.push(Value::Integer(BigInt::from(1)));
112        interp.push(Value::Integer(BigInt::from(2)));
113        div_builtin(&mut interp).unwrap();
114
115        let result = interp.pop().unwrap();
116        if let Value::Rational(r) = result {
117            assert_eq!(*r.numer(), BigInt::from(1));
118            assert_eq!(*r.denom(), BigInt::from(2));
119        } else {
120            panic!("Expected Rational, got {:?}", result);
121        }
122
123        // Test Integer / Integer with exact division -> demotes to Int32: 10 / 2 = 5
124        interp.push(Value::Integer(BigInt::from(10)));
125        interp.push(Value::Integer(BigInt::from(2)));
126        div_builtin(&mut interp).unwrap();
127
128        let result = interp.pop().unwrap();
129        assert!(matches!(result, Value::Int32(5)));
130    }
131
132    #[test]
133    fn test_div_builtin_mixed_types() {
134        let mut interp = setup_interpreter();
135
136        // Test Integer / Number: 10 / 2.0 = 5.0
137        interp.push(Value::Integer(BigInt::from(10)));
138        interp.push(Value::Number(2.0));
139        div_builtin(&mut interp).unwrap();
140
141        let result = interp.pop().unwrap();
142        assert!(matches!(result, Value::Number(n) if n == 5.0));
143
144        // Test Rational / Number: (1/2) / 2.0 = 0.25
145        interp.push(Value::Rational(BigRational::new(
146            BigInt::from(1),
147            BigInt::from(2),
148        )));
149        interp.push(Value::Number(2.0));
150        div_builtin(&mut interp).unwrap();
151
152        let result = interp.pop().unwrap();
153        assert!(matches!(result, Value::Number(n) if n == 0.25));
154    }
155
156    #[test]
157    fn test_div_builtin_rationals() {
158        let mut interp = setup_interpreter();
159
160        // Test Rational / Rational: (1/2) / (1/4) = 2
161        interp.push(Value::Rational(BigRational::new(
162            BigInt::from(1),
163            BigInt::from(2),
164        )));
165        interp.push(Value::Rational(BigRational::new(
166            BigInt::from(1),
167            BigInt::from(4),
168        )));
169        div_builtin(&mut interp).unwrap();
170
171        let result = interp.pop().unwrap();
172        // Should demote to Int32(2)
173        assert!(matches!(result, Value::Int32(2)));
174    }
175
176    #[test]
177    fn test_div_builtin_division_by_zero() {
178        let mut interp = setup_interpreter();
179
180        // Test division by zero with floats
181        interp.push(Value::Number(5.0));
182        interp.push(Value::Number(0.0));
183        let result = div_builtin(&mut interp);
184        assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
185
186        // Test division by zero with integers
187        interp.push(Value::Integer(BigInt::from(5)));
188        interp.push(Value::Integer(BigInt::from(0)));
189        let result = div_builtin(&mut interp);
190        assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
191
192        // Test division by zero with rationals
193        interp.push(Value::Rational(BigRational::new(
194            BigInt::from(5),
195            BigInt::from(1),
196        )));
197        interp.push(Value::Rational(BigRational::new(
198            BigInt::from(0),
199            BigInt::from(1),
200        )));
201        let result = div_builtin(&mut interp);
202        assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
203    }
204
205    #[test]
206    fn test_div_builtin_stack_underflow() {
207        let mut interp = setup_interpreter();
208
209        // Test with empty stack
210        let result = div_builtin(&mut interp);
211        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
212
213        // Test with only one element
214        interp.push(Value::Number(5.0));
215        let result = div_builtin(&mut interp);
216        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
217    }
218
219    #[test]
220    fn test_div_builtin_type_error() {
221        let mut interp = setup_interpreter();
222
223        // Test with wrong types
224        interp.push(Value::Number(5.0));
225        interp.push(Value::Null);
226        let result = div_builtin(&mut interp);
227        assert!(matches!(result, Err(RuntimeError::TypeError(_))));
228    }
229
230    #[test]
231    fn test_div_integer_by_integer_exact_division() {
232        let mut interp = setup_interpreter();
233
234        // Test cases where integer division results in whole numbers (should demote)
235
236        // 100 / 10 = 10
237        interp.push(Value::Integer(BigInt::from(100)));
238        interp.push(Value::Integer(BigInt::from(10)));
239        div_builtin(&mut interp).unwrap();
240        let result = interp.pop().unwrap();
241        assert!(matches!(result, Value::Int32(10)));
242
243        // 1000 / 100 = 10
244        interp.push(Value::Integer(BigInt::from(1000)));
245        interp.push(Value::Integer(BigInt::from(100)));
246        div_builtin(&mut interp).unwrap();
247        let result = interp.pop().unwrap();
248        assert!(matches!(result, Value::Int32(10)));
249
250        // -20 / 4 = -5
251        interp.push(Value::Integer(BigInt::from(-20)));
252        interp.push(Value::Integer(BigInt::from(4)));
253        div_builtin(&mut interp).unwrap();
254        let result = interp.pop().unwrap();
255        assert!(matches!(result, Value::Int32(-5)));
256
257        // 20 / -4 = -5
258        interp.push(Value::Integer(BigInt::from(20)));
259        interp.push(Value::Integer(BigInt::from(-4)));
260        div_builtin(&mut interp).unwrap();
261        let result = interp.pop().unwrap();
262        assert!(matches!(result, Value::Int32(-5)));
263    }
264
265    #[test]
266    fn test_div_integer_by_integer_fractional_result() {
267        let mut interp = setup_interpreter();
268
269        // Test cases where integer division results in fractions (stays Rational)
270
271        // 1 / 3 = 1/3
272        interp.push(Value::Integer(BigInt::from(1)));
273        interp.push(Value::Integer(BigInt::from(3)));
274        div_builtin(&mut interp).unwrap();
275        let result = interp.pop().unwrap();
276        if let Value::Rational(r) = result {
277            assert_eq!(*r.numer(), BigInt::from(1));
278            assert_eq!(*r.denom(), BigInt::from(3));
279        } else {
280            panic!("Expected Rational, got {:?}", result);
281        }
282
283        // 7 / 4 = 7/4
284        interp.push(Value::Integer(BigInt::from(7)));
285        interp.push(Value::Integer(BigInt::from(4)));
286        div_builtin(&mut interp).unwrap();
287        let result = interp.pop().unwrap();
288        if let Value::Rational(r) = result {
289            assert_eq!(*r.numer(), BigInt::from(7));
290            assert_eq!(*r.denom(), BigInt::from(4));
291        } else {
292            panic!("Expected Rational, got {:?}", result);
293        }
294
295        // 2 / 5 = 2/5
296        interp.push(Value::Integer(BigInt::from(2)));
297        interp.push(Value::Integer(BigInt::from(5)));
298        div_builtin(&mut interp).unwrap();
299        let result = interp.pop().unwrap();
300        if let Value::Rational(r) = result {
301            assert_eq!(*r.numer(), BigInt::from(2));
302            assert_eq!(*r.denom(), BigInt::from(5));
303        } else {
304            panic!("Expected Rational, got {:?}", result);
305        }
306
307        // -3 / 7 = -3/7
308        interp.push(Value::Integer(BigInt::from(-3)));
309        interp.push(Value::Integer(BigInt::from(7)));
310        div_builtin(&mut interp).unwrap();
311        let result = interp.pop().unwrap();
312        if let Value::Rational(r) = result {
313            assert_eq!(*r.numer(), BigInt::from(-3));
314            assert_eq!(*r.denom(), BigInt::from(7));
315        } else {
316            panic!("Expected Rational, got {:?}", result);
317        }
318    }
319
320    #[test]
321    fn test_div_integer_by_integer_simplification() {
322        let mut interp = setup_interpreter();
323
324        // Test that results are automatically simplified
325
326        // 2 / 4 = 1/2 (simplified from 2/4)
327        interp.push(Value::Integer(BigInt::from(2)));
328        interp.push(Value::Integer(BigInt::from(4)));
329        div_builtin(&mut interp).unwrap();
330        let result = interp.pop().unwrap();
331        if let Value::Rational(r) = result {
332            assert_eq!(*r.numer(), BigInt::from(1));
333            assert_eq!(*r.denom(), BigInt::from(2));
334        } else {
335            panic!("Expected Rational, got {:?}", result);
336        }
337
338        // 6 / 9 = 2/3 (simplified from 6/9)
339        interp.push(Value::Integer(BigInt::from(6)));
340        interp.push(Value::Integer(BigInt::from(9)));
341        div_builtin(&mut interp).unwrap();
342        let result = interp.pop().unwrap();
343        if let Value::Rational(r) = result {
344            assert_eq!(*r.numer(), BigInt::from(2));
345            assert_eq!(*r.denom(), BigInt::from(3));
346        } else {
347            panic!("Expected Rational, got {:?}", result);
348        }
349
350        // 15 / 25 = 3/5 (simplified from 15/25)
351        interp.push(Value::Integer(BigInt::from(15)));
352        interp.push(Value::Integer(BigInt::from(25)));
353        div_builtin(&mut interp).unwrap();
354        let result = interp.pop().unwrap();
355        if let Value::Rational(r) = result {
356            assert_eq!(*r.numer(), BigInt::from(3));
357            assert_eq!(*r.denom(), BigInt::from(5));
358        } else {
359            panic!("Expected Rational, got {:?}", result);
360        }
361    }
362
363    #[test]
364    fn test_div_mixed_integer_rational() {
365        let mut interp = setup_interpreter();
366
367        // Test Integer / Rational (should use promotion)
368        // 10 / (1/2) = 20
369        interp.push(Value::Integer(BigInt::from(10)));
370        interp.push(Value::Rational(BigRational::new(
371            BigInt::from(1),
372            BigInt::from(2),
373        )));
374        div_builtin(&mut interp).unwrap();
375        let result = interp.pop().unwrap();
376        assert!(matches!(result, Value::Int32(20)));
377
378        // Test Rational / Integer (should use promotion)
379        // (3/4) / 2 = 3/8
380        interp.push(Value::Rational(BigRational::new(
381            BigInt::from(3),
382            BigInt::from(4),
383        )));
384        interp.push(Value::Integer(BigInt::from(2)));
385        div_builtin(&mut interp).unwrap();
386        let result = interp.pop().unwrap();
387        if let Value::Rational(r) = result {
388            assert_eq!(*r.numer(), BigInt::from(3));
389            assert_eq!(*r.denom(), BigInt::from(8));
390        } else {
391            panic!("Expected Rational, got {:?}", result);
392        }
393    }
394
395    #[test]
396    fn test_div_rational_non_simplifying() {
397        let mut interp = setup_interpreter();
398
399        // Test Rational / Rational that doesn't simplify to integer
400        // (2/3) / (4/5) = (2/3) * (5/4) = 10/12 = 5/6
401        interp.push(Value::Rational(BigRational::new(
402            BigInt::from(2),
403            BigInt::from(3),
404        )));
405        interp.push(Value::Rational(BigRational::new(
406            BigInt::from(4),
407            BigInt::from(5),
408        )));
409        div_builtin(&mut interp).unwrap();
410        let result = interp.pop().unwrap();
411        if let Value::Rational(r) = result {
412            assert_eq!(*r.numer(), BigInt::from(5));
413            assert_eq!(*r.denom(), BigInt::from(6));
414        } else {
415            panic!("Expected Rational, got {:?}", result);
416        }
417    }
418
419    #[test]
420    fn test_div_mixed_number_integer() {
421        let mut interp = setup_interpreter();
422
423        // Test Number / Integer (should promote to Number)
424        // 10.5 / 2 = 5.25
425        interp.push(Value::Number(10.5));
426        interp.push(Value::Integer(BigInt::from(2)));
427        div_builtin(&mut interp).unwrap();
428        let result = interp.pop().unwrap();
429        assert!(matches!(result, Value::Number(n) if n == 5.25));
430
431        // Test Integer / Number (should promote to Number)
432        // 15 / 2.0 = 7.5
433        interp.push(Value::Integer(BigInt::from(15)));
434        interp.push(Value::Number(2.0));
435        div_builtin(&mut interp).unwrap();
436        let result = interp.pop().unwrap();
437        assert!(matches!(result, Value::Number(n) if n == 7.5));
438    }
439
440    #[test]
441    fn test_div_mixed_number_rational() {
442        let mut interp = setup_interpreter();
443
444        // Test Number / Rational (should promote to Number)
445        // 8.0 / (1/2) = 16.0
446        interp.push(Value::Number(8.0));
447        interp.push(Value::Rational(BigRational::new(
448            BigInt::from(1),
449            BigInt::from(2),
450        )));
451        div_builtin(&mut interp).unwrap();
452        let result = interp.pop().unwrap();
453        assert!(matches!(result, Value::Number(n) if n == 16.0));
454
455        // Test Rational / Number (should promote to Number)
456        // (3/4) / 2.0 = 0.375
457        interp.push(Value::Rational(BigRational::new(
458            BigInt::from(3),
459            BigInt::from(4),
460        )));
461        interp.push(Value::Number(2.0));
462        div_builtin(&mut interp).unwrap();
463        let result = interp.pop().unwrap();
464        assert!(matches!(result, Value::Number(n) if (n - 0.375).abs() < 1e-10));
465    }
466
467    #[test]
468    #[cfg(feature = "complex_numbers")]
469    fn test_div_complex_numbers() {
470        use num_complex::Complex64;
471
472        let mut interp = setup_interpreter();
473
474        // Test Complex / Complex: (4+2i) / (1+1i) = (6+(-2)i) / 2 = 3-i
475        interp.push(Value::Complex(Complex64::new(4.0, 2.0)));
476        interp.push(Value::Complex(Complex64::new(1.0, 1.0)));
477        div_builtin(&mut interp).unwrap();
478        let result = interp.pop().unwrap();
479        if let Value::Complex(c) = result {
480            assert!((c.re - 3.0).abs() < 1e-10);
481            assert!((c.im - (-1.0)).abs() < 1e-10);
482        } else {
483            panic!("Expected Complex, got {:?}", result);
484        }
485    }
486
487    #[test]
488    fn test_div_large_integers() {
489        let mut interp = setup_interpreter();
490
491        // Test with large coprime integers (no common factors)
492        // Using prime-like numbers to ensure they don't divide evenly
493        let large1 = BigInt::parse_bytes(b"123456789012345678901234567891", 10).unwrap();
494        let large2 = BigInt::parse_bytes(b"987654321098765432109876543211", 10).unwrap();
495
496        interp.push(Value::Integer(large1.clone()));
497        interp.push(Value::Integer(large2.clone()));
498        div_builtin(&mut interp).unwrap();
499
500        let result = interp.pop().unwrap();
501        // Should be a rational since these don't divide evenly
502        // The rational will be simplified, so we just check it's a Rational type
503        assert!(matches!(result, Value::Rational(_)));
504    }
505}