mathhook_core/algebra/complex/
arithmetic.rs

1//! Complex number arithmetic methods for Expression
2//!
3//! Provides convenience methods for working with complex numbers, including
4//! extraction of real and imaginary parts, polar form conversions, and
5//! simplification operations.
6
7use super::operations::ComplexOperations;
8use crate::core::Expression;
9use crate::simplify::Simplify;
10
11impl Expression {
12    /// Extract the real part of a complex number
13    ///
14    /// Returns the real component of a complex expression. For non-complex
15    /// expressions, returns the expression itself.
16    ///
17    /// # Examples
18    ///
19    /// ```rust
20    /// use mathhook_core::{Expression, expr};
21    ///
22    /// let z = Expression::complex(expr!(3), expr!(4));
23    /// let real_part = z.real();
24    /// assert_eq!(real_part, expr!(3));
25    /// ```
26    pub fn real(&self) -> Expression {
27        match self {
28            Expression::Complex(data) => data.real.clone(),
29            _ => self.clone(),
30        }
31    }
32
33    /// Extract the imaginary part of a complex number
34    ///
35    /// Returns the imaginary component of a complex expression. For non-complex
36    /// expressions, returns zero.
37    ///
38    /// # Examples
39    ///
40    /// ```rust
41    /// use mathhook_core::{Expression, expr};
42    ///
43    /// let z = Expression::complex(expr!(3), expr!(4));
44    /// let imag_part = z.imag();
45    /// assert_eq!(imag_part, expr!(4));
46    /// ```
47    pub fn imag(&self) -> Expression {
48        match self {
49            Expression::Complex(data) => data.imag.clone(),
50            _ => Expression::integer(0),
51        }
52    }
53
54    /// Compute the complex conjugate
55    ///
56    /// Returns the complex conjugate (a + bi → a - bi). For non-complex
57    /// expressions, returns the expression itself.
58    ///
59    /// # Examples
60    ///
61    /// ```rust
62    /// use mathhook_core::{Expression, expr};
63    ///
64    /// let z = Expression::complex(expr!(3), expr!(4));
65    /// let conjugate = z.conjugate();
66    /// if let Expression::Complex(data) = conjugate {
67    ///     assert_eq!(data.real, expr!(3));
68    ///     assert_eq!(data.imag, expr!(-4));
69    /// }
70    /// ```
71    pub fn conjugate(&self) -> Expression {
72        self.complex_conjugate()
73    }
74
75    /// Compute the absolute value (modulus) of a complex number
76    ///
77    /// Returns |z| = √(re² + im²). For complex numbers, this is the magnitude.
78    /// For real numbers, this is the absolute value.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// use mathhook_core::{Expression, expr};
84    ///
85    /// let z = Expression::complex(expr!(3), expr!(4));
86    /// let magnitude = z.abs();
87    /// ```
88    pub fn abs(&self) -> Expression {
89        self.complex_modulus()
90    }
91
92    /// Compute the argument (phase angle) of a complex number
93    ///
94    /// Returns the angle θ = atan2(im, re) in radians, in the range (-π, π].
95    /// This is the principal value of the argument.
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// use mathhook_core::{Expression, expr};
101    ///
102    /// let z = Expression::complex(expr!(1), expr!(1));
103    /// let angle = z.arg();
104    /// ```
105    pub fn arg(&self) -> Expression {
106        self.complex_argument()
107    }
108
109    /// Convert to polar form (magnitude, angle)
110    ///
111    /// Returns (r, θ) where z = r·e^(iθ). The angle is in radians,
112    /// in the range (-π, π].
113    ///
114    /// # Examples
115    ///
116    /// ```rust
117    /// use mathhook_core::{Expression, expr};
118    ///
119    /// let z = Expression::complex(expr!(3), expr!(4));
120    /// let (magnitude, angle) = z.to_polar();
121    /// ```
122    pub fn to_polar(&self) -> (Expression, Expression) {
123        self.to_polar_form()
124    }
125
126    /// Create a complex number from polar form
127    ///
128    /// Converts polar coordinates (magnitude, angle) to rectangular form (a + bi).
129    /// The angle should be in radians.
130    ///
131    /// # Arguments
132    ///
133    /// * `magnitude` - The magnitude (r) of the complex number
134    /// * `angle` - The angle (θ) in radians
135    ///
136    /// # Examples
137    ///
138    /// ```rust
139    /// use mathhook_core::{Expression, expr};
140    ///
141    /// let magnitude = expr!(5);
142    /// let angle = Expression::pi();
143    /// let z = Expression::from_polar(magnitude, angle);
144    /// ```
145    pub fn from_polar(magnitude: Expression, angle: Expression) -> Expression {
146        Self::from_polar_form(magnitude, angle)
147    }
148
149    /// Create a complex number from polar form
150    ///
151    /// Converts polar coordinates (magnitude, angle) to rectangular form (a + bi).
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// use mathhook_core::{Expression, expr};
157    ///
158    /// let magnitude = expr!(5);
159    /// let angle = Expression::pi();
160    /// let z = Expression::from_polar_form(magnitude, angle);
161    /// ```
162    pub fn from_polar_form(magnitude: Expression, angle: Expression) -> Expression {
163        Expression::complex(
164            Expression::mul(vec![
165                magnitude.clone(),
166                Expression::function("cos", vec![angle.clone()]),
167            ])
168            .simplify(),
169            Expression::mul(vec![magnitude, Expression::function("sin", vec![angle])]).simplify(),
170        )
171    }
172
173    /// Simplify complex expressions by removing zero parts
174    ///
175    /// Converts complex numbers to their simplest form by removing zero
176    /// real or imaginary components.
177    ///
178    /// # Examples
179    ///
180    /// ```rust
181    /// use mathhook_core::{Expression, expr};
182    /// use mathhook_core::simplify::Simplify;
183    ///
184    /// let z = Expression::complex(expr!(3), expr!(0));
185    /// let simplified = z.simplify();
186    /// ```
187    pub fn simplify_complex(expr: &Expression) -> Expression {
188        match expr {
189            Expression::Complex(data) => {
190                let real_simplified = data.real.simplify();
191                let imag_simplified = data.imag.simplify();
192
193                if imag_simplified.is_zero() {
194                    return real_simplified;
195                }
196
197                if real_simplified.is_zero() {
198                    return Expression::mul(vec![imag_simplified, Expression::i()]).simplify();
199                }
200
201                Expression::complex(real_simplified, imag_simplified)
202            }
203            _ => expr.clone(),
204        }
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211    use crate::expr;
212
213    #[test]
214    fn test_complex_addition() {
215        let z1 = Expression::complex(expr!(3), expr!(4));
216        let z2 = Expression::complex(expr!(1), expr!(2));
217        let result = z1.complex_add(&z2);
218
219        if let Expression::Complex(data) = result {
220            assert_eq!(data.real, expr!(4));
221            assert_eq!(data.imag, expr!(6));
222        } else {
223            panic!("Expected complex result");
224        }
225    }
226
227    #[test]
228    fn test_complex_subtraction() {
229        let z1 = Expression::complex(expr!(5), expr!(7));
230        let z2 = Expression::complex(expr!(2), expr!(3));
231        let result = z1.complex_subtract(&z2);
232
233        if let Expression::Complex(data) = result {
234            assert_eq!(data.real, expr!(3));
235            assert_eq!(data.imag, expr!(4));
236        } else {
237            panic!("Expected complex result");
238        }
239    }
240
241    #[test]
242    fn test_complex_multiplication() {
243        let z1 = Expression::complex(expr!(3), expr!(4));
244        let z2 = Expression::complex(expr!(1), expr!(2));
245        let result = z1.complex_multiply(&z2);
246
247        if let Expression::Complex(data) = result {
248            assert_eq!(data.real, expr!(-5));
249            assert_eq!(data.imag, expr!(10));
250        } else {
251            panic!("Expected complex result");
252        }
253    }
254
255    #[test]
256    fn test_complex_division() {
257        let z1 = Expression::complex(expr!(2), expr!(3));
258        let z2 = Expression::complex(expr!(1), expr!(-1));
259        let result = z1.complex_divide(&z2);
260
261        if let Expression::Complex(_) = result {
262        } else {
263            panic!("Expected complex result");
264        }
265    }
266
267    #[test]
268    fn test_complex_conjugate() {
269        let z = Expression::complex(expr!(3), expr!(4));
270        let result = z.complex_conjugate();
271
272        if let Expression::Complex(data) = result {
273            assert_eq!(data.real, expr!(3));
274            assert_eq!(data.imag, expr!(-4));
275        } else {
276            panic!("Expected complex result");
277        }
278    }
279
280    #[test]
281    fn test_real_method() {
282        let z = Expression::complex(expr!(3), expr!(4));
283        let real_part = z.real();
284        assert_eq!(real_part, expr!(3));
285
286        let real_num = expr!(5);
287        let real_part = real_num.real();
288        assert_eq!(real_part, expr!(5));
289    }
290
291    #[test]
292    fn test_imag_method() {
293        let z = Expression::complex(expr!(3), expr!(4));
294        let imag_part = z.imag();
295        assert_eq!(imag_part, expr!(4));
296
297        let real_num = expr!(5);
298        let imag_part = real_num.imag();
299        assert_eq!(imag_part, expr!(0));
300    }
301
302    #[test]
303    fn test_conjugate_method() {
304        let z = Expression::complex(expr!(3), expr!(4));
305        let conjugate = z.conjugate();
306
307        if let Expression::Complex(data) = conjugate {
308            assert_eq!(data.real, expr!(3));
309            assert_eq!(data.imag, expr!(-4));
310        } else {
311            panic!("Expected complex result");
312        }
313    }
314
315    #[test]
316    fn test_abs_method() {
317        let z = Expression::complex(expr!(3), expr!(4));
318        let magnitude = z.abs();
319
320        match magnitude {
321            Expression::Function { .. } => {}
322            _ => panic!("Expected function expression for abs"),
323        }
324    }
325
326    #[test]
327    fn test_arg_method() {
328        let z = Expression::complex(expr!(1), expr!(1));
329        let angle = z.arg();
330
331        match angle {
332            Expression::Function { .. } => {}
333            _ => panic!("Expected function expression for arg"),
334        }
335    }
336
337    #[test]
338    fn test_to_polar_method() {
339        let z = Expression::complex(expr!(3), expr!(4));
340        let (_magnitude, _angle) = z.to_polar();
341    }
342
343    #[test]
344    fn test_from_polar_method() {
345        let magnitude = expr!(5);
346        let angle = expr!(0);
347        let _z = Expression::from_polar(magnitude, angle);
348    }
349
350    #[test]
351    fn test_complex_with_symbols() {
352        let x = expr!(x);
353        let y = expr!(y);
354        let a = expr!(a);
355        let b = expr!(b);
356
357        let z1 = Expression::complex(x.clone(), y.clone());
358        let z2 = Expression::complex(a.clone(), b.clone());
359        let result = z1.complex_add(&z2);
360
361        if let Expression::Complex(data) = result {
362            match (&data.real, &data.imag) {
363                (Expression::Add(real_terms), Expression::Add(imag_terms)) => {
364                    assert_eq!(real_terms.len(), 2);
365                    assert_eq!(imag_terms.len(), 2);
366                }
367                _ => panic!("Expected addition expressions for real and imaginary parts"),
368            }
369        } else {
370            panic!("Expected complex result");
371        }
372    }
373
374    #[test]
375    fn test_simplify_complex() {
376        let z = Expression::complex(expr!(3), expr!(0));
377        let result = Expression::simplify_complex(&z);
378        assert_eq!(result, expr!(3));
379
380        let z = Expression::complex(expr!(0), expr!(4));
381        let result = Expression::simplify_complex(&z);
382        assert_eq!(result, Expression::mul(vec![expr!(4), Expression::i()]));
383    }
384
385    #[test]
386    fn test_complex_zero() {
387        let z = Expression::complex(expr!(0), expr!(0));
388        let real_part = z.real();
389        let imag_part = z.imag();
390        assert_eq!(real_part, expr!(0));
391        assert_eq!(imag_part, expr!(0));
392    }
393
394    #[test]
395    fn test_complex_pure_real() {
396        let z = Expression::complex(expr!(5), expr!(0));
397        assert!(z.is_real());
398        assert!(!z.is_pure_imaginary());
399    }
400
401    #[test]
402    fn test_complex_pure_imaginary() {
403        let z = Expression::complex(expr!(0), expr!(5));
404        assert!(!z.is_real());
405        assert!(z.is_pure_imaginary());
406    }
407
408    #[test]
409    fn test_complex_general() {
410        let z = Expression::complex(expr!(3), expr!(4));
411        assert!(!z.is_real());
412        assert!(z.is_imaginary());
413        assert!(!z.is_pure_imaginary());
414    }
415
416    #[test]
417    fn test_complex_multiplication_zero() {
418        let z1 = Expression::complex(expr!(3), expr!(4));
419        let z2 = Expression::complex(expr!(0), expr!(0));
420        let result = z1.complex_multiply(&z2);
421
422        if let Expression::Complex(data) = result {
423            assert_eq!(data.real, expr!(0));
424            assert_eq!(data.imag, expr!(0));
425        } else {
426            panic!("Expected complex result");
427        }
428    }
429
430    #[test]
431    fn test_complex_addition_negative() {
432        let z1 = Expression::complex(expr!(-2), expr!(-3));
433        let z2 = Expression::complex(expr!(5), expr!(7));
434        let result = z1.complex_add(&z2);
435
436        if let Expression::Complex(data) = result {
437            assert_eq!(data.real, expr!(3));
438            assert_eq!(data.imag, expr!(4));
439        } else {
440            panic!("Expected complex result");
441        }
442    }
443
444    #[test]
445    fn test_conjugate_twice() {
446        let z = Expression::complex(expr!(3), expr!(4));
447        let conjugate = z.conjugate();
448        let double_conjugate = conjugate.conjugate();
449
450        if let Expression::Complex(data) = double_conjugate {
451            assert_eq!(data.real, expr!(3));
452            assert_eq!(data.imag, expr!(4));
453        } else {
454            panic!("Expected complex result");
455        }
456    }
457
458    #[test]
459    fn test_complex_multiply_i() {
460        let z = Expression::complex(expr!(3), expr!(4));
461        let i = Expression::complex(expr!(0), expr!(1));
462        let result = z.complex_multiply(&i);
463
464        if let Expression::Complex(data) = result {
465            assert_eq!(data.real, expr!(-4));
466            assert_eq!(data.imag, expr!(3));
467        } else {
468            panic!("Expected complex result");
469        }
470    }
471
472    #[test]
473    fn test_from_polar_zero_angle() {
474        let magnitude = expr!(5);
475        let angle = expr!(0);
476        let z = Expression::from_polar(magnitude, angle);
477
478        if let Expression::Complex(_) = z {
479        } else {
480            panic!("Expected complex result from polar conversion");
481        }
482    }
483
484    #[test]
485    fn test_complex_real_extraction() {
486        let real = expr!(7);
487        let imag = expr!(-3);
488        let z = Expression::complex(real.clone(), imag.clone());
489
490        assert_eq!(z.real(), real);
491        assert_eq!(z.imag(), imag);
492    }
493
494    #[test]
495    fn test_complex_subtraction_result_zero() {
496        let z1 = Expression::complex(expr!(3), expr!(4));
497        let z2 = Expression::complex(expr!(3), expr!(4));
498        let result = z1.complex_subtract(&z2);
499
500        if let Expression::Complex(data) = result {
501            assert_eq!(data.real, expr!(0));
502            assert_eq!(data.imag, expr!(0));
503        } else {
504            panic!("Expected complex result");
505        }
506    }
507}