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