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 #[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}