Skip to main content

dashu_cmplx/
add.rs

1//! Complex addition and subtraction.
2//!
3//! Addition and subtraction are componentwise perfectly-rounded real additions/subtractions on each
4//! part, forwarded through the shared [`impl_cbig_binop!`] macro (same pattern as `Mul` / `Div`).
5
6use crate::cbig::CBig;
7use crate::repr::{combine_parts, CfpResult, Context};
8use core::ops::{Add, AddAssign, Sub, SubAssign};
9use dashu_float::round::Round;
10use dashu_int::Word;
11
12impl<R: Round> Context<R> {
13    /// Add two complex numbers under this context (context layer).
14    ///
15    /// Returns a [`CfpResult`] carrying each part's inexactness. Addition is componentwise, so each
16    /// part is a single correctly-rounded real addition.
17    pub fn add<const B: Word>(&self, z: &CBig<R, B>, w: &CBig<R, B>) -> CfpResult<R, B> {
18        let re = self.float().add(z.re(), w.re())?;
19        let im = self.float().add(z.im(), w.im())?;
20        Ok(combine_parts(re, im))
21    }
22
23    /// Subtract two complex numbers under this context (context layer).
24    ///
25    /// Returns a [`CfpResult`] carrying each part's inexactness. Subtraction is componentwise, so each
26    /// part is a single correctly-rounded real subtraction.
27    pub fn sub<const B: Word>(&self, z: &CBig<R, B>, w: &CBig<R, B>) -> CfpResult<R, B> {
28        let re = self.float().sub(z.re(), w.re())?;
29        let im = self.float().sub(z.im(), w.im())?;
30        Ok(combine_parts(re, im))
31    }
32}
33
34// --- Add: all four ref/val combinations, plus Assign, via the shared macro ---
35crate::helper_macros::impl_cbig_binop!(Add, add, AddAssign, add_assign);
36
37// --- Sub: all four ref/val combinations, plus Assign, via the shared macro ---
38crate::helper_macros::impl_cbig_binop!(Sub, sub, SubAssign, sub_assign);
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use dashu_float::round::mode;
44
45    type C = CBig<mode::HalfAway, 10>;
46
47    #[test]
48    fn add_componentwise() {
49        let z = C::from_parts(3.into(), 4.into());
50        let w = C::from_parts(1.into(), 2.into());
51        let r = &z + &w;
52        assert_eq!(r.re().significand(), &4.into());
53        assert_eq!(r.im().significand(), &6.into());
54    }
55
56    #[test]
57    fn add_all_ref_val_combinations() {
58        let z = C::from_parts(1.into(), 2.into());
59        let w = C::from_parts(3.into(), 4.into());
60        // val + val, val + ref, ref + val, ref + ref
61        assert_eq!((z.clone() + w.clone()).im().significand(), &6.into());
62        assert_eq!((z.clone() + &w).im().significand(), &6.into());
63        assert_eq!((&z + w.clone()).im().significand(), &6.into());
64        assert_eq!((&z + &w).im().significand(), &6.into());
65    }
66
67    #[test]
68    fn add_assign_val_and_ref() {
69        let z = C::from_parts(1.into(), 2.into());
70        let w = C::from_parts(3.into(), 4.into());
71
72        let mut acc = z.clone();
73        acc += w.clone();
74        assert_eq!(acc.re().significand(), &4.into());
75        assert_eq!(acc.im().significand(), &6.into());
76
77        let mut acc = z.clone();
78        acc += &w;
79        assert_eq!(acc.re().significand(), &4.into());
80    }
81
82    #[test]
83    fn sub_componentwise() {
84        let z = C::from_parts(3.into(), 4.into());
85        let w = C::from_parts(1.into(), 2.into());
86        let r = &z - &w;
87        assert_eq!(r.re().significand(), &2.into());
88        assert_eq!(r.im().significand(), &2.into());
89    }
90
91    #[test]
92    fn sub_all_ref_val_combinations() {
93        let z = C::from_parts(5.into(), 6.into());
94        let w = C::from_parts(2.into(), 1.into());
95        assert_eq!((z.clone() - w.clone()).re().significand(), &3.into());
96        assert_eq!((z.clone() - &w).re().significand(), &3.into());
97        assert_eq!((&z - w.clone()).re().significand(), &3.into());
98        assert_eq!((&z - &w).re().significand(), &3.into());
99    }
100
101    #[test]
102    fn z_minus_z_is_zero() {
103        let z = C::from_parts(7.into(), 9.into());
104        let r = &z - &z;
105        assert!(r.is_zero());
106    }
107
108    #[test]
109    fn sub_assign_val_and_ref() {
110        let z = C::from_parts(5.into(), 6.into());
111        let w = C::from_parts(2.into(), 1.into());
112
113        let mut acc = z.clone();
114        acc -= w.clone();
115        assert_eq!(acc.re().significand(), &3.into());
116        assert_eq!(acc.im().significand(), &5.into());
117
118        let mut acc = z.clone();
119        acc -= &w;
120        assert_eq!(acc.re().significand(), &3.into());
121    }
122}