uni_core/primitives/
multiply.rs

1// RUST CONCEPT: Modular primitive organization
2// Each primitive gets its own file with implementation and tests
3use crate::compat::ToString;
4use crate::interpreter::Interpreter;
5use crate::value::{RuntimeError, Value};
6use num_bigint::BigInt;
7#[cfg(feature = "complex_numbers")]
8use num_complex::Complex64;
9use num_rational::BigRational;
10#[cfg(feature = "complex_numbers")]
11use num_traits::ToPrimitive;
12
13#[cfg(target_os = "none")]
14use num_traits::Float;
15
16// RUST CONCEPT: Polymorphic multiplication - multiple numeric types
17// Stack-based multiplication: ( n1 n2 -- product )
18// Supports: f64, BigInt, BigRational, GaussianInt, Complex64
19pub fn mul_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
20    let b = interp.pop()?;
21    let a = interp.pop()?;
22
23    match (&a, &b) {
24        // ========== NUMERIC MULTIPLICATIONS ==========
25
26        // RUST CONCEPT: Fast path for Int32 - checked arithmetic for embedded safety
27        // Int32 * Int32
28        (Value::Int32(i1), Value::Int32(i2)) => {
29            match i1.checked_mul(*i2) {
30                Some(result) => interp.push(Value::Int32(result)),
31                // Overflow: promote to BigInt
32                None => interp.push(Value::Integer(BigInt::from(*i1) * BigInt::from(*i2))),
33            }
34        }
35
36        // Float * Float
37        (Value::Number(n1), Value::Number(n2)) => {
38            interp.push(Value::Number(n1 * n2));
39        }
40
41        // BigInt * BigInt
42        (Value::Integer(i1), Value::Integer(i2)) => {
43            interp.push(Value::Integer(i1 * i2));
44        }
45
46        // Rational * Rational
47        (Value::Rational(r1), Value::Rational(r2)) => {
48            let result = Value::Rational(r1 * r2);
49            interp.push(result.demote());
50        }
51
52        // Complex * Complex
53        #[cfg(feature = "complex_numbers")]
54        (Value::Complex(c1), Value::Complex(c2)) => {
55            interp.push(Value::Complex(c1 * c2));
56        }
57
58        // GaussianInt * GaussianInt: (a+bi)(c+di) = (ac-bd)+(ad+bc)i
59        #[cfg(feature = "complex_numbers")]
60        (Value::GaussianInt(a_re, a_im), Value::GaussianInt(b_re, b_im)) => {
61            let ac = a_re * b_re;
62            let bd = a_im * b_im;
63            let ad = a_re * b_im;
64            let bc = a_im * b_re;
65            let result = Value::GaussianInt(&ac - &bd, ad + bc);
66            interp.push(result.demote());
67        }
68
69        // ========== MIXED NUMERIC TYPE MULTIPLICATIONS ==========
70
71        // Int32 * Integer
72        (Value::Int32(i32_val), Value::Integer(i)) | (Value::Integer(i), Value::Int32(i32_val)) => {
73            interp.push(Value::Integer(BigInt::from(*i32_val) * i));
74        }
75
76        // Int32 * Rational
77        (Value::Int32(i32_val), Value::Rational(r)) | (Value::Rational(r), Value::Int32(i32_val)) => {
78            let i_rat = BigRational::from(BigInt::from(*i32_val));
79            let result = Value::Rational(i_rat * r);
80            interp.push(result.demote());
81        }
82
83        // Int32 * Number
84        (Value::Int32(i32_val), Value::Number(n)) | (Value::Number(n), Value::Int32(i32_val)) => {
85            interp.push(Value::Number(*i32_val as f64 * n));
86        }
87
88        // Int32 * Complex
89        #[cfg(feature = "complex_numbers")]
90        (Value::Int32(i32_val), Value::Complex(c)) | (Value::Complex(c), Value::Int32(i32_val)) => {
91            interp.push(Value::Complex(Complex64::new(*i32_val as f64, 0.0) * c));
92        }
93
94        // Int32 * GaussianInt
95        #[cfg(feature = "complex_numbers")]
96        (Value::Int32(i32_val), Value::GaussianInt(re, im))
97        | (Value::GaussianInt(re, im), Value::Int32(i32_val)) => {
98            let result = Value::GaussianInt(re * BigInt::from(*i32_val), im * BigInt::from(*i32_val));
99            interp.push(result.demote());
100        }
101
102        // Float * Integer
103        (Value::Number(n), Value::Integer(i)) | (Value::Integer(i), Value::Number(n)) => {
104            if n.fract() == 0.0 && n.is_finite() {
105                let n_int = BigInt::from(*n as i64);
106                interp.push(Value::Integer(&n_int * i));
107            } else {
108                let r = float_to_rational(*n);
109                let i_rat = BigRational::from(i.clone());
110                interp.push(Value::Rational(r * i_rat));
111            }
112        }
113
114        // Float * Rational
115        (Value::Number(n), Value::Rational(r)) | (Value::Rational(r), Value::Number(n)) => {
116            let n_rat = float_to_rational(*n);
117            interp.push(Value::Rational(n_rat * r));
118        }
119
120        // Float * Complex
121        #[cfg(feature = "complex_numbers")]
122        (Value::Number(n), Value::Complex(c)) | (Value::Complex(c), Value::Number(n)) => {
123            interp.push(Value::Complex(Complex64::new(*n, 0.0) * c));
124        }
125
126        // Integer * Rational
127        (Value::Integer(i), Value::Rational(r)) | (Value::Rational(r), Value::Integer(i)) => {
128            let i_rat = BigRational::from(i.clone());
129            let result = Value::Rational(i_rat * r);
130            interp.push(result.demote());
131        }
132
133        // Integer * Complex
134        #[cfg(feature = "complex_numbers")]
135        (Value::Integer(i), Value::Complex(c)) | (Value::Complex(c), Value::Integer(i)) => {
136            let i_float = i.to_f64().unwrap_or(f64::INFINITY);
137            interp.push(Value::Complex(Complex64::new(i_float, 0.0) * c));
138        }
139
140        // Rational * Complex
141        #[cfg(feature = "complex_numbers")]
142        (Value::Rational(r), Value::Complex(c)) | (Value::Complex(c), Value::Rational(r)) => {
143            let r_float = rational_to_float(r);
144            interp.push(Value::Complex(Complex64::new(r_float, 0.0) * c));
145        }
146
147        // GaussianInt * Integer: (a+bi) * c = ac + bci
148        #[cfg(feature = "complex_numbers")]
149        (Value::GaussianInt(re, im), Value::Integer(i))
150        | (Value::Integer(i), Value::GaussianInt(re, im)) => {
151            let result = Value::GaussianInt(re * i, im * i);
152            interp.push(result.demote());
153        }
154
155        // GaussianInt * Float -> Complex64 (promote)
156        #[cfg(feature = "complex_numbers")]
157        (Value::GaussianInt(re, im), Value::Number(n))
158        | (Value::Number(n), Value::GaussianInt(re, im)) => {
159            let re_float = re.to_f64().unwrap_or(f64::INFINITY);
160            let im_float = im.to_f64().unwrap_or(f64::INFINITY);
161            interp.push(Value::Complex(
162                Complex64::new(re_float, im_float) * Complex64::new(*n, 0.0),
163            ));
164        }
165
166        // GaussianInt * Rational -> Complex64 (promote)
167        #[cfg(feature = "complex_numbers")]
168        (Value::GaussianInt(re, im), Value::Rational(r))
169        | (Value::Rational(r), Value::GaussianInt(re, im)) => {
170            let re_float = re.to_f64().unwrap_or(f64::INFINITY);
171            let im_float = im.to_f64().unwrap_or(f64::INFINITY);
172            let r_float = rational_to_float(r);
173            interp.push(Value::Complex(
174                Complex64::new(re_float, im_float) * Complex64::new(r_float, 0.0),
175            ));
176        }
177
178        // GaussianInt * Complex -> Complex64
179        #[cfg(feature = "complex_numbers")]
180        (Value::GaussianInt(re, im), Value::Complex(c))
181        | (Value::Complex(c), Value::GaussianInt(re, im)) => {
182            let re_float = re.to_f64().unwrap_or(f64::INFINITY);
183            let im_float = im.to_f64().unwrap_or(f64::INFINITY);
184            interp.push(Value::Complex(Complex64::new(re_float, im_float) * c));
185        }
186
187        _ => {
188            return Err(RuntimeError::TypeError(
189                "Multiplication requires numbers".to_string(),
190            ));
191        }
192    }
193
194    Ok(())
195}
196
197// Helper function to convert f64 to BigRational
198fn float_to_rational(f: f64) -> BigRational {
199    use num_bigint::ToBigInt;
200    use num_traits::Zero;
201
202    if !f.is_finite() {
203        return BigRational::zero();
204    }
205
206    let denominator = 1_000_000_000i64;
207    let numerator = (f * denominator as f64).round() as i64;
208
209    BigRational::new(
210        numerator.to_bigint().unwrap(),
211        denominator.to_bigint().unwrap(),
212    )
213}
214
215// Helper function to convert BigRational to f64
216#[cfg(feature = "complex_numbers")]
217fn rational_to_float(r: &BigRational) -> f64 {
218    let numer = r.numer().to_f64().unwrap_or(0.0);
219    let denom = r.denom().to_f64().unwrap_or(1.0);
220    numer / denom
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226    use crate::value::Value;
227
228    fn setup_interpreter() -> Interpreter {
229        Interpreter::new()
230    }
231
232    #[test]
233    fn test_mul_builtin() {
234        let mut interp = setup_interpreter();
235
236        // Test basic multiplication: 4 * 3 = 12
237        interp.push(Value::Number(4.0));
238        interp.push(Value::Number(3.0));
239        mul_builtin(&mut interp).unwrap();
240
241        let result = interp.pop().unwrap();
242        assert!(matches!(result, Value::Number(n) if n == 12.0));
243
244        // Test with negative numbers: -2 * 7 = -14
245        interp.push(Value::Number(-2.0));
246        interp.push(Value::Number(7.0));
247        mul_builtin(&mut interp).unwrap();
248
249        let result = interp.pop().unwrap();
250        assert!(matches!(result, Value::Number(n) if n == -14.0));
251
252        // Test with zero
253        interp.push(Value::Number(42.0));
254        interp.push(Value::Number(0.0));
255        mul_builtin(&mut interp).unwrap();
256
257        let result = interp.pop().unwrap();
258        assert!(matches!(result, Value::Number(n) if n == 0.0));
259
260        // Test with fractional numbers
261        interp.push(Value::Number(2.5));
262        interp.push(Value::Number(4.0));
263        mul_builtin(&mut interp).unwrap();
264
265        let result = interp.pop().unwrap();
266        assert!(matches!(result, Value::Number(n) if n == 10.0));
267    }
268
269    #[test]
270    fn test_mul_builtin_stack_underflow() {
271        let mut interp = setup_interpreter();
272
273        // Test with empty stack
274        let result = mul_builtin(&mut interp);
275        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
276
277        // Test with only one element
278        interp.push(Value::Number(5.0));
279        let result = mul_builtin(&mut interp);
280        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
281    }
282
283    #[test]
284    fn test_mul_builtin_type_error() {
285        let mut interp = setup_interpreter();
286
287        // Test with wrong types
288        let atom = interp.intern_atom("not-a-number");
289        interp.push(Value::Atom(atom));
290        interp.push(Value::Number(5.0));
291        let result = mul_builtin(&mut interp);
292        assert!(matches!(result, Err(RuntimeError::TypeError(_))));
293    }
294
295    #[test]
296    #[cfg(feature = "complex_numbers")]
297    fn test_mul_gaussian_int() {
298        let mut interp = setup_interpreter();
299
300        // Test i * i = -1 (fundamental property of i, demoted to Int32)
301        interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(1))); // i
302        interp.push(Value::GaussianInt(BigInt::from(0), BigInt::from(1))); // i
303        mul_builtin(&mut interp).unwrap();
304
305        let result = interp.pop().unwrap();
306        assert!(matches!(result, Value::Int32(-1)));
307
308        // Test (3+4i) * (2+i) = 6+3i+8i+4i² = 6+11i-4 = 2+11i
309        interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
310        interp.push(Value::GaussianInt(BigInt::from(2), BigInt::from(1)));
311        mul_builtin(&mut interp).unwrap();
312
313        let result = interp.pop().unwrap();
314        assert!(matches!(
315            result,
316            Value::GaussianInt(ref re, ref im)
317            if re == &BigInt::from(2) && im == &BigInt::from(11)
318        ));
319    }
320
321    #[test]
322    #[cfg(feature = "complex_numbers")]
323    fn test_mul_gaussian_int_with_integer() {
324        let mut interp = setup_interpreter();
325
326        // Test (3+4i) * 5 = 15+20i
327        interp.push(Value::GaussianInt(BigInt::from(3), BigInt::from(4)));
328        interp.push(Value::Integer(BigInt::from(5)));
329        mul_builtin(&mut interp).unwrap();
330
331        let result = interp.pop().unwrap();
332        assert!(matches!(
333            result,
334            Value::GaussianInt(ref re, ref im)
335            if re == &BigInt::from(15) && im == &BigInt::from(20)
336        ));
337    }
338}