Skip to main content

abels_complex/complex/f32/
rectangular.rs

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