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