abels_complex/complex/f64/
rectangular.rs

1use core::fmt;
2use core::ops::*;
3use core::write;
4
5type Polar = crate::complex::polar::ComplexPolar<FT>;
6type Rectangular = crate::complex::rectangular::Complex<FT>;
7type FT = f64;
8
9impl Add<Rectangular> for FT {
10    type Output = Rectangular;
11    fn add(self, z: Self::Output) -> Self::Output {
12        z + self
13    }
14}
15
16impl Sub<Rectangular> for FT {
17    type Output = Rectangular;
18    fn sub(self, z: Self::Output) -> Self::Output {
19        Rectangular::new(self - z.re, -z.im)
20    }
21}
22
23impl Mul<Rectangular> for FT {
24    type Output = Rectangular;
25    fn mul(self, z: Self::Output) -> Self::Output {
26        z * self
27    }
28}
29
30impl Div<Rectangular> for FT {
31    type Output = Rectangular;
32    fn div(self, z: Self::Output) -> Self::Output {
33        self * z.recip()
34    }
35}
36
37impl Rectangular {
38    pub const NEG_ONE: Self = Self::new(-1.0, 0.0);
39    pub const NEG_I: Self = Self::new(0.0, -1.0);
40
41    /// Casts to a glam::Vec2.
42    #[cfg(feature = "glam")]
43    pub fn as_vec2(self) -> glam::Vec2 {
44        glam::vec2(self.re as f32, self.im as f32)
45    }
46
47    /// Casts to a glam::DVec2.
48    #[cfg(feature = "glam")]
49    pub fn as_dvec2(self) -> glam::DVec2 {
50        glam::dvec2(self.re, self.im)
51    }
52}
53
54impl fmt::Display for Rectangular {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        fn fmt_x(f: &mut fmt::Formatter, x: FT, sign_plus: bool) -> fmt::Result {
57            match (f.precision(), sign_plus) {
58                (None, false) => write!(f, "{}", x),
59                (None, true) => write!(f, "{:+}", x),
60                (Some(p), false) => write!(f, "{:.*}", p, x),
61                (Some(p), true) => write!(f, "{:+.*}", p, x),
62            }
63        }
64        match (self.re, self.im, f.sign_plus()) {
65            (re, 0.0, sp) => fmt_x(f, re, sp),
66            (0.0, 1.0, false) => write!(f, "i"),
67            (0.0, 1.0, true) => write!(f, "+i"),
68            (0.0, -1.0, _) => write!(f, "-i"),
69            (0.0, im, sp) => {
70                fmt_x(f, im, sp)?;
71                write!(f, "i")
72            }
73            (re, 1.0, sp) => {
74                fmt_x(f, re, sp)?;
75                write!(f, "+i")
76            }
77            (re, -1.0, sp) => {
78                fmt_x(f, re, sp)?;
79                write!(f, "-i")
80            }
81            (re, im, sp) => {
82                fmt_x(f, re, sp)?;
83                fmt_x(f, im, true)?;
84                write!(f, "i")
85            }
86        }
87    }
88}
89
90#[cfg(feature = "rand")]
91impl rand::distr::Distribution<Rectangular> for rand::distr::StandardUniform {
92    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Rectangular {
93        rng.sample::<Polar, _>(self).to_rectangular()
94    }
95}
96
97#[cfg(feature = "glam")]
98impl From<glam::Vec2> for Rectangular {
99    fn from(v: glam::Vec2) -> Self {
100        Self::new(v.x as FT, v.y as FT)
101    }
102}
103
104#[cfg(feature = "glam")]
105impl From<glam::DVec2> for Rectangular {
106    fn from(v: glam::DVec2) -> Self {
107        Self::new(v.x, v.y)
108    }
109}
110
111#[cfg(feature = "glam")]
112impl From<Rectangular> for glam::Vec2 {
113    fn from(z: Rectangular) -> Self {
114        z.as_vec2()
115    }
116}
117
118#[cfg(feature = "glam")]
119impl From<Rectangular> for glam::DVec2 {
120    fn from(z: Rectangular) -> Self {
121        z.as_dvec2()
122    }
123}
124
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use approx::*;
130    use core::f64::consts::{E, FRAC_PI_2, PI, SQRT_2};
131    use core::iter::Iterator;
132    use rand::{
133        Rng, SeedableRng,
134        distr::{Distribution, StandardUniform},
135        rngs::StdRng,
136    };
137
138    const NUM_SAMPLES: usize = 100;
139
140    fn random_samples<T>() -> impl Iterator<Item = T>
141    where
142        StandardUniform: Distribution<T>,
143    {
144        StdRng::seed_from_u64(21)
145            .sample_iter(StandardUniform)
146            .take(NUM_SAMPLES)
147    }
148
149    #[test]
150    fn addition() {
151        for z0 in random_samples::<Rectangular>() {
152            for z1 in random_samples::<Rectangular>() {
153                let z = z0 + z1;
154                assert_eq!(z.re, z0.re + z1.re);
155                assert_eq!(z.im, z0.im + z1.im);
156
157                let z = z0 + z1.re;
158                assert_eq!(z.re, z0.re + z1.re);
159                assert_eq!(z.im, z0.im);
160
161                let z = z0.re + z1;
162                assert_eq!(z.re, z0.re + z1.re);
163                assert_eq!(z.im, z1.im);
164
165                let mut z = z0;
166                z += z1;
167                assert_eq!(z, z0 + z1);
168
169                let mut z = z0;
170                z += z1.re;
171                assert_eq!(z, z0 + z1.re);
172            }
173            assert_eq!(z0 + Rectangular::ZERO, z0);
174        }
175    }
176
177    #[test]
178    fn subtraction() {
179        for z0 in random_samples::<Rectangular>() {
180            for z1 in random_samples::<Rectangular>() {
181                let z = z0 - z1;
182                assert_eq!(z.re, z0.re - z1.re);
183                assert_eq!(z.im, z0.im - z1.im);
184
185                let z = z0 - z1.re;
186                assert_eq!(z.re, z0.re - z1.re);
187                assert_eq!(z.im, z0.im);
188
189                let z = z0.re - z1;
190                assert_eq!(z.re, z0.re - z1.re);
191                assert_eq!(z.im, -z1.im);
192
193                let mut z = z0;
194                z -= z1;
195                assert_eq!(z, z0 - z1);
196
197                let mut z = z0;
198                z -= z1.re;
199                assert_eq!(z, z0 - z1.re);
200            }
201            assert_eq!(z0 - z0, Rectangular::ZERO);
202            assert_eq!(z0 - Rectangular::ZERO, z0);
203        }
204    }
205
206    #[test]
207    fn multiplication() {
208        for z0 in random_samples::<Rectangular>() {
209            for z1 in random_samples::<Rectangular>() {
210                let z = z0 * z1;
211                assert_ulps_eq!(z.abs(), z0.abs() * z1.abs());
212                assert_ulps_eq!(
213                    z.arg().sin(),
214                    (z0.arg() + z1.arg()).sin(),
215                    epsilon = 4.0 * FT::EPSILON
216                );
217
218                let z = z0 * z1.re;
219                assert_eq!(z.re, z0.re * z1.re);
220                assert_eq!(z.im, z0.im * z1.re);
221
222                let z = z0.re * z1;
223                assert_eq!(z.re, z0.re * z1.re);
224                assert_eq!(z.im, z0.re * z1.im);
225
226                let mut z = z0;
227                z *= z1;
228                assert_eq!(z, z0 * z1);
229
230                let mut z = z0;
231                z *= z1.re;
232                assert_eq!(z, z0 * z1.re);
233            }
234            assert_eq!(z0 * Rectangular::ONE, z0);
235            assert_eq!(z0 * Rectangular::ZERO, Rectangular::ZERO);
236            assert_eq!(z0 * 0.0, Rectangular::ZERO);
237        }
238    }
239
240    #[test]
241    fn division() {
242        for z0 in random_samples::<Rectangular>() {
243            for z1 in random_samples::<Rectangular>() {
244                let z = z0 / z1;
245                assert_relative_eq!(
246                    z.abs(),
247                    z0.abs() / z1.abs(),
248                    max_relative = 3.0 * FT::EPSILON
249                );
250                assert_ulps_eq!(
251                    z.arg().sin(),
252                    (z0.arg() - z1.arg()).sin(),
253                    epsilon = 4.0 * FT::EPSILON
254                );
255
256                let z = z0 / z1.re;
257                assert_eq!(z.re, z0.re / z1.re);
258                assert_eq!(z.im, z0.im / z1.re);
259
260                let z = z0.re / z1;
261                assert_ulps_eq!(z.abs(), z0.re.abs() / z1.abs());
262                assert_ulps_eq!(
263                    z.arg().sin(),
264                    (-z0.re.signum() * z1.arg()).sin(),
265                    epsilon = 2.0 * FT::EPSILON
266                );
267
268                let mut z = z0;
269                z /= z1;
270                assert_eq!(z, z0 / z1);
271
272                let mut z = z0;
273                z /= z1.re;
274                assert_eq!(z, z0 / z1.re);
275            }
276            assert_ulps_eq!(z0 / z0, Rectangular::ONE);
277            assert_eq!(Rectangular::ZERO / z0, Rectangular::ZERO);
278        }
279    }
280
281    #[test]
282    fn negation() {
283        for z in random_samples::<Rectangular>() {
284            assert_eq!(-z, Rectangular::new(-z.re, -z.im));
285        }
286        assert_eq!(-Rectangular::ONE, Rectangular::NEG_ONE);
287        assert_eq!(-Rectangular::I, Rectangular::NEG_I);
288        assert_eq!(-Rectangular::NEG_ONE, Rectangular::ONE);
289        assert_eq!(-Rectangular::NEG_I, Rectangular::I);
290    }
291
292    #[test]
293    fn reciprocal() {
294        for z in random_samples::<Rectangular>() {
295            assert_eq!(z.recip(), 1.0 / z);
296            assert_ulps_eq!(z * z.recip(), Rectangular::ONE);
297        }
298        assert_eq!(Rectangular::ONE.recip(), Rectangular::ONE);
299        assert_eq!(Rectangular::I.recip(), Rectangular::NEG_I);
300        assert_eq!(Rectangular::NEG_ONE.recip(), Rectangular::NEG_ONE);
301        assert_eq!(Rectangular::NEG_I.recip(), Rectangular::I);
302    }
303
304    #[test]
305    fn sqrt() {
306        for z in random_samples::<Rectangular>() {
307            assert_ulps_eq!(z.sqrt().abs(), z.abs().sqrt());
308            assert_ulps_eq!(
309                z.sqrt().arg(),
310                z.arg() / 2.0,
311                epsilon = 1400.0 * FT::EPSILON
312            );
313        }
314        assert_eq!(Rectangular::ONE.sqrt(), Rectangular::ONE);
315        assert_eq!(Rectangular::NEG_ONE.sqrt(), Rectangular::I);
316        assert_eq!(
317            Rectangular::new(0.0, 2.0).sqrt(),
318            Rectangular::new(1.0, 1.0)
319        );
320        assert_eq!(
321            Rectangular::new(0.0, -2.0).sqrt(),
322            Rectangular::new(1.0, -1.0)
323        );
324    }
325
326    #[test]
327    fn abs() {
328        for z in random_samples::<Rectangular>() {
329            assert_ulps_eq!(z.abs_sq(), z.abs() * z.abs());
330            assert_eq!(z.abs_sq(), z.re * z.re + z.im * z.im);
331        }
332        assert_eq!(Rectangular::ONE.abs(), 1.0);
333        assert_eq!(Rectangular::I.abs(), 1.0);
334        assert_eq!(Rectangular::NEG_ONE.abs(), 1.0);
335        assert_eq!(Rectangular::NEG_I.abs(), 1.0);
336        assert_eq!(Rectangular::new(1.0, 1.0).abs(), SQRT_2);
337        assert_eq!(Rectangular::new(-1.0, 1.0).abs(), SQRT_2);
338        assert_eq!(Rectangular::new(-1.0, -1.0).abs(), SQRT_2);
339        assert_eq!(Rectangular::new(1.0, -1.0).abs(), SQRT_2);
340    }
341
342    #[test]
343    fn conjugate() {
344        for z in random_samples::<Rectangular>() {
345            assert_eq!(z.conjugate().re, z.re);
346            assert_eq!(z.conjugate().im, -z.im);
347            assert_eq!(z.conjugate().conjugate(), z);
348        }
349        assert_eq!(Rectangular::ONE.conjugate(), Rectangular::ONE);
350        assert_eq!(Rectangular::I.conjugate(), Rectangular::NEG_I);
351        assert_eq!(Rectangular::NEG_ONE.conjugate(), Rectangular::NEG_ONE);
352        assert_eq!(Rectangular::NEG_I.conjugate(), Rectangular::I);
353    }
354
355    #[test]
356    fn arg() {
357        assert_eq!(Rectangular::ONE.arg(), 0.0);
358        assert_eq!(Rectangular::I.arg(), FRAC_PI_2);
359        assert_eq!(Rectangular::NEG_ONE.arg(), PI);
360        assert_eq!(Rectangular::NEG_I.arg(), -FRAC_PI_2);
361    }
362
363    #[test]
364    fn exp() {
365        for z in random_samples::<Rectangular>() {
366            assert_eq!(z.exp().abs, z.re.exp());
367            assert_eq!(z.exp().arg, z.im);
368            assert_ulps_eq!(z.exp().ln(), z);
369        }
370        assert_eq!(Rectangular::ONE.exp(), Polar::new(E, 0.0));
371        assert_eq!(Rectangular::I.exp(), Polar::new(1.0, 1.0));
372        assert_eq!(Rectangular::NEG_ONE.exp(), Polar::new(E.recip(), 0.0));
373        assert_eq!(Rectangular::NEG_I.exp(), Polar::new(1.0, -1.0));
374    }
375
376    #[test]
377    fn log() {
378        for z in random_samples::<Rectangular>() {
379            assert_eq!(z.ln().re, z.abs().ln());
380            assert_eq!(z.ln().im, z.arg());
381            assert_ulps_eq!(z.ln().exp(), z.to_polar());
382        }
383        assert_eq!(Rectangular::ONE.ln(), Rectangular::ZERO);
384        assert_eq!(Rectangular::I.ln(), Rectangular::I * FRAC_PI_2);
385        assert_eq!(Rectangular::NEG_ONE.ln(), Rectangular::I * PI);
386        assert_eq!(Rectangular::NEG_I.ln(), Rectangular::I * -FRAC_PI_2);
387
388        assert_ulps_eq!(Rectangular::new(E, 0.0).ln(), Rectangular::ONE);
389        assert_ulps_eq!(Rectangular::new(2.0, 0.0).log2(), Rectangular::ONE);
390        assert_ulps_eq!(Rectangular::new(10.0, 0.0).log10(), Rectangular::ONE);
391    }
392
393    #[test]
394    fn powi() {
395        for z in random_samples::<Rectangular>() {
396            assert_eq!(z.powi(0), Polar::ONE);
397            assert_eq!(z.powi(1), z.to_polar());
398            for n in random_samples::<i32>() {
399                assert_eq!(z.powi(n).abs, z.abs().powi(n));
400                assert_eq!(z.powi(n).arg, z.arg() * n as FT);
401            }
402        }
403        for n in random_samples::<i32>() {
404            assert_eq!(Rectangular::ZERO.powi(n.abs()), Polar::ZERO);
405            assert_eq!(Rectangular::ONE.powi(n), Polar::ONE);
406        }
407    }
408
409    #[test]
410    fn powf() {
411        for z in random_samples::<Rectangular>() {
412            assert_eq!(z.powf(0.0), Polar::ONE);
413            assert_eq!(z.powf(1.0), z.to_polar());
414            for n in random_samples::<i32>() {
415                let x = n as FT * 0.01;
416                assert_eq!(z.powf(x).abs, z.abs().powf(x));
417                assert_eq!(z.powf(x).arg, z.arg() * x);
418            }
419        }
420        for n in random_samples::<i32>() {
421            let x = n as FT * 0.01;
422            assert_eq!(Rectangular::ZERO.powf(x.abs()), Polar::ZERO);
423            assert_eq!(Rectangular::ONE.powf(x), Polar::ONE);
424        }
425    }
426}