cpx_coords/lib.rs
1// Copyright 2025 En-Jui Chang
2//
3// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>
4// or the MIT license <http://opensource.org/licenses/MIT>, at your option.
5// This file may not be copied, modified, or distributed except according to those terms.
6
7use core::hash::Hash;
8use core::ops::{Add, Div, Mul, Neg, Sub};
9use num_traits::{Float, NumCast};
10
11#[doc(hidden)]
12/// Private module to seal the `f32` or `f64`.
13mod float_sealed {
14 pub trait FloatSealed {}
15 impl FloatSealed for f32 {}
16 impl FloatSealed for f64 {}
17}
18
19/// Extension trait for providing common mathematical constants for floats.
20pub trait FloatExt:
21 float_sealed::FloatSealed + Float + NumCast + Copy + PartialOrd + 'static
22{
23 const ZERO: Self;
24 const ONE: Self;
25 const NEG_ONE: Self;
26 const TWO: Self;
27 const SQRT_2: Self;
28 const FRAC_1_SQRT_2: Self;
29 const PI: Self;
30 const FRAC_1_PI: Self;
31 const FRAC_PI_2: Self;
32 const FRAC_2_PI: Self;
33 const TAU: Self;
34 const E: Self;
35 const FRAC_1_E: Self;
36 const FRAC_2_SQRT_PI: Self;
37 const NEG_FRAC_PI_2: Self;
38 const FRAC_PI_3: Self;
39 const FRAC_PI_4: Self;
40 const FRAC_PI_6: Self;
41 const FRAC_PI_8: Self;
42 const LN_2: Self;
43 const LN_10: Self;
44 const LOG2_10: Self;
45 const LOG2_E: Self;
46 const LOG10_2: Self;
47 const LOG10_E: Self;
48 const EXP_PI: Self;
49 const EXP_NEG_PI: Self;
50 const EXP_FRAC_PI_2: Self;
51 const EXP_NEG_FRAC_PI_2: Self;
52 const EPSILON: Self;
53 /// Returns the threshold below which two floats are considered equal.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use cpx_coords::FloatExt;
59 ///
60 /// assert_eq!(f32::THRESHOLD,10.0 * f32::EPSILON);
61 /// assert_eq!(f64::THRESHOLD,10.0 * f64::EPSILON);
62 /// ```
63 const THRESHOLD: Self;
64 /// Returns the `ln(threshold)`.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use cpx_coords::FloatExt;
70 ///
71 /// assert_eq!(f32::LN_THRESHOLD,-13.6398001_f32);
72 /// assert_eq!(f64::LN_THRESHOLD,-67.4821365922_f64);
73 /// ```
74 const LN_THRESHOLD: Self;
75
76 /// Approximately checks whether two `T` numbers are equal within a given threshold.
77 ///
78 /// # Arguments
79 /// * `a` - First floating-point number.
80 /// * `b` - Second floating-point number.
81 ///
82 /// # Returns
83 /// * `true` if the absolute difference between `a` and `b` is less than or equal to `threshold`, otherwise `false`.
84 ///
85 /// # Examples
86 /// ```
87 /// use cpx_coords::FloatExt;
88 ///
89 /// type F1 = f32;
90 /// let a1: F1 = 1.0;
91 /// let b1: F1 = a1 + 0.9 * F1::THRESHOLD;
92 /// assert!(a1.approx_eq(b1));
93 /// let c1: F1 = a1 + 1.1 * F1::THRESHOLD;
94 /// assert!(!a1.approx_eq(c1));
95 ///
96 /// type F2 = f64;
97 /// let a2: F2 = 1.0;
98 /// let b2: F2 = a2 - 1.0 * F2::THRESHOLD;
99 /// assert!(a2.approx_eq(b2));
100 /// let c2: F2 = a2 + 1.1 * F2::THRESHOLD;
101 /// assert!(!a2.approx_eq(c2));
102 /// ```
103 ///
104 /// # Performance
105 ///
106 /// ```rust
107 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
108 ///
109 /// let batch = 100_000;
110 /// let reps = 50;
111 ///
112 /// type F = f32;
113 /// let a: F = 1.0;
114 /// let b: F = 2.0;
115 ///
116 /// assert!((..=4).contains(&measure_cycles( || { let _ = a.approx_eq(b); }, || { let _ = a; }, batch, reps)));
117 /// ```
118 fn approx_eq(self, other: Self) -> bool;
119
120 /// Wraps a phase angle into the `(-π, π]` interval.
121 ///
122 /// # Examples
123 /// ```
124 /// use cpx_coords::FloatExt;
125 ///
126 /// type F = f32;
127 ///
128 /// assert_eq!(0.0.principal_val(), 0.0);
129 /// assert_eq!(F::PI.principal_val(), F::PI);
130 /// assert_eq!((-F::PI).principal_val(), F::PI); // -π maps to π
131 ///
132 ///
133 /// let x = (F::PI + 0.1).principal_val();
134 /// let expected = -F::PI + 0.1;
135 /// assert!((x - expected).abs() <= F::THRESHOLD);
136 ///
137 /// let y = (-F::PI - 0.1).principal_val();
138 /// let expected = F::PI - 0.1;
139 /// assert!((y - expected).abs() <= F::THRESHOLD);
140 ///
141 /// assert!((3.0 * F::TAU).principal_val().abs() <= F::THRESHOLD);
142 /// ```
143 ///
144 /// ```rust
145 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
146 ///
147 /// let batch = 100_000;
148 /// let reps = 50;
149 ///
150 /// type F = f64;
151 /// let val: F = 3.0;
152 /// // assert!((..=5).contains(&measure_cycles( || { let _ = val.principal_val(); }, || { let _ = val; }, batch, reps)));
153 ///
154 /// let near_val: F = -F::PI;
155 /// // assert!((..=11).contains(&measure_cycles( || { let _ = near_val.principal_val(); }, || { let _ = near_val; }, batch, reps)));
156 ///
157 /// let mid_val: F = 15.0;
158 /// // assert!((..=30).contains(&measure_cycles( || { let _ = mid_val.principal_val(); }, || { let _ = mid_val; }, batch, reps)));
159 ///
160 /// let far_val: F = F::MAX;
161 /// // assert!((..=1850).contains(&measure_cycles( || { let _ = far_val.principal_val(); }, || { let _ = far_val; }, batch, reps)));
162 /// ```
163 #[inline(always)]
164 fn principal_val(self) -> Self {
165 if self > -Self::PI && self <= Self::PI {
166 return self;
167 } else if self == -Self::PI {
168 return Self::PI;
169 }
170 let mut r = self % Self::TAU;
171 if r <= -Self::PI {
172 r = r + Self::TAU
173 } else if r > Self::PI {
174 r = r - Self::TAU
175 }
176 r
177 }
178 /// Returns the maximum of this floating type.
179 const MAX: Self;
180 /// Returns the minimum of this floating type.
181 const MIN: Self;
182 /// Returns the `ln(T::MAX)` of this floating type.
183 const LN_MAX: Self;
184 const INFINITY: Self;
185 const NEG_INFINITY: Self;
186}
187
188impl FloatExt for f32 {
189 const ZERO: Self = 0.0_f32;
190 const ONE: Self = 1.0_f32;
191 const NEG_ONE: Self = -1.0_f32;
192 const TWO: Self = 2.0_f32;
193 const SQRT_2: Self = core::f32::consts::SQRT_2;
194 const FRAC_1_SQRT_2: Self = core::f32::consts::FRAC_1_SQRT_2;
195 const PI: Self = core::f32::consts::PI;
196 const FRAC_1_PI: Self = core::f32::consts::FRAC_1_PI;
197 const FRAC_PI_2: Self = core::f32::consts::FRAC_PI_2;
198 const FRAC_2_PI: Self = core::f32::consts::FRAC_2_PI;
199 const TAU: Self = core::f32::consts::TAU;
200 const E: Self = core::f32::consts::E;
201 const FRAC_1_E: Self = core::f32::consts::E.recip();
202 const FRAC_2_SQRT_PI: Self = core::f32::consts::FRAC_2_SQRT_PI;
203 const NEG_FRAC_PI_2: Self = -core::f32::consts::FRAC_PI_2;
204 const FRAC_PI_3: Self = core::f32::consts::FRAC_PI_3;
205 const FRAC_PI_4: Self = core::f32::consts::FRAC_PI_4;
206 const FRAC_PI_6: Self = core::f32::consts::FRAC_PI_6;
207 const FRAC_PI_8: Self = core::f32::consts::FRAC_PI_8;
208 const LN_2: Self = core::f32::consts::LN_2;
209 const LN_10: Self = core::f32::consts::LN_10;
210 const LOG2_10: Self = core::f32::consts::LOG2_10;
211 const LOG2_E: Self = core::f32::consts::LOG2_E;
212 const LOG10_2: Self = core::f32::consts::LOG10_2;
213 const LOG10_E: Self = core::f32::consts::LOG10_E;
214 const EXP_PI: Self = 23.140696_f32;
215 const EXP_NEG_PI: Self = 0.043213915_f32;
216 const EXP_FRAC_PI_2: Self = 4.8104777_f32;
217 const EXP_NEG_FRAC_PI_2: Self = 0.20787957_f32;
218 const EPSILON: Self = f32::EPSILON;
219 const THRESHOLD: Self = f32::EPSILON * 10.0;
220 const LN_THRESHOLD: Self = -13.639_8_f32;
221 fn approx_eq(self, other: f32) -> bool {
222 let val = self - other;
223 if val > Self::THRESHOLD {
224 false
225 } else {
226 val >= -Self::THRESHOLD
227 }
228 //(self - other).abs() <= Self::THRESHOLD
229 }
230 const MAX: Self = f32::MAX;
231 const MIN: Self = f32::MIN;
232 const LN_MAX: Self = 88.72284_f32;
233 const INFINITY: Self = f32::INFINITY;
234 const NEG_INFINITY: Self = f32::NEG_INFINITY;
235}
236
237impl FloatExt for f64 {
238 const ZERO: Self = 0.0_f64;
239 const ONE: Self = 1.0_f64;
240 const NEG_ONE: Self = -1.0_f64;
241 const TWO: Self = 2.0_f64;
242 const SQRT_2: Self = core::f64::consts::SQRT_2;
243 const FRAC_1_SQRT_2: Self = core::f64::consts::FRAC_1_SQRT_2;
244 const PI: Self = core::f64::consts::PI;
245 const FRAC_1_PI: Self = core::f64::consts::FRAC_1_PI;
246 const FRAC_PI_2: Self = core::f64::consts::FRAC_PI_2;
247 const FRAC_2_PI: Self = core::f64::consts::FRAC_2_PI;
248 const TAU: Self = core::f64::consts::TAU;
249 const E: Self = core::f64::consts::E;
250 const FRAC_1_E: Self = core::f64::consts::E.recip();
251 const FRAC_2_SQRT_PI: Self = core::f64::consts::FRAC_2_SQRT_PI;
252 const NEG_FRAC_PI_2: Self = -core::f64::consts::FRAC_PI_2;
253 const FRAC_PI_3: Self = core::f64::consts::FRAC_PI_3;
254 const FRAC_PI_4: Self = core::f64::consts::FRAC_PI_4;
255 const FRAC_PI_6: Self = core::f64::consts::FRAC_PI_6;
256 const FRAC_PI_8: Self = core::f64::consts::FRAC_PI_8;
257 const LN_2: Self = core::f64::consts::LN_2;
258 const LN_10: Self = core::f64::consts::LN_10;
259 const LOG2_10: Self = core::f64::consts::LOG2_10;
260 const LOG2_E: Self = core::f64::consts::LOG2_E;
261 const LOG10_2: Self = core::f64::consts::LOG10_2;
262 const LOG10_E: Self = core::f64::consts::LOG10_E;
263 const EXP_PI: Self = 23.140692632779267_f64;
264 const EXP_NEG_PI: Self = 0.04321391826377226_f64;
265 const EXP_FRAC_PI_2: Self = 4.810477380965351_f64;
266 const EXP_NEG_FRAC_PI_2: Self = 0.20787957635076193_f64;
267 const EPSILON: Self = f64::EPSILON;
268 const THRESHOLD: Self = f64::EPSILON * 10.0;
269 const LN_THRESHOLD: Self = -67.4821365922_f64;
270 fn approx_eq(self, other: f64) -> bool {
271 let val = self - other;
272 if val > Self::THRESHOLD {
273 false
274 } else {
275 val >= -Self::THRESHOLD
276 }
277 //(self - other).abs() <= Self::THRESHOLD
278 }
279 const MAX: Self = f64::MAX;
280 const MIN: Self = f64::MIN;
281 const LN_MAX: Self = 709.782712893384_f64;
282 const INFINITY: Self = f64::INFINITY;
283 const NEG_INFINITY: Self = f64::NEG_INFINITY;
284}
285
286/// Enum representing complex numbers in various coordinate systems.
287#[derive(Debug, Clone, Copy, Default, Eq)]
288pub enum Cpx<T>
289where
290 T: FloatExt,
291{
292 #[default]
293 /// Represents the additive identity, `0.0`.
294 Zero,
295 /// Represents the multiplicative identity, `1.0`.
296 One,
297 /// Represents `-1.0`.
298 NegOne,
299 /// Represents the imaginary unit, `j * 1.0`, which is one of the square root of `-1.0`.
300 J,
301 /// Represents `j * (-1.0)`.
302 NegJ,
303 /// Represents a real floating number `re`.
304 Re { re: T },
305 /// Represents a imaginary floating number `j * im`.
306 Im { im: T },
307 /// Represents a complex number with the unit radius and a real floating phase angle `ph` in `(-π, π]`, i.e. `cos(ph) + j * sin(ph)`.
308 Ph { ph: T },
309 /// Represents a complex number in Cartesian coordinates `re + j * im`.
310 ReIm { re: T, im: T },
311 /// Represents a complex number in polar coordinates: `rad * exp(j * ph)`.
312 Pl { rad: T, ph: T },
313 /// Represents a complex number `z = exp(re + j * im)` in logarithmic form: `ln(z) = re + j * im`.
314 Ln { re: T, im: T },
315}
316
317/// Canonicalize an axis expression into symbolic constants if possible.
318#[macro_export]
319macro_rules! canonicalize_axis {
320 ($variant:ident, $val:ident, $one:ident, $neg_one:ident) => {
321 match $val {
322 _ if $val == T::ONE => $one,
323 _ if $val == T::ZERO => Cpx::Zero,
324 _ if $val == T::NEG_ONE => $neg_one,
325 _ => $variant { $val },
326 }
327 };
328}
329
330/// Canonicalizes an axis-aligned value into a symbolic constant when possible.
331///
332/// This macro converts `$val` into one of the predefined constants (`$one` or `$neg_one`)
333/// if it matches `T::ONE` or `T::NEG_ONE`; otherwise, it constructs the variant `$variant { $val }`.
334///
335/// Assumes that `$val == T::ZERO` has already been checked and excluded by the caller.
336#[macro_export]
337macro_rules! canonicalize_axis_skip_zero {
338 ($variant:ident, $val:ident, $one:ident, $neg_one:ident) => {
339 match $val {
340 _ if $val == T::ONE => $one,
341 _ if $val == T::NEG_ONE => $neg_one,
342 _ => $variant { $val },
343 }
344 };
345}
346
347/// Canonicalize a phase expression into symbolic constants if possible.
348#[macro_export]
349macro_rules! canonicalize_phase {
350 ($ph_expr:expr) => {
351 match ($ph_expr).principal_val() {
352 ph if ph == T::PI => Cpx::NegOne,
353 ph if ph == T::FRAC_PI_2 => Cpx::J,
354 ph if ph == T::ZERO => Cpx::One,
355 ph if ph == T::NEG_FRAC_PI_2 => Cpx::NegJ,
356 ph => Cpx::Ph { ph },
357 }
358 };
359}
360#[macro_export]
361macro_rules! canonicalize_polar {
362 ($rad:expr, $ph:expr) => {
363 match $rad {
364 r if r == T::ZERO => Cpx::Zero,
365 r if r == T::ONE => canonicalize_phase!($ph),
366 r if r > T::ZERO => match $ph.principal_val() {
367 ph if ph == T::PI => Cpx::Re { re: -r },
368 ph if ph == T::FRAC_PI_2 => Cpx::Im { im: r },
369 ph if ph == T::ZERO => Cpx::Re { re: r },
370 ph if ph == T::NEG_FRAC_PI_2 => Cpx::Im { im: -r },
371 ph => Cpx::Pl { rad: r, ph },
372 },
373 r => match ($ph + T::PI).principal_val() {
374 ph if ph == T::PI => Cpx::Re { re: r },
375 ph if ph == T::FRAC_PI_2 => Cpx::Im { im: -r },
376 ph if ph == T::ZERO => Cpx::Re { re: -r },
377 ph if ph == T::NEG_FRAC_PI_2 => Cpx::Im { im: r },
378 ph => Cpx::Pl { rad: -r, ph },
379 },
380 }
381 };
382}
383
384impl<T: FloatExt> Cpx<T> {
385 /// Returns the real part of the complex number as T.
386 ///
387 /// # Example
388 /// ```rust
389 /// use cpx_coords::{Cpx,FloatExt};
390 /// use Cpx::*;
391 ///
392 /// type F = f32;
393 /// let (val,val_2): (F,F) = (3.0,4.0);
394 ///
395 /// assert_eq!((Zero::<F>.re(), One::<F>.re(), NegOne::<F>.re(), J::<F>.re(), NegJ::<F>.re(), Im { im: val }.re()), (0.0, 1.0, -1.0, 0.0, 0.0, 0.0));
396 /// assert_eq!((Re { re: val }.re(), ReIm { re: val, im: val_2 }.re()),(val,val));
397 /// assert_eq!((Ph { ph: val_2 }.re(), Pl { rad: val, ph: val_2 }.re(), Ln { re: val, im: val_2 }.re()),(val_2.cos(), val * val_2.cos(), val.exp() * val_2.cos()));
398 /// ```
399 ///
400 /// # Performance
401 ///
402 /// ```rust
403 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
404 ///
405 /// let batch = 100_000;
406 /// let reps = 50;
407 ///
408 /// type F = f64;
409 /// type C = Cpx<F>;
410 ///
411 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
412 ///
413 /// let (val,val_2): (F,F) = (1.0,4.0);
414 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
415 ///
416 /// let phase: F = F::FRAC_PI_3;
417 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
418 ///
419 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.re(); }, || { let _ = zero; }, batch, reps)));
420 /// // assert!((..=2).contains(&measure_cycles( || { let _ = re.re(); }, || { let _ = re; }, batch, reps)));
421 /// // assert!((..=2).contains(&measure_cycles( || { let _ = re_im.re(); }, || { let _ = re_im; }, batch, reps)));
422 /// // assert!((..=42).contains(&measure_cycles( || { let _ = ph.re(); }, || { let _ = ph; }, batch, reps)));
423 /// // assert!((..=44).contains(&measure_cycles( || { let _ = pl.re(); }, || { let _ = pl; }, batch, reps)));
424 /// // assert!((..=66).contains(&measure_cycles( || { let _ = ln.re(); }, || { let _ = ln; }, batch, reps)));
425 /// ```
426 #[inline(always)]
427 pub fn re(&self) -> T {
428 use Cpx::*;
429 match self {
430 Zero | J | NegJ | Im { .. } => T::ZERO,
431 One => T::ONE,
432 NegOne => T::NEG_ONE,
433 Re { re } | ReIm { re, .. } => *re,
434 Ph { ph } => match *ph {
435 x if (x == T::FRAC_PI_2) || (x == T::NEG_FRAC_PI_2) => T::ZERO, // (x == T::FRAC_PI_2) || (x == T::NEG_FRAC_PI_2) => T::ZERO,
436 x if x == T::ZERO => T::ONE,
437 x if x == T::PI => T::NEG_ONE,
438 x => x.cos(),
439 },
440 Pl { rad, ph } => match *ph {
441 x if (x == T::FRAC_PI_2) || (x == T::NEG_FRAC_PI_2) => T::ZERO,
442 x if x == T::ZERO => *rad,
443 x if x == T::PI => -*rad,
444 x => x.cos() * *rad,
445 },
446 Ln { re, im } => match *re {
447 r if (r < T::LN_THRESHOLD)
448 || (*im == T::FRAC_PI_2)
449 || (*im == T::NEG_FRAC_PI_2) =>
450 {
451 T::ZERO
452 }
453 r if r >= T::LN_MAX => match im.principal_val() {
454 i if (i < T::NEG_FRAC_PI_2) || (i > T::FRAC_PI_2) => T::NEG_INFINITY,
455 i if (i == T::NEG_FRAC_PI_2) || (i == T::FRAC_PI_2) => T::ZERO,
456 _ => T::INFINITY,
457 },
458 r => {
459 let rad = r.exp();
460 match *im {
461 i if i == T::ZERO => rad,
462 i if i == T::PI => -rad,
463 i => i.cos() * rad,
464 }
465 }
466 },
467 }
468 }
469
470 /// Returns the imaginary part of the complex number as T.
471 ///
472 /// # Example
473 /// ```rust
474 /// use cpx_coords::{Cpx,FloatExt};
475 /// use Cpx::*;
476 ///
477 /// type F = f64;
478 /// let val_1: F = 3.0;
479 /// let val_2: F = 4.0;
480 ///
481 /// assert_eq!((Zero::<F>.im(), One::<F>.im(), NegOne::<F>.im(), J::<F>.im(), NegJ::<F>.im(), Im { im: val_1 }.im()), (0.0, 0.0, 0.0, 1.0, -1.0, val_1));
482 /// assert_eq!((Re { re: val_1 }.im(), ReIm { re: val_1, im: val_2 }.im()),(0.0,val_2));
483 /// assert_eq!((Ph { ph: val_2 }.im(), Pl { rad: val_1, ph: val_2 }.im(), Ln { re: val_1, im: val_2 }.im()),(val_2.sin(), val_1 * val_2.sin(), val_1.exp() * val_2.sin()));
484 /// ```
485 ///
486 /// # Performance
487 ///
488 /// ```rust
489 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
490 ///
491 /// let batch = 100_000;
492 /// let reps = 50;
493 ///
494 /// type F = f64;
495 /// type C = Cpx<F>;
496 ///
497 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
498 ///
499 /// let (val,val_2): (F,F) = (3.0,4.0);
500 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
501 ///
502 /// let phase: F = F::FRAC_PI_3;
503 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
504 ///
505 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.im(); }, || { let _ = zero; }, batch, reps)));
506 /// // assert!((..=2).contains(&measure_cycles( || { let _ = im.im(); }, || { let _ = im; }, batch, reps)));
507 /// // assert!((..=2).contains(&measure_cycles( || { let _ = re_im.im(); }, || { let _ = re_im; }, batch, reps)));
508 /// // assert!((..=38).contains(&measure_cycles( || { let _ = ph.im(); }, || { let _ = ph; }, batch, reps)));
509 /// // assert!((..=42).contains(&measure_cycles( || { let _ = pl.im(); }, || { let _ = pl; }, batch, reps)));
510 /// // assert!((..=65).contains(&measure_cycles( || { let _ = ln.im(); }, || { let _ = ln; }, batch, reps)));
511 /// ```
512 #[inline(always)]
513 pub fn im(&self) -> T {
514 use Cpx::*;
515 match self {
516 Zero | One | NegOne | Re { .. } => T::ZERO,
517 J => T::ONE,
518 NegJ => T::NEG_ONE,
519 Im { im } | ReIm { im, .. } => *im,
520 Ph { ph } => match *ph {
521 x if (x == T::ZERO) || (x == T::PI) => T::ZERO,
522 x if x == T::FRAC_PI_2 => T::ONE,
523 x if x == T::NEG_FRAC_PI_2 => T::NEG_ONE,
524 x => x.sin(),
525 },
526 Pl { rad, ph } => match *ph {
527 x if (x == T::ZERO) || (x == T::PI) => T::ZERO,
528 x if x == T::FRAC_PI_2 => *rad,
529 x if x == T::NEG_FRAC_PI_2 => -*rad,
530 x => x.sin() * *rad,
531 },
532 Ln { re, im } => match *re {
533 r if (r < T::LN_THRESHOLD) || (*im == T::ZERO) || (*im == T::PI) => T::ZERO,
534 r if r >= T::LN_MAX => match im.principal_val() {
535 i if (i == T::ZERO) || (i == T::PI) => T::ZERO,
536 i if i < T::ZERO => T::NEG_INFINITY,
537 _ => T::INFINITY,
538 },
539 r => {
540 let rad = r.exp();
541 match *im {
542 i if i == T::FRAC_PI_2 => rad,
543 i if i == T::NEG_FRAC_PI_2 => -rad,
544 i => i.sin() * rad,
545 }
546 }
547 },
548 }
549 }
550
551 /// Returns the real and imaginary components of the complex number as a tuple `(re, im)`.
552 ///
553 /// Both components are of type `T`. This method is equivalent to calling `.re()` and `.im()` separately,
554 /// but returns the result in a single call for performance benefits.
555 ///
556 ///
557 /// # Example
558 /// ```rust
559 /// use cpx_coords::{Cpx, FloatExt};
560 /// use Cpx::*;
561 ///
562 /// type F = f64;
563 /// let val_1: F = 3.0;
564 /// let val_2: F = 4.0;
565 ///
566 /// assert_eq!((Zero::<F>.re_im(), One::<F>.re_im(), NegOne::<F>.re_im(), J::<F>.re_im(), NegJ::<F>.re_im()),((0.0, 0.0), (1.0, 0.0), (-1.0, 0.0), (0.0, 1.0), (0.0, -1.0)));
567 /// assert_eq!((Re { re: val_1 }.re_im(), Im { im: val_2 }.re_im(), ReIm { re: val_1, im: val_2 }.re_im()),((val_1, 0.0), (0.0, val_2), (val_1, val_2)));
568 ///
569 /// let ph = F::FRAC_PI_3;
570 /// assert_eq!((Ph { ph }.re_im(), Ln { re: val_1, im: ph }.re_im(), Pl { rad: val_1, ph }.re_im()),((ph.cos(),ph.sin()), (val_1.exp() * ph.cos(), val_1.exp() * ph.sin()), (val_1 * ph.cos(), val_1 * ph.sin())));
571 /// ```
572 ///
573 /// # Performance
574 ///
575 /// ```rust
576 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
577 ///
578 /// let batch = 100_000;
579 /// let reps = 50;
580 ///
581 /// type F = f64;
582 /// type C = Cpx<F>;
583 ///
584 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
585 ///
586 /// let (val,val_2): (F,F) = (3.0,4.0);
587 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
588 ///
589 /// let phase: F = F::FRAC_PI_6;
590 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
591 ///
592 /// // Improvement compare to use individual `.re()` and `.im()`.
593 ///
594 /// // Minor improvement:
595 /// // assert!((1..).contains(&measure_cycles( || { let _ = (zero.re(), zero.im()); }, || { let _ = zero.re_im(); }, batch, reps)));
596 /// // assert!((2..).contains(&measure_cycles( || { let _ = (re.re(), re.im()); }, || { let _ = re.re_im(); }, batch, reps)));
597 /// // assert!((3..).contains(&measure_cycles( || { let _ = (re_im.re(), re_im.im()); }, || { let _ = re_im.re_im(); }, batch, reps)));
598 ///
599 /// // Moderate improvement:
600 /// // Computing `.cos()` and `.sin()` within the same match arm avoids redundant branching and separate match evaluations.
601 /// // assert!((18..).contains(&measure_cycles( || { let _ = (ph.re(), ph.im()); }, || { let _ = ph.re_im(); }, batch, reps)));
602 /// // assert!((18..).contains(&measure_cycles( || { let _ = (pl.re(), pl.im()); }, || { let _ = pl.re_im(); }, batch, reps)));
603 ///
604 /// // Higher improvement:
605 /// // Same reason as above, with the additional benefit of avoiding two `.exp()` computations.
606 /// // assert!((27..).contains(&measure_cycles( || { let _ = (ln.re(), ln.im()); }, || { let _ = ln.re_im(); }, batch, reps)));
607 /// ```
608 #[inline(always)]
609 pub fn re_im(self) -> (T, T) {
610 use Cpx::*;
611 match self {
612 Zero => (T::ZERO, T::ZERO),
613 One => (T::ONE, T::ZERO),
614 NegOne => (T::NEG_ONE, T::ZERO),
615 J => (T::ZERO, T::ONE),
616 NegJ => (T::ZERO, T::NEG_ONE),
617 Re { re } => (re, T::ZERO),
618 Im { im } => (T::ZERO, im),
619 ReIm { re, im } => (re, im),
620 //Ph { ph } | Pl { ph, .. } | Ln { im: ph, .. } => {
621 // let (cos, sin) = (ph.cos(), ph.sin());
622 // match self {
623 // Ph { .. } => (cos, sin),
624 // Pl { rad, .. } => (cos * rad, sin * rad),
625 // Ln { re, .. } => {
626 // let rad = re.exp();
627 // (cos * rad, sin * rad)
628 // }
629 // _ => unreachable!(),
630 // }
631 //}
632 // This expanded version outperforms the simpler implementation. For special cases, early returns
633 // eliminate unnecessary function calls, reducing overhead significantly.
634 // Even in the general case, where `.cos()`, `.sin()`, and `.exp()` are computed, the performance
635 // improves (e.g., ~75 CPU cycles down to ~45).
636 // The main difference is avoiding the nested match, which itself costs only 1–2 cycles, so the
637 // speedup is surprising. Likely, the compiler achieves better instruction-level parallelism (ILP)
638 // and branch prediction with this structure.
639 // Further investigation using LLVM IR or assembly analysis might confirm this hypothesis.
640 Ph { ph } => match ph {
641 x if x == T::ZERO => (T::ONE, T::ZERO),
642 x if x == T::PI => (T::NEG_ONE, T::ZERO),
643 x if x == T::FRAC_PI_2 => (T::ZERO, T::ONE),
644 x if x == T::NEG_FRAC_PI_2 => (T::ZERO, T::NEG_ONE),
645 _ => (ph.cos(), ph.sin()),
646 },
647 Pl { rad, ph } => match ph {
648 x if x == T::ZERO => (rad, T::ZERO),
649 x if x == T::PI => (-rad, T::ZERO),
650 x if x == T::FRAC_PI_2 => (T::ZERO, rad),
651 x if x == T::NEG_FRAC_PI_2 => (T::ZERO, -rad),
652 _ => (rad * ph.cos(), rad * ph.sin()),
653 },
654 Ln { re, im } => match re {
655 r if (r < T::LN_THRESHOLD) || (im == T::ZERO) || (im == T::PI) => {
656 (T::ZERO, T::ZERO)
657 }
658 r if r >= T::LN_MAX => match im.principal_val() {
659 i if i == T::ZERO => (T::INFINITY, T::ZERO),
660 i if i == T::PI => (T::NEG_INFINITY, T::ZERO),
661 i if i == T::FRAC_PI_2 => (T::ZERO, T::INFINITY),
662 i if i == T::NEG_FRAC_PI_2 => (T::ZERO, T::NEG_INFINITY),
663 i if (i > -T::PI) && (i < T::NEG_FRAC_PI_2) => {
664 (T::NEG_INFINITY, T::NEG_INFINITY)
665 }
666 i if (i > T::NEG_FRAC_PI_2) && (i < T::ZERO) => (T::INFINITY, T::NEG_INFINITY),
667 i if (i > T::ZERO) && (i < T::FRAC_PI_2) => (T::INFINITY, T::INFINITY),
668 _ => (T::NEG_INFINITY, T::INFINITY),
669 },
670 r => {
671 let rad = r.exp();
672 match im {
673 i if i == T::ZERO => (rad, T::ZERO),
674 i if i == T::PI => (-rad, T::ZERO),
675 i if i == T::FRAC_PI_2 => (T::ZERO, rad),
676 i if i == T::NEG_FRAC_PI_2 => (T::ZERO, -rad),
677 i => (i.cos() * rad, i.sin() * rad),
678 }
679 }
680 },
681 }
682 }
683
684 /// Converts this complex number into its canonical rectangular (real–imaginary) form.
685 ///
686 /// # Performance
687 ///
688 /// ```rust
689 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
690 ///
691 /// let batch = 100_000;
692 /// let reps = 50;
693 ///
694 /// type F = f64;
695 /// type C = Cpx<F>;
696 ///
697 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
698 ///
699 /// let (val,val_2): (F,F) = (3.0,4.0);
700 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
701 ///
702 /// let phase: F = F::FRAC_PI_6;
703 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
704 ///
705 /// // assert!((..=2).contains(&measure_cycles( || { let _ = zero.to_re_im(); }, || { let _ = zero; }, batch, reps)));
706 /// // assert!((..=4).contains(&measure_cycles( || { let _ = re.to_re_im(); }, || { let _ = re; }, batch, reps)));
707 /// // assert!((..=4).contains(&measure_cycles( || { let _ = re_im.to_re_im(); }, || { let _ = re_im; }, batch, reps)));
708 /// // assert!((..=58).contains(&measure_cycles( || { let _ = ph.to_re_im(); }, || { let _ = ph; }, batch, reps)));
709 /// // assert!((..=63).contains(&measure_cycles( || { let _ = pl.to_re_im(); }, || { let _ = pl; }, batch, reps)));
710 /// // assert!((..=95).contains(&measure_cycles( || { let _ = ln.to_re_im(); }, || { let _ = ln; }, batch, reps)));
711 /// ```
712 #[inline(always)]
713 pub fn to_re_im(self) -> Self {
714 use Cpx::*;
715 let (re, im) = self.re_im();
716 ReIm { re, im }
717 }
718
719 /// Canonicalizes and simplifies a `Cpx<T>` to its rectangular (real–imaginary) form if possible.
720 /// This method normalizes the representation of the complex number to one of the canonical forms:
721 /// - `Zero`, `One`, `NegOne`, `J`, `NegJ`
722 /// - `Re { re }`
723 /// - `Im { im }`
724 /// - `ReIm { re, im }`
725 ///
726 /// # Performance Considerations
727 /// - [`Cpx::canonicalize_re_im()`] is generally **more expensive** (~10 CPU cycles) than [`Cpx::to_re_im()`]
728 /// because it performs extra checks and canonicalization.
729 /// - For simple equality checks, [`Cpx::to_re_im()`] is usually sufficient.
730 /// - For **addition**, [`Cpx::to_re_im()`] typically provides normalized operands and is preferred in most cases.
731 /// - Use [`Cpx::canonicalize_re_im()`] only when canonical variants (`Zero`, `One`, `NegOne`, `J`, `NegJ`,
732 /// `Re { re }`, `Im { im }`) are highly desirable.
733 /// - Example: when summing many complex numbers, grouping pure real and imaginary parts first
734 /// allows extremely fast aggregation, followed by combining the remaining components.
735 /// - Another example: in non-performance-critical cases, such as rendering complex numbers as
736 /// LaTeX strings, canonicalization can simplify formatting.
737 ///
738 /// # Performance
739 ///
740 /// ```rust
741 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
742 ///
743 /// let batch = 100_000;
744 /// let reps = 50;
745 ///
746 /// type F = f64;
747 /// type C = Cpx<F>;
748 ///
749 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
750 ///
751 /// let (val,val_2): (F,F) = (3.0,4.0);
752 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
753 ///
754 /// let phase: F = F::FRAC_PI_6;
755 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
756 ///
757 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.canonicalize_re_im(); }, || { let _ = zero; }, batch, reps)));
758 /// // assert!((..=14).contains(&measure_cycles( || { let _ = re.canonicalize_re_im(); }, || { let _ = re; }, batch, reps)));
759 /// // assert!((..=10).contains(&measure_cycles( || { let _ = re_im.canonicalize_re_im(); }, || { let _ = re_im; }, batch, reps)));
760 /// // assert!((..=71).contains(&measure_cycles( || { let _ = ph.canonicalize_re_im(); }, || { let _ = ph; }, batch, reps)));
761 /// // assert!((..=79).contains(&measure_cycles( || { let _ = pl.canonicalize_re_im(); }, || { let _ = pl; }, batch, reps)));
762 /// // assert!((..=104).contains(&measure_cycles( || { let _ = ln.canonicalize_re_im(); }, || { let _ = ln; }, batch, reps)));
763 /// ```
764 #[inline(always)]
765 pub fn canonicalize_re_im(self) -> Self {
766 use Cpx::*;
767 match self {
768 Zero | One | NegOne | J | NegJ => self,
769 Re { re } => canonicalize_axis!(Re, re, One, NegOne),
770 Im { im } => canonicalize_axis!(Im, im, J, NegJ),
771 ReIm { re, im } => {
772 if im == T::ZERO {
773 canonicalize_axis!(Re, re, One, NegOne)
774 } else if re == T::ZERO {
775 canonicalize_axis_skip_zero!(Im, im, J, NegJ)
776 } else {
777 self
778 }
779 }
780 _ => {
781 let (re, im) = self.re_im();
782 if im == T::ZERO {
783 canonicalize_axis!(Re, re, One, NegOne)
784 } else if re == T::ZERO {
785 canonicalize_axis_skip_zero!(Im, im, J, NegJ)
786 } else {
787 self
788 }
789 }
790 }
791 }
792
793 /// Returns the radius (magnitude) of the complex number as T.
794 ///
795 /// For rectangular coordinates (`ReIm { re, im }`), this computes
796 /// `sqrt(re² + im²)` using [`f32::hypot`] or [`f64::hypot`], which minimizes intermediate
797 /// overflow/underflow. However, if both `re` and `im` are near `T::MAX`,
798 /// the result will exceed `T::MAX` and return positive infinity (`∞`).
799 ///
800 /// # Example
801 /// ```rust
802 /// use cpx_coords::{Cpx,FloatExt};
803 /// use Cpx::*;
804 ///
805 /// type F = f64;
806 ///
807 /// let ph = F::FRAC_PI_3;
808 /// assert_eq!((Zero::<F>.rad(), One::<F>.rad(), NegOne::<F>.rad(), J::<F>.rad(), NegJ::<F>.rad(), Ph::<F> { ph }.rad()),(0.0, 1.0, 1.0, 1.0, 1.0, 1.0));
809 ///
810 /// let (val, val_2): (F,F) = (-3.0, 4.0);
811 /// assert_eq!((Re { re: val }.rad(), Im { im: val }.rad(), ReIm { re: val, im: val_2 }.rad()),(val.abs(), val.abs(), val.hypot(val_2)));
812 ///
813 /// let rad: F = 2.0;
814 /// assert_eq!((Ln { re: rad.ln(), im: ph }.rad(), Pl { rad, ph }.rad()),(rad, rad));
815 /// ```
816 ///
817 /// # Performance
818 ///
819 /// ```rust
820 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
821 ///
822 /// let batch = 100_000;
823 /// let reps = 50;
824 ///
825 /// type F = f64;
826 /// type C = Cpx<F>;
827 ///
828 /// let (mut zero, mut one, mut neg_one, mut j, mut neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
829 ///
830 /// let (val,val_2): (F,F) = (3.0,4.0);
831 /// let (mut re, mut im, mut re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
832 ///
833 /// let phase: F = F::FRAC_PI_6;
834 /// let (mut ph, mut pl, mut ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
835 ///
836 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.rad(); }, || { let _ = zero; }, batch, reps)));
837 /// // assert!((..=1).contains(&measure_cycles( || { let _ = ph.rad(); }, || { let _ = ph; }, batch, reps)));
838 /// // assert!((..=1).contains(&measure_cycles( || { let _ = pl.rad(); }, || { let _ = pl; }, batch, reps)));
839 /// // assert!((..=3).contains(&measure_cycles( || { let _ = re.rad(); }, || { let _ = re; }, batch, reps)));
840 /// // assert!((..=17).contains(&measure_cycles( || { let _ = re_im.rad(); }, || { let _ = re_im; }, batch, reps)));
841 /// assert!((..=23).contains(&measure_cycles( || { let _ = ln.rad(); }, || { let _ = ln; }, batch, reps)));
842 /// ```
843 #[inline(always)]
844 pub fn rad(&self) -> T {
845 use Cpx::*;
846 match self {
847 Zero => T::ZERO,
848 One | NegOne | J | NegJ | Ph { .. } => T::ONE,
849 Pl { rad, .. } => *rad,
850 Re { re } | Im { im: re } => {
851 if *re >= T::ZERO {
852 *re
853 } else {
854 -*re
855 }
856 }
857 ReIm { re, im } => re.hypot(*im),
858 Ln { re, .. } => match *re {
859 x if x < T::LN_THRESHOLD => T::ZERO,
860 x if x > T::LN_MAX => T::INFINITY,
861 x => x.exp(),
862 },
863 }
864 }
865
866 /// Returns the phase angle of the complex number as `T`.
867 ///
868 /// # Example
869 /// ```rust
870 /// use cpx_coords::{Cpx,FloatExt};
871 /// use Cpx::*;
872 ///
873 /// type F = f64;
874 ///
875 /// //assert_eq!((Zero::<F>.ph(), One::<F>.ph(), NegOne::<F>.ph(), J::<F>.ph(), NegJ::<F>.ph()), (0.0, 0.0, F::PI, F::FRAC_PI_2, F::NEG_FRAC_PI_2));
876 ///
877 /// let pos_val: F = 3.0;
878 /// //assert_eq!((Re { re: pos_val }.ph(), Re { re: -pos_val }.ph(), Im { im: pos_val }.ph(), Im { im: -pos_val }.ph()), (0.0, F::PI, F::FRAC_PI_2, F::NEG_FRAC_PI_2));
879 ///
880 /// let ph: F = F::FRAC_PI_6.principal_val();
881 /// //assert_eq!((Ph { ph }.ph(), Pl { rad: pos_val, ph }.ph(), Ln { re: pos_val, im: ph }.ph()), (ph, ph, ph));
882 ///
883 /// let (re, im): (F, F) = (3.0, 4.0);
884 /// //assert_eq!(ReIm { re, im }.ph(), im.atan2(re));
885 /// ```
886 ///
887 /// # Performance
888 ///
889 /// ```rust
890 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
891 ///
892 /// let batch = 100_000;
893 /// let reps = 50;
894 ///
895 /// type F = f64;
896 /// type C = Cpx<F>;
897 ///
898 /// let (mut zero, mut one, mut neg_one, mut j, mut neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
899 ///
900 /// let (val,val_2): (F,F) = (3.0,4.0);
901 /// let (mut re, mut im, mut re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
902 ///
903 /// let phase: F = F::FRAC_PI_2;
904 /// let (mut ph, mut pl, mut ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
905 ///
906 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.ph(); }, || { let _ = zero; }, batch, reps)));
907 /// // assert!((..=2).contains(&measure_cycles( || { let _ = re.ph(); }, || { let _ = re; }, batch, reps)));
908 /// // assert!((..=6).contains(&measure_cycles( || { let _ = ph.ph(); }, || { let _ = ph; }, batch, reps)));
909 /// // assert!((..=7).contains(&measure_cycles( || { let _ = pl.ph(); }, || { let _ = pl; }, batch, reps)));
910 /// // assert!((..=7).contains(&measure_cycles( || { let _ = ln.ph(); }, || { let _ = ln; }, batch, reps)));
911 /// // assert!((..=33).contains(&measure_cycles( || { let _ = re_im.ph(); }, || { let _ = re_im; }, batch, reps)));
912 /// ```
913 #[inline(always)]
914 pub fn ph(&self) -> T {
915 use Cpx::*;
916 match self {
917 Zero | One => T::ZERO,
918 J => T::FRAC_PI_2,
919 NegOne => T::PI,
920 NegJ => T::NEG_FRAC_PI_2,
921 Re { re } => match *re {
922 r if r >= T::ZERO => T::ZERO,
923 _ => T::PI,
924 },
925 Im { im } => match *im {
926 i if i < T::ZERO => T::NEG_FRAC_PI_2,
927 i if i > T::ZERO => T::FRAC_PI_2,
928 _ => T::ZERO,
929 },
930 Ph { ph } => ph.principal_val(),
931 Pl { rad, ph } => match *rad {
932 rad if rad > T::ZERO => ph.principal_val(),
933 _ => T::ZERO,
934 },
935 Ln { re, im: ph } => match *re {
936 r if r >= T::LN_THRESHOLD => ph.principal_val(),
937 _ => T::ZERO,
938 },
939 ReIm { re, im } => im.atan2(*re),
940 }
941 }
942
943 /// Returns the polar coordinates `(r, φ)` of the complex number, where:
944 ///
945 /// * `r` is the radius (magnitude)
946 /// * `φ` is the phase angle in radians
947 ///
948 /// Both values are returned as type `T`.
949 ///
950 /// For rectangular coordinates (`ReIm { re, im }`), the radius is computed as
951 /// `sqrt(re² + im²)` using [`f32::hypot`] or [`f64::hypot`], which minimizes intermediate
952 /// overflow and underflow. However, if both `re` and `im` are close to
953 /// [`FloatExt::MAX`], the result will exceed the representable range and return
954 /// positive infinity (`∞`).
955 ///
956 /// The phase is normalized using the [`FloatExt::principal_val()`] form to ensure a
957 /// well-defined principal value in the range `(-π, π]`.
958 ///
959 /// # Example
960 /// ```rust
961 /// use cpx_coords::{Cpx, FloatExt};
962 /// use Cpx::*;
963 ///
964 /// type F = f64;
965 ///
966 /// assert_eq!((Zero::<F>.rad_ph(), One::<F>.rad_ph(), NegOne::<F>.rad_ph(), J::<F>.rad_ph(), NegJ::<F>.rad_ph()), ((0.0, 0.0), (1.0, 0.0), (1.0, F::PI), (1.0, F::FRAC_PI_2), (1.0, F::NEG_FRAC_PI_2)));
967 ///
968 /// let pos_val: F = 3.0;
969 /// assert_eq!((Re { re: pos_val }.rad_ph(), Re { re: -pos_val }.rad_ph(), Im { im: pos_val }.rad_ph(), Im { im: -pos_val }.rad_ph()), ((pos_val, 0.0), (pos_val, F::PI), (pos_val, F::FRAC_PI_2), (pos_val, F::NEG_FRAC_PI_2)));
970 ///
971 /// let (rad, ph): (F, F) = (4.0, F::FRAC_PI_6.principal_val());
972 /// assert_eq!((Ph { ph }.rad_ph(), Pl { rad, ph }.rad_ph(), Ln { re: rad.ln(), im: ph }.rad_ph()), ((1.0, ph), (rad, ph), (rad, ph)));
973 ///
974 /// let (re, im): (F, F) = (3.0, 4.0);
975 /// assert_eq!(ReIm { re, im }.rad_ph(), (re.hypot(im), im.atan2(re)));
976 /// ```
977 ///
978 /// # Performance
979 ///
980 /// ```rust
981 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
982 ///
983 /// let batch = 100_000;
984 /// let reps = 50;
985 ///
986 /// type F = f64;
987 /// type C = Cpx<F>;
988 ///
989 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
990 ///
991 /// let (val,val_2): (F,F) = (3.0,4.0);
992 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
993 ///
994 /// let phase: F = F::FRAC_PI_6;
995 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
996 ///
997 /// // The CPU cycles improved comparing to separate calls.
998 /// // assert!((2..).contains(&measure_cycles( || { let _ = (zero.rad(), zero.ph()); }, || { let _ = zero.rad_ph(); }, batch, reps)));
999 /// // assert!((1..).contains(&measure_cycles( || { let _ = (ph.rad(), ph.ph()); }, || { let _ = ph.rad_ph(); }, batch, reps)));
1000 /// // assert!((0..).contains(&measure_cycles( || { let _ = (pl.rad(), pl.ph()); }, || { let _ = pl.rad_ph(); }, batch, reps)));
1001 /// // assert!((2..).contains(&measure_cycles( || { let _ = (ln.rad(), ln.ph()); }, || { let _ = ln.rad_ph(); }, batch, reps)));
1002 /// // assert!((3..).contains(&measure_cycles( || { let _ = (re.rad(), re.ph()); }, || { let _ = re.rad_ph(); }, batch, reps)));
1003 /// // assert!((3..).contains(&measure_cycles( || { let _ = (re_im.rad(), re_im.ph()); }, || { let _ = re_im.rad_ph(); }, batch, reps)));
1004 /// ```
1005 #[inline(always)]
1006 pub fn rad_ph(self) -> (T, T) {
1007 use Cpx::*;
1008 match self {
1009 Zero => (T::ZERO, T::ZERO),
1010 One => (T::ONE, T::ZERO),
1011 J => (T::ONE, T::FRAC_PI_2),
1012 NegOne => (T::ONE, T::PI),
1013 NegJ => (T::ONE, T::NEG_FRAC_PI_2),
1014 Re { re } => match re {
1015 r if r < T::ZERO => (-r, T::PI),
1016 r => (r, T::ZERO),
1017 },
1018 Im { im } => match im {
1019 i if i < T::ZERO => (-i, T::NEG_FRAC_PI_2),
1020 i if i > T::ZERO => (i, T::FRAC_PI_2),
1021 _ => (T::ZERO, T::ZERO),
1022 },
1023 Ph { ph } => (T::ONE, ph.principal_val()),
1024 Pl { rad, ph } => match rad {
1025 _ if rad > T::ZERO => (rad, ph.principal_val()),
1026 _ => (T::ZERO, T::ZERO),
1027 },
1028 Ln { re, im } => match re {
1029 r if r < T::LN_THRESHOLD => (T::ZERO, T::ZERO),
1030 r if r > T::LN_MAX => (T::INFINITY, im.principal_val()),
1031 r => (r.exp(), im.principal_val()),
1032 },
1033 ReIm { re, im } => (re.hypot(im), im.atan2(re)),
1034 }
1035 }
1036
1037 /// Converts this complex number into its canonical polar (radius–phase) form.
1038 ///
1039 /// # Performance
1040 ///
1041 /// ```rust
1042 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1043 ///
1044 /// let batch = 100_000;
1045 /// let reps = 50;
1046 ///
1047 /// type F = f64;
1048 /// type C = Cpx<F>;
1049 ///
1050 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1051 ///
1052 /// let (val,val_2): (F,F) = (3.0,4.0);
1053 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1054 ///
1055 /// let phase: F = F::FRAC_PI_6;
1056 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1057 ///
1058 /// // assert!((..=3).contains(&measure_cycles( || { let _ = zero.to_polar(); }, || { let _ = zero; }, batch, reps)));
1059 /// // assert!((..=3).contains(&measure_cycles( || { let _ = re.to_polar(); }, || { let _ = re; }, batch, reps)));
1060 /// // assert!((..=9).contains(&measure_cycles( || { let _ = ph.to_polar(); }, || { let _ = ph; }, batch, reps)));
1061 /// // assert!((..=9).contains(&measure_cycles( || { let _ = pl.to_polar(); }, || { let _ = pl; }, batch, reps)));
1062 /// // assert!((..=32).contains(&measure_cycles( || { let _ = ln.to_polar(); }, || { let _ = ln; }, batch, reps)));
1063 /// // assert!((..=54).contains(&measure_cycles( || { let _ = re_im.to_polar(); }, || { let _ = re_im; }, batch, reps)));
1064 /// ```
1065 #[inline(always)]
1066 pub fn to_polar(self) -> Self {
1067 use Cpx::*;
1068 // Using `rad_ph()` in a single call is about 3 CPU cycles faster than computing
1069 // `rad()` and `ph()` separately.
1070 // For simple variants, this yields a significant improvement (≈25–50%),
1071 // while for more complex variants the gain is smaller but still noticeable (≈5–10%).
1072 //let (rad, ph) = (self.rad(), self.ph());
1073 let (rad, ph) = self.rad_ph();
1074 Pl { rad, ph }
1075 }
1076
1077 /// Converts this complex number into its canonical polar (radius–phase) form.
1078 /// This method normalizes all coordinate variants to one of the canonical forms:
1079 /// - `Zero`, `One`, `NegOne`, `J`, `NegJ`
1080 /// - `Re { re }`
1081 /// - `Im { im }`
1082 /// - `Ph { ph }`
1083 /// - `Pl { re, im }`
1084 ///
1085 /// # Performance Considerations
1086 /// - [`Cpx::canonicalize_polar()`] is generally **more expensive** (~14-54 CPU cycles) than [`Cpx::to_polar()`] because it performs additional checks
1087 /// and conversions for canonicalization.
1088 /// - For **multiplication**, [`Cpx::to_polar()`] typically provides normalized operands and is preferred in most cases.
1089 /// - Use [`Cpx::canonicalize_polar()`] only when canonical variants (`Zero`, `One`, `NegOne`, `J`, `NegJ`,
1090 /// `Re { re }`, `Im { im }`) are highly desirable.
1091 /// - Example: when multiplying many complex numbers, grouping simple variants first
1092 /// allows extremely fast aggregation, followed by combining the remaining components.
1093 /// - Another example: in non-performance-critical cases, such as rendering complex numbers as
1094 /// LaTeX strings, canonicalization can simplify formatting.
1095 ///
1096 /// # Performance
1097 ///
1098 /// ```rust
1099 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1100 ///
1101 /// let batch = 100_000;
1102 /// let reps = 50;
1103 ///
1104 /// type F = f64;
1105 /// type C = Cpx<F>;
1106 ///
1107 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1108 ///
1109 /// let (val,val_2): (F,F) = (3.0,4.0);
1110 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1111 ///
1112 /// let phase: F = F::FRAC_PI_6;
1113 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1114 ///
1115 /// // assert!((..=3).contains(&measure_cycles( || { let _ = zero.canonicalize_polar(); }, || { let _ = zero; }, batch, reps)));
1116 /// // assert!((..=17).contains(&measure_cycles( || { let _ = re.canonicalize_polar(); }, || { let _ = re; }, batch, reps)));
1117 /// // assert!((..=27).contains(&measure_cycles( || { let _ = ph.canonicalize_polar(); }, || { let _ = ph; }, batch, reps)));
1118 /// // assert!((..=40).contains(&measure_cycles( || { let _ = pl.canonicalize_polar(); }, || { let _ = pl; }, batch, reps)));
1119 /// // assert!((..=73).contains(&measure_cycles( || { let _ = ln.canonicalize_polar(); }, || { let _ = ln; }, batch, reps)));
1120 /// // assert!((..=108).contains(&measure_cycles( || { let _ = re_im.canonicalize_polar(); }, || { let _ = re_im; }, batch, reps)));
1121 /// ```
1122 #[inline(always)]
1123 pub fn canonicalize_polar(self) -> Self {
1124 use Cpx::*;
1125 match self {
1126 Zero | One | NegOne | J | NegJ => self,
1127 Re { re } => canonicalize_axis!(Re, re, One, NegOne),
1128 Im { im } => canonicalize_axis!(Im, im, J, NegJ),
1129 Ph { ph } => canonicalize_phase!(ph),
1130 Pl { rad, ph } => canonicalize_polar!(rad, ph),
1131 _ => {
1132 let (rad, ph) = self.rad_ph();
1133 canonicalize_polar!(rad, ph)
1134 }
1135 }
1136 }
1137
1138 /// Canonicalizes and simplifies a `Cpx<T>` form if possible.
1139 /// This method normalizes the representation of the complex number to one of the canonical forms:
1140 /// - `Zero`, `One`, `NegOne`, `J`, `NegJ`
1141 /// - `Re { re }`
1142 /// - `Im { im }`
1143 /// - `ReIm { re, im }`
1144 /// - `Pl { rad, ph }`
1145 ///
1146 /// Returns a canonicalized form of the complex number.
1147 ///
1148 /// ## Examples
1149 /// ```rust
1150 /// use cpx_coords::{Cpx, FloatExt};
1151 /// use Cpx::*;
1152 ///
1153 /// type F = f64;
1154 ///
1155 /// assert_eq!(Re { re: 0.0 }.canonicalize_lazy(), Zero);
1156 /// assert_eq!(Re { re: 1.0 }.canonicalize_lazy(), One);
1157 /// assert_eq!(Re { re: -1.0 }.canonicalize_lazy(), NegOne);
1158 /// assert_eq!(Im { im: 1.0 }.canonicalize_lazy(), J);
1159 /// assert_eq!(Im { im: -1.0 }.canonicalize_lazy(), NegJ);
1160 ///
1161 /// assert_eq!(Ph { ph: 0.0 }.canonicalize_lazy(), One);
1162 /// assert_eq!(Ph { ph: F::FRAC_PI_2 }.canonicalize_lazy(), J);
1163 /// assert_eq!(Ph { ph: -F::PI }.canonicalize_lazy(), NegOne);
1164 ///
1165 /// let ph = F::FRAC_PI_2;
1166 /// assert_eq!(Pl { rad: 1.0, ph }.canonicalize_lazy(), J);
1167 /// assert_eq!(Pl { rad: 2.0, ph }.canonicalize_lazy(), Im { im: 2.0 });
1168 ///
1169 /// let z = Ln { re: 1.0_f64.ln(), im: 0.0 };
1170 /// assert_eq!(z.canonicalize_lazy(), One);
1171 ///
1172 /// let z = Ln { re: 2.0_f64.ln(), im: F::FRAC_PI_2 };
1173 /// assert_eq!(z.canonicalize_lazy(), Im { im: 2.0 });
1174 /// ```
1175 ///
1176 /// # Performance Considerations
1177 /// - [`Cpx::canonicalize_lazy()`] has more moderate worst-case performance than [`Cpx::canonicalize_re_im()`] or [`Cpx::canonicalize_polar()`],
1178 /// as it adaptively chooses the most appropriate canonical form based on the current representation, avoiding costly
1179 /// conversions (e.g., from `ReIm` to `Pl`).
1180 /// - Prefer [`Cpx::canonicalize_lazy()`] when canonical forms such as `Zero`, `One`, `NegOne`, `J`, `NegJ`,
1181 /// `Re { re }`, or `Im { im }` are strongly desired, or when implementing new functions that benefit from
1182 /// structured, predictable inputs with moderate worst-case performance.
1183 ///
1184 /// # Performance
1185 ///
1186 /// ```rust
1187 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1188 ///
1189 /// let batch = 100_000;
1190 /// let reps = 50;
1191 ///
1192 /// type F = f64;
1193 /// type C = Cpx<F>;
1194 ///
1195 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1196 ///
1197 /// let (val,val_2): (F,F) = (3.0,4.0);
1198 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1199 ///
1200 /// let phase: F = F::FRAC_PI_6;
1201 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1202 ///
1203 /// // assert!((..=4).contains(&measure_cycles( || { let _ = zero.canonicalize_lazy(); }, || { let _ = zero; }, batch, reps)));
1204 /// // assert!((..=16).contains(&measure_cycles( || { let _ = re.canonicalize_lazy(); }, || { let _ = re; }, batch, reps)));
1205 /// // assert!((..=12).contains(&measure_cycles( || { let _ = re_im.canonicalize_lazy(); }, || { let _ = re_im; }, batch, reps)));
1206 /// // assert!((..=25).contains(&measure_cycles( || { let _ = ph.canonicalize_lazy(); }, || { let _ = ph; }, batch, reps)));
1207 /// // assert!((..=35).contains(&measure_cycles( || { let _ = pl.canonicalize_lazy(); }, || { let _ = pl; }, batch, reps)));
1208 /// // assert!((..=58).contains(&measure_cycles( || { let _ = ln.canonicalize_lazy(); }, || { let _ = ln; }, batch, reps)));
1209 /// ```
1210 #[inline(always)]
1211 pub fn canonicalize_lazy(self) -> Self {
1212 use Cpx::*;
1213 match self {
1214 Zero | One | NegOne | J | NegJ => self,
1215 Re { re } => canonicalize_axis!(Re, re, One, NegOne),
1216 Im { im } => canonicalize_axis!(Im, im, J, NegJ),
1217 ReIm { re, im } => {
1218 if im == T::ZERO {
1219 canonicalize_axis!(Re, re, One, NegOne)
1220 } else if re == T::ZERO {
1221 canonicalize_axis_skip_zero!(Im, im, J, NegJ)
1222 } else {
1223 self
1224 }
1225 }
1226 Ph { ph } => canonicalize_phase!(ph),
1227 Pl { rad, ph } => canonicalize_polar!(rad, ph),
1228 Ln { im: ph, .. } => {
1229 let rad = self.rad();
1230 canonicalize_polar!(rad, ph)
1231 }
1232 }
1233 }
1234
1235 /// Returns the complex conjugate of this number.
1236 ///
1237 /// # Examples
1238 /// ```rust
1239 /// use cpx_coords::{Cpx, FloatExt};
1240 ///
1241 /// type F = f64;
1242 /// type C = Cpx<F>;
1243 ///
1244 /// assert_eq!((C::Zero.conj(), C::One.conj(), C::NegOne.conj(), C::J.conj(), C::NegJ.conj()), (C::Zero, C::One, C::NegOne, C::NegJ, C::J));
1245 /// let val = 3.0;
1246 /// assert_eq!((C::Re { re: val }.conj(), C::Im { im: val }.conj()), (C::Re { re: val }, C::Im { im: -val }));
1247 /// let val_2 = 4.0;
1248 /// assert_eq!(C::ReIm { re: val, im: val_2 }.conj(), C::ReIm { re: val, im: -val_2 });
1249 ///
1250 /// let (rad, ph): (F, F) = (1.0, F::FRAC_PI_6);
1251 /// assert_eq!((C::Ph { ph }.conj(), C::Pl { rad, ph }.conj(), C::Ln { re: rad.ln(), im: ph }.conj()),(C::Ph { ph: -ph }, C::Pl { rad, ph: -ph }, C::Ln { re: rad.ln(), im: -ph }));
1252 /// ```
1253 ///
1254 /// # Performance
1255 ///
1256 /// ```rust
1257 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1258 ///
1259 /// let batch = 100_000;
1260 /// let reps = 50;
1261 ///
1262 /// type F = f64;
1263 /// type C = Cpx<F>;
1264 ///
1265 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1266 ///
1267 /// let (val,val_2): (F,F) = (3.0,4.0);
1268 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1269 ///
1270 /// let phase: F = F::FRAC_PI_6;
1271 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1272 ///
1273 /// // assert!((..=1).contains(&measure_cycles( || { let _ = zero.conj(); }, || { let _ = zero; }, batch, reps)));
1274 /// // assert!((..=1).contains(&measure_cycles( || { let _ = re.conj(); }, || { let _ = re; }, batch, reps)));
1275 /// // assert!((..=2).contains(&measure_cycles( || { let _ = neg_j.conj(); }, || { let _ = neg_j; }, batch, reps)));
1276 /// // assert!((..=4).contains(&measure_cycles( || { let _ = im.conj(); }, || { let _ = im; }, batch, reps)));
1277 /// // assert!((..=4).contains(&measure_cycles( || { let _ = re_im.conj(); }, || { let _ = re_im; }, batch, reps)));
1278 /// // assert!((..=14).contains(&measure_cycles( || { let _ = ph.conj(); }, || { let _ = ph; }, batch, reps)));
1279 /// // assert!((..=12).contains(&measure_cycles( || { let _ = pl.conj(); }, || { let _ = pl; }, batch, reps)));
1280 /// // assert!((..=11).contains(&measure_cycles( || { let _ = ln.conj(); }, || { let _ = ln; }, batch, reps)));
1281 /// ```
1282 #[inline(always)]
1283 pub fn conj(self) -> Self {
1284 use Cpx::*;
1285 match self {
1286 Zero | One | NegOne | Re { .. } => self,
1287 J => NegJ,
1288 NegJ => J,
1289 Im { im } => Im { im: -im },
1290 ReIm { re, im } => ReIm { re, im: -im },
1291 Ph { ph } => Ph {
1292 ph: (-ph).principal_val(),
1293 },
1294 Pl { rad, ph } => Pl {
1295 rad,
1296 ph: (-ph).principal_val(),
1297 },
1298 Ln { re, im } => Ln {
1299 re,
1300 im: (-im).principal_val(),
1301 },
1302 }
1303 }
1304 /// Returns the reciprocal of the complex number.
1305 ///
1306 /// This method serves as a helper for implementing division.
1307 ///
1308 /// # Examples
1309 /// ```rust
1310 /// use cpx_coords::{Cpx, FloatExt};
1311 ///
1312 /// type F = f64;
1313 /// type C = Cpx<F>;
1314 ///
1315 /// assert_eq!((C::One.recip(), C::NegOne.recip(), C::J.recip(), C::NegJ.recip()), (C::One, C::NegOne, C::NegJ, C::J));
1316 /// let val: F = 3.0;
1317 /// assert_eq!((C::Re { re: val }.recip(), C::Im { im: val }.recip()), (C::Re { re: val.recip() }, C::Im { im: -val.recip() }));
1318 ///
1319 /// let (rad, ph): (F, F) = (1.0, F::FRAC_PI_6);
1320 /// assert_eq!((C::Ph { ph }.recip(), C::Pl { rad, ph }.recip(), C::Ln { re: rad.ln(), im: ph }.recip()),(C::Ph { ph: -ph }, C::Pl { rad: rad.recip(), ph: -ph }, C::Ln { re: -rad.ln(), im: -ph }));
1321 ///
1322 /// let val_2: F = 4.0;
1323 /// let rad2: F = val.hypot(val_2).powi(2);
1324 /// assert_eq!(C::ReIm { re: val, im: val_2 }.recip(), C::ReIm { re: val / rad2, im: -val_2 / rad2 });
1325 /// ```
1326 ///
1327 /// # Performance
1328 ///
1329 /// ```rust
1330 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1331 ///
1332 /// let batch = 100_000;
1333 /// let reps = 50;
1334 ///
1335 /// type F = f64;
1336 /// type C = Cpx<F>;
1337 ///
1338 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1339 ///
1340 /// let (val,val_2): (F,F) = (0.6 * F::LN_THRESHOLD,0.8);
1341 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1342 ///
1343 /// let phase: F = F::FRAC_PI_6;
1344 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1345 ///
1346 /// // assert!((..=1).contains(&measure_cycles( || { let _ = one.recip(); }, || { let _ = one; }, batch, reps)));
1347 /// // assert!((..=2).contains(&measure_cycles( || { let _ = neg_j.recip(); }, || { let _ = neg_j; }, batch, reps)));
1348 /// // assert!((..=2).contains(&measure_cycles( || { let _ = zero.recip(); }, || { let _ = zero; }, batch, reps)));
1349 /// // assert!((..=4).contains(&measure_cycles( || { let _ = ph.recip(); }, || { let _ = ph; }, batch, reps)));
1350 /// // assert!((..=8).contains(&measure_cycles( || { let _ = re.recip(); }, || { let _ = re; }, batch, reps)));
1351 /// // assert!((..=12).contains(&measure_cycles( || { let _ = im.recip(); }, || { let _ = im; }, batch, reps)));
1352 /// // assert!((..=9).contains(&measure_cycles( || { let _ = ln.recip(); }, || { let _ = ln; }, batch, reps)));
1353 /// // assert!((..=13).contains(&measure_cycles( || { let _ = pl.recip(); }, || { let _ = pl; }, batch, reps)));
1354 /// assert!((..=25).contains(&measure_cycles( || { let _ = re_im.recip(); }, || { let _ = re_im; }, batch, reps)));
1355 /// ```
1356 #[inline(always)]
1357 pub fn recip(self) -> Self {
1358 use Cpx::*;
1359 match self {
1360 One | NegOne => self,
1361 J => NegJ,
1362 NegJ => J,
1363 Zero => Re { re: T::INFINITY },
1364 Re { re } => Re { re: re.recip() },
1365 Im { im } => Im { im: -im.recip() },
1366 Ph { ph } => Ph { ph: -ph },
1367 Ln { re, im } => Ln { re: -re, im: -im },
1368 Pl { rad, ph } => Pl {
1369 rad: rad.recip(),
1370 ph: -ph,
1371 },
1372 ReIm { re, im } => {
1373 //let rad2 = re.hypot(im).powi(2);
1374 let rad2 = re * re + im * im;
1375 ReIm {
1376 re: re / rad2,
1377 im: -im / rad2,
1378 }
1379 } //{
1380 //let rad = re.hypot(im);
1381 //if rad == T::ZERO {
1382 // Re { re: T::INFINITY }
1383 //} else if rad == T::ONE {
1384 // ReIm { re, im: -im }
1385 //} else {
1386 // let rad2 = rad.powi(2);
1387 // ReIm {
1388 // re: re / rad2,
1389 // im: -im / rad2,
1390 // }
1391 //}
1392 //}
1393 }
1394 }
1395
1396 /// Returns the exponential of the complex number.
1397 ///
1398 /// # Examples
1399 /// ```rust
1400 /// use cpx_coords::{Cpx, FloatExt};
1401 ///
1402 /// type F = f64;
1403 /// type C = Cpx<F>;
1404 ///
1405 /// // exp(0) = 1
1406 /// //assert_eq!(C::Zero.exp(), C::One);
1407 ///
1408 /// // exp(1) = e
1409 /// //assert_eq!(C::One.exp(), C::Re { re: F::E });
1410 ///
1411 /// // exp(-1) = 1/e
1412 /// //assert_eq!(C::NegOne.exp(), C::Re { re: F::FRAC_1_E });
1413 ///
1414 /// // exp(i) = phase(1)
1415 /// //assert_eq!(C::J.exp(), C::Ph { ph: 1.0 });
1416 ///
1417 /// // exp(-i) = phase(-1)
1418 /// //assert_eq!(C::NegJ.exp(), C::Ph { ph: -1.0 });
1419 ///
1420 /// // Real input
1421 /// let z = C::Re { re: 2.0 };
1422 /// //assert_eq!(z.exp(), C::Ln { re: 2.0, im: F::ZERO });
1423 ///
1424 /// // Imaginary input
1425 /// let z = C::Im { im: 2.0 };
1426 /// //assert_eq!(z.exp(), Cpx::Ph { ph: 2.0 });
1427 ///
1428 /// // exp(PL) → Ln(re, im)
1429 /// let z = C::Pl { rad: F::SQRT_2, ph: F::FRAC_PI_4 };
1430 /// let expected = C::Ln { re: 1.0, im: 1.0 };
1431 /// //assert!((z.exp() - expected).rad() < F::THRESHOLD);
1432 ///
1433 /// // exp(Ln) → Ln(re * cos, re * sin)
1434 /// let z = C::Ln { re: F::LN_2, im: F::FRAC_PI_4 };
1435 /// let expected = C::Ln { re: F::SQRT_2, im: F::SQRT_2 };
1436 /// //assert!((z.exp() - expected).rad() < F::THRESHOLD);
1437 ///
1438 /// // Identity of exp: 0.318132 + 1.33724i
1439 /// //let (v0, v1): (F,F) = (0.318132,1.3372368815372089);
1440 /// //let z: C = C::Pl { rad: 1.3745570107437075, ph: 1.3372357014306895 };
1441 /// let z: C = C::ReIm { re: 0.31813150520476413, im: 1.3372357014306895 };
1442 /// assert_eq!(z.exp(), z);
1443 /// ```
1444 ///
1445 /// # Performance
1446 ///
1447 /// ```rust
1448 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1449 ///
1450 /// let batch = 100_000;
1451 /// let reps = 50;
1452 ///
1453 /// type F = f64;
1454 /// type C = Cpx<F>;
1455 ///
1456 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1457 ///
1458 /// let (val,val_2): (F,F) = (3.0,4.0);
1459 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1460 ///
1461 /// let phase: F = F::FRAC_PI_6;
1462 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1463 ///
1464 /// // assert!((..=4).contains(&measure_cycles( || { let _ = zero.exp(); }, || { let _ = zero; }, batch, reps)));
1465 /// // assert!((..=4).contains(&measure_cycles( || { let _ = one.exp(); }, || { let _ = one; }, batch, reps)));
1466 /// // assert!((..=5).contains(&measure_cycles( || { let _ = j.exp(); }, || { let _ = j; }, batch, reps)));
1467 /// // assert!((..=4).contains(&measure_cycles( || { let _ = re.exp(); }, || { let _ = re; }, batch, reps)));
1468 /// // assert!((..=5).contains(&measure_cycles( || { let _ = im.exp(); }, || { let _ = im; }, batch, reps)));
1469 /// // assert!((..=5).contains(&measure_cycles( || { let _ = re_im.exp(); }, || { let _ = re_im; }, batch, reps)));
1470 /// // assert!((..=40).contains(&measure_cycles( || { let _ = ph.exp(); }, || { let _ = ph; }, batch, reps)));
1471 /// // assert!((..=47).contains(&measure_cycles( || { let _ = pl.exp(); }, || { let _ = pl; }, batch, reps)));
1472 /// // assert!((..=68).contains(&measure_cycles( || { let _ = ln.exp(); }, || { let _ = ln; }, batch, reps)));
1473 /// ```
1474 #[inline(never)]
1475 pub fn exp(self) -> Self {
1476 use Cpx::*;
1477 match self {
1478 Zero => One,
1479 One => Re { re: T::E },
1480 NegOne => Re { re: T::FRAC_1_E },
1481 J => Ph { ph: T::ONE },
1482 NegJ => Ph { ph: T::NEG_ONE },
1483 Re { re } => Ln { re, im: T::ZERO },
1484 Im { im: ph } => Ph { ph },
1485 ReIm { re, im } => Ln { re, im },
1486 Ph { ph } => Ln {
1487 re: ph.cos(),
1488 im: ph.sin(),
1489 },
1490 Pl { rad, ph } => Ln {
1491 re: rad * ph.cos(),
1492 im: rad * ph.sin(),
1493 },
1494 Ln { re, im } => {
1495 let rad = re.exp();
1496 Ln {
1497 re: rad * im.cos(),
1498 im: rad * im.sin(),
1499 }
1500 } // The above explicitly way for `Ph`, `Pl`, and `Ln` variants is about 20-30 CPU cycles faster than the below concise one.
1501 //x => {
1502 // let (re, im) = x.re_im();
1503 // Ln { re, im }
1504 //}
1505 }
1506 }
1507
1508 /// Returns the logarithm of the complex number.
1509 ///
1510 /// # Examples
1511 /// ```rust
1512 /// use cpx_coords::{Cpx,FloatExt};
1513 /// use Cpx::*;
1514 ///
1515 /// type F = f64;
1516 ///
1517 /// let zero: Cpx<F> = Zero.ln();
1518 /// assert!(matches!(zero, Re { re } if re.is_infinite() && re.is_sign_negative()));
1519 ///
1520 /// let one: Cpx<F> = One.ln();
1521 /// assert_eq!(one, Zero);
1522 ///
1523 /// let neg_one = NegOne.ln();
1524 /// assert_eq!(neg_one, Im { im: F::PI });
1525 ///
1526 /// let j = J.ln();
1527 /// assert_eq!(j, Im { im: F::FRAC_PI_2 });
1528 ///
1529 /// let neg_j = NegJ.ln();
1530 /// assert_eq!(neg_j, Im { im: F::NEG_FRAC_PI_2 });
1531 ///
1532 /// let re_val = Re { re: 2.0 }.ln();
1533 /// assert!(matches!(re_val, Re { re } if (re - F::LN_2).abs() < F::THRESHOLD));
1534 ///
1535 /// let neg_re = Re { re: -2.0 }.ln();
1536 /// assert!(matches!(neg_re, ReIm { re, im }
1537 /// if (re - 2.0f64.ln()).abs() < F::THRESHOLD && (im - F::PI).abs() < F::THRESHOLD));
1538 ///
1539 /// let im_val = Im { im: 3.0 }.ln();
1540 /// assert!(matches!(im_val, ReIm { re, im }
1541 /// if (re - 3.0f64.ln()).abs() < F::THRESHOLD && (im - F::FRAC_PI_2).abs() < F::THRESHOLD));
1542 ///
1543 /// let neg_im = Im { im: -4.0 }.ln();
1544 /// assert!(matches!(neg_im, ReIm { re, im }
1545 /// if (re - 4.0f64.ln()).abs() < F::THRESHOLD && (im + F::FRAC_PI_2).abs() < F::THRESHOLD));
1546 ///
1547 /// let phase = Ph { ph: 1.2 }.ln();
1548 /// assert_eq!(phase, Im { im: 1.2 });
1549 ///
1550 /// let pl = Pl { rad: 2.0, ph: 1.1 }.ln();
1551 /// assert!(matches!(pl, ReIm { re, im }
1552 /// if (re - 2.0f64.ln()).abs() < F::THRESHOLD && (im - 1.1).abs() < F::THRESHOLD));
1553 /// ```
1554 ///
1555 /// # Performance
1556 ///
1557 /// Cycle counts measured on `x86_64` with `measure_cycles`
1558 /// (`batch = 100_000`, `reps = 50`):
1559 ///
1560 /// | Variant | Cycles (typical) |
1561 /// |---------------|------------------|
1562 /// | `Zero` | ≤ 5 |
1563 /// | `One` | ≤ 6 |
1564 /// | `J` | ≤ 6 |
1565 /// | `Im` | ≤ 15 |
1566 /// | `Re` | ≤ 25 |
1567 /// | `ReIm` | ≤ 40 |
1568 /// | `Ph` | ≤ 65 |
1569 /// | `Pl` | ≤ 85 |
1570 /// | `Ln` | ≤ 110 |
1571 ///
1572 /// ```rust
1573 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1574 ///
1575 /// let batch = 100_000;
1576 /// let reps = 50;
1577 ///
1578 /// type F = f64;
1579 /// type C = Cpx<F>;
1580 ///
1581 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1582 ///
1583 /// let (val,val_2): (F,F) = (3.0,4.0);
1584 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1585 ///
1586 /// let phase: F = F::FRAC_PI_6;
1587 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1588 ///
1589 /// // assert!((..=4).contains(&measure_cycles( || { let _ = one.ln(); }, || { let _ = one; }, batch, reps)));
1590 /// // assert!((..=5).contains(&measure_cycles( || { let _ = j.ln(); }, || { let _ = j; }, batch, reps)));
1591 /// // assert!((..=5).contains(&measure_cycles( || { let _ = zero.ln(); }, || { let _ = zero; }, batch, reps)));
1592 /// // assert!((..=5).contains(&measure_cycles( || { let _ = ph.ln(); }, || { let _ = ph; }, batch, reps)));
1593 /// // assert!((..=5).contains(&measure_cycles( || { let _ = ln.ln(); }, || { let _ = ln; }, batch, reps)));
1594 /// // assert!((..=22).contains(&measure_cycles( || { let _ = pl.ln(); }, || { let _ = pl; }, batch, reps)));
1595 /// // assert!((..=22).contains(&measure_cycles( || { let _ = re.ln(); }, || { let _ = re; }, batch, reps)));
1596 /// // assert!((..=22).contains(&measure_cycles( || { let _ = im.ln(); }, || { let _ = im; }, batch, reps)));
1597 /// // assert!((..=85).contains(&measure_cycles( || { let _ = re_im.ln(); }, || { let _ = re_im; }, batch, reps)));
1598 /// ```
1599 #[inline(never)]
1600 pub fn ln(self) -> Self {
1601 use Cpx::*;
1602 match self {
1603 One => Zero,
1604 NegOne => Im { im: T::PI },
1605 J => Im { im: T::FRAC_PI_2 },
1606 NegJ => Im {
1607 im: T::NEG_FRAC_PI_2,
1608 },
1609 Zero => Re {
1610 re: T::NEG_INFINITY,
1611 },
1612 Ph { ph: im } => Im { im },
1613 Ln { re, im } => ReIm { re, im },
1614 Pl { rad, ph: im } => ReIm { re: rad.ln(), im },
1615 Re { re } => match re {
1616 x if x > T::ZERO => Re { re: x.ln() },
1617 x if x < T::ZERO => ReIm {
1618 re: (-x).ln(),
1619 im: T::PI,
1620 },
1621 _ => Zero,
1622 },
1623 Im { im } => match im {
1624 x if x > T::ZERO => ReIm {
1625 re: x.ln(),
1626 im: T::FRAC_PI_2,
1627 },
1628 x if x < T::ZERO => ReIm {
1629 re: (-x).ln(),
1630 im: T::NEG_FRAC_PI_2,
1631 },
1632 _ => Zero,
1633 },
1634 ReIm { re, im } => ReIm {
1635 re: re.hypot(im).ln(),
1636 im: im.atan2(re),
1637 },
1638 }
1639 }
1640 /// Raises the complex number to an integer power using polar form.
1641 ///
1642 /// Computes `rad^n * e^(i * n * θ)` by raising the magnitude to `n` and scaling the phase.
1643 /// Returns the canonicalized result.
1644 ///
1645 /// # Example
1646 /// ```rust
1647 /// use cpx_coords::{Cpx,FloatExt};
1648 /// use Cpx::*;
1649 ///
1650 /// type F = f64;
1651 /// type C = Cpx<F>;
1652 ///
1653 /// let (v0, v1): (F,F) = (3.0,4.0);
1654 ///
1655 /// // Any number to the 0th power is 1
1656 /// let z: C = ReIm { re: v0, im: v1 };
1657 /// //assert_eq!(z.powi32(0), One);
1658 ///
1659 /// let pow: i32 = 2;
1660 /// // Real positive number
1661 /// let x: C = Re { re: v0 };
1662 /// //assert_eq!(x.powi32(pow), Re {re: v0.powi(pow)});
1663 ///
1664 /// // Real negative number to even power: positive real
1665 /// let x: C = Re { re: -v0 };
1666 /// //assert_eq!(x.powi32(pow), Re {re: (-v0).powi(pow)});
1667 ///
1668 /// // Imaginary unit squared: i^2 = -1
1669 /// //assert_eq!(C::J.powi32(2), NegOne);
1670 ///
1671 /// // (i)^4 = 1
1672 /// //assert_eq!(C::J.powi32(4), One);
1673 ///
1674 /// let phase: F = F::FRAC_PI_2;
1675 ///
1676 /// // Polar representation
1677 /// let z: Cpx<F> = Pl { rad: v0, ph: phase };
1678 /// //assert_eq!(z.powi32(pow), Pl { rad: v0.powi(pow), ph: phase * F::from(pow)});
1679 ///
1680 /// // General complex number: verify magnitude grows appropriately
1681 /// let z: C = ReIm { re: 1.0, im: 1.0 };
1682 /// let z2 = z.powi32(2);
1683 /// //assert_eq!(z.powi32(2), Im { im: 2.0}); // This fails due to floating-point precision limits in f32/f64.
1684 /// //assert!((z2 - Im { im: 2.0}).rad() < F::THRESHOLD);
1685 ///
1686 /// // Zero to positive power is still T::ZERO
1687 /// //assert_eq!(C::Zero.powi32(5), Zero);
1688 ///
1689 /// // Zero to T::ZERO is defined as One (by convention)
1690 /// //assert_eq!(C::Zero.powi32(0), One);
1691 ///
1692 /// // J = i; i^3 = -i = -J
1693 /// //assert_eq!(C::J.powi32(3), NegJ);
1694 ///
1695 /// // Negative J to odd power remains imaginary
1696 /// //assert_eq!(C::NegJ.powi32(3), J);
1697 ///
1698 /// // Unit modulus phase rotation (θ = π/2): (e^{iπ/2})^4 = 1
1699 /// let z = Ph { ph: F::FRAC_PI_2 };
1700 /// //assert_eq!(z.powi32(4), One);
1701 ///
1702 /// // Phase angle wrapping: e^{iπ} = -1; (e^{iπ})^3 = -1
1703 /// let z = Ph { ph: F::PI };
1704 /// //assert_eq!(z.powi32(3), NegOne);
1705 ///
1706 /// // Large integer exponent: (2 + 0i)^20 = 2^20
1707 /// let x: C = Re { re: 2.0 };
1708 /// //assert_eq!(x.powi32(20), Re { re: 1048576.0 });
1709 /// ```
1710 ///
1711 /// ```rust
1712 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1713 ///
1714 /// let batch = 100_000;
1715 /// let reps = 10;
1716 ///
1717 /// type F = f64;
1718 /// type C = Cpx<F>;
1719 ///
1720 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1721 ///
1722 /// let (val,val_2): (F,F) = (3.0,4.0);
1723 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1724 ///
1725 /// let phase: F = F::FRAC_PI_6;
1726 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1727 /// // assert!((..=6).contains(&measure_cycles( || { let _ = zero.powi32(2); }, || { let _ = zero; }, batch, reps)));
1728 /// // assert!((..=6).contains(&measure_cycles( || { let _ = one.powi32(2); }, || { let _ = one; }, batch, reps)));
1729 /// // assert!((..=7).contains(&measure_cycles( || { let _ = neg_one.powi32(2); }, || { let _ = neg_one; }, batch, reps)));
1730 /// // assert!((..=8).contains(&measure_cycles( || { let _ = j.powi32(2); }, || { let _ = j; }, batch, reps)));
1731 /// // assert!((..=6).contains(&measure_cycles( || { let _ = neg_j.powi32(2); }, || { let _ = neg_j; }, batch, reps)));
1732 /// // assert!((..=25).contains(&measure_cycles( || { let _ = re.powi32(2); }, || { let _ = re; }, batch, reps)));
1733 /// // assert!((..=30).contains(&measure_cycles( || { let _ = im.powi32(2); }, || { let _ = im; }, batch, reps)));
1734 /// // assert!((..=28).contains(&measure_cycles( || { let _ = ph.powi32(2); }, || { let _ = ph; }, batch, reps)));
1735 /// // assert!((..=40).contains(&measure_cycles( || { let _ = pl.powi32(2); }, || { let _ = pl; }, batch, reps)));
1736 /// // assert!((..=40).contains(&measure_cycles( || { let _ = ln.powi32(2); }, || { let _ = ln; }, batch, reps)));
1737 /// // assert!((..=6).contains(&measure_cycles( || { let _ = re_im.powi32(0); }, || { let _ = re_im; }, batch, reps)));
1738 /// // assert!((..=6).contains(&measure_cycles( || { let _ = re_im.powi32(1); }, || { let _ = re_im; }, batch, reps)));
1739 /// // assert!((..=45).contains(&measure_cycles( || { let _ = re_im.powi32(-1); }, || { let _ = re_im; }, batch, reps)));
1740 /// // assert!((..=30).contains(&measure_cycles( || { let _ = re_im.powi32(2); }, || { let _ = re_im; }, batch, reps)));
1741 /// // assert!((..=50).contains(&measure_cycles( || { let _ = re_im.powi32(-2); }, || { let _ = re_im; }, batch, reps)));
1742 /// // assert!((..=55).contains(&measure_cycles( || { let _ = re_im.powi32(3); }, || { let _ = re_im; }, batch, reps)));
1743 /// // assert!((..=50).contains(&measure_cycles( || { let _ = re_im.powi32(4); }, || { let _ = re_im; }, batch, reps)));
1744 /// // assert!((..=70).contains(&measure_cycles( || { let _ = re_im.powi32(8); }, || { let _ = re_im; }, batch, reps)));
1745 /// // assert!((..=90).contains(&measure_cycles( || { let _ = re_im.powi32(16); }, || { let _ = re_im; }, batch, reps)));
1746 /// // assert!((..=110).contains(&measure_cycles( || { let _ = re_im.powi32(32); }, || { let _ = re_im; }, batch, reps)));
1747 /// // assert!((..=130).contains(&measure_cycles( || { let _ = re_im.powi32(64); }, || { let _ = re_im; }, batch, reps)));
1748 /// // assert!((..=75).contains(&measure_cycles( || { let _ = re_im.powi32(6); }, || { let _ = re_im; }, batch, reps)));
1749 /// // assert!((..=95).contains(&measure_cycles( || { let _ = re_im.powi32(10); }, || { let _ = re_im; }, batch, reps)));
1750 /// // assert!((..=95).contains(&measure_cycles( || { let _ = re_im.powi32(12); }, || { let _ = re_im; }, batch, reps)));
1751 /// // assert!((..=115).contains(&measure_cycles( || { let _ = re_im.powi32(14); }, || { let _ = re_im; }, batch, reps)));
1752 /// // assert!((..=115).contains(&measure_cycles( || { let _ = re_im.powi32(18); }, || { let _ = re_im; }, batch, reps)));
1753 /// // assert!((..=115).contains(&measure_cycles( || { let _ = re_im.powi32(20); }, || { let _ = re_im; }, batch, reps)));
1754 /// // assert!((..=130).contains(&measure_cycles( || { let _ = re_im.powi32(22); }, || { let _ = re_im; }, batch, reps))); // No benefit
1755 /// // assert!((..=115).contains(&measure_cycles( || { let _ = re_im.powi32(24); }, || { let _ = re_im; }, batch, reps)));
1756 /// // assert!((..=155).contains(&measure_cycles( || { let _ = re_im.powi32(1000); }, || { let _ = re_im; }, batch, reps)));
1757 /// ```
1758 #[inline(never)]
1759 pub fn powi32(self, n: i32) -> Self {
1760 use Cpx::*;
1761
1762 // Special cases first
1763 if n == 0 {
1764 return One;
1765 } else if n == 1 {
1766 return self;
1767 } else if n == -1 {
1768 return self.recip();
1769 }
1770 #[inline(always)]
1771 fn zero_to_pow<T: FloatExt>(n: i32) -> Cpx<T> {
1772 match n {
1773 _ if n > 0 => Zero,
1774 _ => Re { re: T::INFINITY },
1775 }
1776 }
1777
1778 #[inline(always)]
1779 fn neg_one_to_pow<T: FloatExt>(n: i32) -> Cpx<T> {
1780 match n & 1_i32 {
1781 0 => One,
1782 _ => NegOne,
1783 }
1784 }
1785
1786 #[inline(always)]
1787 fn j_to_pow<T: FloatExt>(n: i32) -> Cpx<T> {
1788 match n & 3_i32 {
1789 0 => One,
1790 1 => J,
1791 2 => NegOne,
1792 _ => NegJ,
1793 }
1794 }
1795
1796 #[inline(always)]
1797 fn neg_j_to_pow<T: FloatExt>(n: i32) -> Cpx<T> {
1798 match n & 3_i32 {
1799 0 => One,
1800 1 => NegJ,
1801 2 => NegOne,
1802 _ => J,
1803 }
1804 }
1805
1806 #[inline(always)]
1807 fn ph_times_i32<T: FloatExt>(ph: T, n: i32) -> T {
1808 (ph * T::from(n).unwrap()).principal_val()
1809 }
1810
1811 match self {
1812 One => One,
1813 Zero => zero_to_pow(n),
1814 NegOne => neg_one_to_pow(n),
1815 J => j_to_pow(n),
1816 NegJ => neg_j_to_pow(n),
1817 Re { re } => Re { re: re.powi(n) },
1818 Im { im } => {
1819 let val = im.powi(n);
1820 match n & 3_i32 {
1821 0 => Re { re: val },
1822 1 => Im { im: val },
1823 2 => Re { re: -val },
1824 _ => Im { im: -val },
1825 }
1826 }
1827 Ph { ph } => Ph {
1828 ph: ph_times_i32(ph, n),
1829 },
1830 Pl { rad, ph } => Pl {
1831 rad: rad.powi(n),
1832 ph: ph_times_i32(ph, n),
1833 },
1834 Ln { re, im: ph } => Ln {
1835 re: re * T::from(n).unwrap(),
1836 im: ph_times_i32(ph, n),
1837 },
1838 ReIm { re, im } => {
1839 #[inline(always)]
1840 fn square<T: FloatExt>(re: T, im: T) -> (T, T) {
1841 let re2 = re * re - im * im;
1842 let im2 = re * im * T::TWO;
1843 (re2, im2)
1844 }
1845
1846 #[inline(always)]
1847 fn tiny_mul<T: FloatExt>(lhs: (T, T), rhs: (T, T)) -> (T, T) {
1848 let re = lhs.0 * rhs.0 - lhs.1 * rhs.1;
1849 let im = lhs.1 * rhs.0 + lhs.0 * rhs.1;
1850 (re, im)
1851 }
1852
1853 match n {
1854 2 => {
1855 let (re_2, im_2) = square(re, im);
1856 ReIm { re: re_2, im: im_2 }
1857 }
1858 4 => {
1859 let (re_2, im_2) = square(re, im); // z^2
1860 let (re_4, im_4) = square(re_2, im_2); // (z^2)^2 = z^4
1861 ReIm { re: re_4, im: im_4 }
1862 }
1863 8 => {
1864 let (re_2, im_2) = square(re, im); // z^2
1865 let (re_4, im_4) = square(re_2, im_2); // (z^2)^2 = z^4
1866 let (re_8, im_8) = square(re_4, im_4); // (z^4)^2 = z^8
1867 ReIm { re: re_8, im: im_8 }
1868 }
1869 16 => {
1870 let (re_2, im_2) = square(re, im);
1871 let (re_4, im_4) = square(re_2, im_2);
1872 let (re_8, im_8) = square(re_4, im_4);
1873 let (re_16, im_16) = square(re_8, im_8);
1874 ReIm {
1875 re: re_16,
1876 im: im_16,
1877 }
1878 }
1879 32 => {
1880 let (re_2, im_2) = square(re, im);
1881 let (re_4, im_4) = square(re_2, im_2);
1882 let (re_8, im_8) = square(re_4, im_4);
1883 let (re_16, im_16) = square(re_8, im_8);
1884 let (re_32, im_32) = square(re_16, im_16);
1885 ReIm {
1886 re: re_32,
1887 im: im_32,
1888 }
1889 }
1890 -2 => {
1891 let (re_2, im_2) = square(re, im);
1892 let rad2 = re * re + im * im;
1893 let rad4 = rad2 * rad2;
1894 ReIm {
1895 re: re_2 / rad4,
1896 im: -im_2 / rad4,
1897 }
1898 }
1899 3 => {
1900 let (re_2, im_2) = square(re, im);
1901 let (re, im) = tiny_mul((re_2, im_2), (re, im));
1902 ReIm { re, im }
1903 }
1904 6 => {
1905 let (re_2, im_2) = square(re, im);
1906 let (re_4, im_4) = square(re_2, im_2);
1907 let (re, im) = tiny_mul((re_4, im_4), (re_2, im_2));
1908 ReIm { re, im }
1909 }
1910 10 => {
1911 let (re_2, im_2) = square(re, im);
1912 let (re_4, im_4) = square(re_2, im_2);
1913 let (re_8, im_8) = square(re_4, im_4);
1914 let (re, im) = tiny_mul((re_8, im_8), (re_2, im_2));
1915 ReIm { re, im }
1916 }
1917 12 => {
1918 let (re_2, im_2) = square(re, im);
1919 let (re_4, im_4) = square(re_2, im_2);
1920 let (re_8, im_8) = square(re_4, im_4);
1921 let (re, im) = tiny_mul((re_8, im_8), (re_4, im_4));
1922 ReIm { re, im }
1923 }
1924 14 => {
1925 let (re_2, im_2) = square(re, im);
1926 let (re_4, im_4) = square(re_2, im_2);
1927 let (re_8, im_8) = square(re_4, im_4);
1928 let (re_12, im_12) = tiny_mul((re_8, im_8), (re_4, im_4));
1929 let (re, im) = tiny_mul((re_12, im_12), (re_2, im_2));
1930 ReIm { re, im }
1931 }
1932 18 => {
1933 let (re_2, im_2) = square(re, im);
1934 let (re_4, im_4) = square(re_2, im_2);
1935 let (re_8, im_8) = square(re_4, im_4);
1936 let (re_16, im_16) = square(re_8, im_8);
1937 let (re, im) = tiny_mul((re_16, im_16), (re_2, im_2));
1938 ReIm { re, im }
1939 }
1940 20 => {
1941 let (re_2, im_2) = square(re, im);
1942 let (re_4, im_4) = square(re_2, im_2);
1943 let (re_8, im_8) = square(re_4, im_4);
1944 let (re_16, im_16) = square(re_8, im_8);
1945 let (re, im) = tiny_mul((re_16, im_16), (re_4, im_4));
1946 ReIm { re, im }
1947 }
1948 //22 => {
1949 // let (re_2, im_2) = square(re, im);
1950 // let (re_4, im_4) = square(re_2, im_2);
1951 // let (re_8, im_8) = square(re_4, im_4);
1952 // let (re_16, im_16) = square(re_8, im_8);
1953 // let (re_20, im_20) = tiny_mul((re_16, im_16), (re_4, im_4));
1954 // let (re, im) = tiny_mul((re_20, im_20), (re_2, im_2));
1955 // ReIm { re, im }
1956 //}
1957 24 => {
1958 let (re_2, im_2) = square(re, im);
1959 let (re_4, im_4) = square(re_2, im_2);
1960 let (re_8, im_8) = square(re_4, im_4);
1961 let (re_16, im_16) = square(re_8, im_8);
1962 let (re, im) = tiny_mul((re_16, im_16), (re_8, im_8));
1963 ReIm { re, im }
1964 }
1965 _ => {
1966 let (rad, ph) = (re.hypot(im), im.atan2(re)); // self.rad_ph();
1967 Pl {
1968 rad: rad.powi(n),
1969 ph: ph_times_i32(ph, n),
1970 }
1971 }
1972 }
1973 }
1974 }
1975 }
1976
1977 /// Raises the complex number to a floating-point power using polar form.
1978 ///
1979 /// Computes `r^f * e^(i * f * θ)` by raising the magnitude to `f` and scaling the phase.
1980 /// Returns the canonicalized result.
1981 ///
1982 /// ```rust
1983 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
1984 ///
1985 /// let batch = 100_000;
1986 /// let reps = 50;
1987 ///
1988 /// type F = f64;
1989 /// type C = Cpx<F>;
1990 ///
1991 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
1992 ///
1993 /// let (val,val_2): (F,F) = (3.0,4.0);
1994 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
1995 ///
1996 /// let phase: F = F::FRAC_PI_6;
1997 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
1998 ///
1999 /// // assert!((..=11).contains(&measure_cycles( || { let _ = zero.powf(0.0); }, || { let _ = zero; }, batch, reps)));
2000 /// // assert!((..=25).contains(&measure_cycles( || { let _ = one.powf(0.5); }, || { let _ = one; }, batch, reps)));
2001 /// // assert!((..=25).contains(&measure_cycles( || { let _ = zero.powf(0.5); }, || { let _ = zero; }, batch, reps)));
2002 /// // assert!((..=40).contains(&measure_cycles( || { let _ = neg_one.powf(0.5); }, || { let _ = neg_one; }, batch, reps)));
2003 /// // assert!((..=40).contains(&measure_cycles( || { let _ = j.powf(0.5); }, || { let _ = j; }, batch, reps)));
2004 /// // assert!((..=40).contains(&measure_cycles( || { let _ = neg_j.powf(0.5); }, || { let _ = neg_j; }, batch, reps)));
2005 /// // assert!((..=40).contains(&measure_cycles( || { let _ = ph.powf(0.5); }, || { let _ = ph; }, batch, reps)));
2006 /// // assert!((..=40).contains(&measure_cycles( || { let _ = ln.powf(0.5); }, || { let _ = ln; }, batch, reps)));
2007 /// // assert!((..=50).contains(&measure_cycles( || { let _ = re.powf(0.5); }, || { let _ = re; }, batch, reps)));
2008 /// // assert!((..=60).contains(&measure_cycles( || { let _ = pl.powf(0.5); }, || { let _ = pl; }, batch, reps)));
2009 /// // assert!((..=60).contains(&measure_cycles( || { let _ = im.powf(0.5); }, || { let _ = im; }, batch, reps)));
2010 /// // assert!((..=110).contains(&measure_cycles( || { let _ = re_im.powf(0.5); }, || { let _ = re_im; }, batch, reps)));
2011 /// ```
2012 pub fn powf(self, f: T) -> Self {
2013 use Cpx::*;
2014
2015 if f == T::ZERO {
2016 return One;
2017 } else if f == T::ONE {
2018 return self;
2019 } else if f == T::NEG_ONE {
2020 return self.recip();
2021 }
2022 #[inline(always)]
2023 fn ph_times_f<T: FloatExt>(ph: T, f: T) -> T {
2024 (ph * f).principal_val()
2025 }
2026
2027 #[inline(always)]
2028 fn f_mod2<T: FloatExt>(f: T) -> T {
2029 if f >= -T::TWO && f <= T::TWO {
2030 f
2031 } else {
2032 f % T::TWO
2033 }
2034 }
2035
2036 #[inline(always)]
2037 fn f_mod4<T: FloatExt>(f: T) -> T {
2038 let four = T::TWO + T::TWO;
2039 if f >= -four && f <= four { f } else { f % four }
2040 }
2041
2042 match self {
2043 One => One,
2044 Zero => match f {
2045 _ if f > T::ZERO => Zero,
2046 _ => Re { re: T::INFINITY },
2047 },
2048 NegOne => Ph {
2049 ph: ph_times_f(T::PI, f_mod2(f)),
2050 },
2051 J => Ph {
2052 ph: ph_times_f(T::FRAC_PI_2, f_mod4(f)),
2053 },
2054 NegJ => Ph {
2055 ph: ph_times_f(T::NEG_FRAC_PI_2, f_mod4(f)),
2056 },
2057 Ph { ph } => Ph {
2058 ph: ph_times_f(ph, f),
2059 },
2060 Ln { re, im: ph } => Ln {
2061 re: re * f,
2062 im: ph_times_f(ph, f),
2063 },
2064 Pl { rad, ph } => Pl {
2065 rad: rad.powf(f),
2066 ph: ph_times_f(ph, f),
2067 },
2068 Re { re } => match re {
2069 _ if re > T::ZERO => Re { re: re.powf(f) },
2070 _ => Pl {
2071 rad: (-re).powf(f),
2072 ph: ph_times_f(T::PI, f_mod2(f)),
2073 },
2074 },
2075 Im { im } => match im {
2076 _ if im > T::ZERO => Pl {
2077 rad: im.powf(f),
2078 ph: ph_times_f(T::FRAC_PI_2, f_mod4(f)),
2079 },
2080 _ => Pl {
2081 rad: (-im).powf(f),
2082 ph: ph_times_f(T::NEG_FRAC_PI_2, f_mod4(f)),
2083 },
2084 },
2085 ReIm { re, im } => Pl {
2086 rad: (re * re + im * im).powf(f / T::TWO),
2087 ph: ph_times_f(im.atan2(re), f),
2088 },
2089 }
2090 }
2091 /// Returns the *n*-th root of the complex number.
2092 ///
2093 /// This computes `self^(1/n)` using floating-point exponentiation in polar form.
2094 ///
2095 /// # Panics
2096 ///
2097 /// Panics if `n == 0` (root index cannot be zero) or if the conversion from `u32` to `T` fails.
2098 ///
2099 /// # Performance
2100 ///
2101 /// ```rust
2102 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
2103 ///
2104 /// let batch = 100_000;
2105 /// let reps = 50;
2106 ///
2107 /// type F = f64;
2108 /// type C = Cpx<F>;
2109 ///
2110 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
2111 ///
2112 /// let (val,val_2): (F,F) = (3.0,4.0);
2113 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
2114 ///
2115 /// let phase: F = F::FRAC_PI_6;
2116 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
2117 ///
2118 /// // assert!((..=5).contains(&measure_cycles( || { let _ = zero.root_u32(1); }, || { let _ = zero; }, batch, reps)));
2119 /// // assert!((..=20).contains(&measure_cycles( || { let _ = zero.root_u32(2); }, || { let _ = zero; }, batch, reps)));
2120 /// // assert!((..=20).contains(&measure_cycles( || { let _ = one.root_u32(2); }, || { let _ = one; }, batch, reps)));
2121 /// // assert!((..=90).contains(&measure_cycles( || { let _ = neg_one.root_u32(2); }, || { let _ = neg_one; }, batch, reps)));
2122 /// // assert!((..=90).contains(&measure_cycles( || { let _ = j.root_u32(2); }, || { let _ = j; }, batch, reps)));
2123 /// // assert!((..=90).contains(&measure_cycles( || { let _ = neg_j.root_u32(2); }, || { let _ = neg_j; }, batch, reps)));
2124 /// // assert!((..=80).contains(&measure_cycles( || { let _ = ph.root_u32(2); }, || { let _ = ph; }, batch, reps)));
2125 /// // assert!((..=190).contains(&measure_cycles( || { let _ = ln.root_u32(2); }, || { let _ = ln; }, batch, reps)));
2126 /// // assert!((..=130).contains(&measure_cycles( || { let _ = re.root_u32(2); }, || { let _ = re; }, batch, reps)));
2127 /// // assert!((..=140).contains(&measure_cycles( || { let _ = pl.root_u32(2); }, || { let _ = pl; }, batch, reps)));
2128 /// // assert!((..=140).contains(&measure_cycles( || { let _ = im.root_u32(2); }, || { let _ = im; }, batch, reps)));
2129 /// // assert!((..=170).contains(&measure_cycles( || { let _ = re_im.root_u32(2); }, || { let _ = re_im; }, batch, reps)));
2130 /// ```
2131 pub fn root_u32(self, n: u32) -> Self {
2132 use Cpx::*;
2133 if n == 1_u32 {
2134 return self;
2135 } else if n == 0_u32 {
2136 panic!("root number cannot be zero");
2137 };
2138
2139 let canon = self.canonicalize_lazy();
2140
2141 match canon {
2142 Zero | One => canon,
2143 _ => {
2144 // Precompute conversions once
2145 let n_t: T = T::from(n).unwrap();
2146 let recip = n_t.recip();
2147 canon.powf(recip)
2148 }
2149 }
2150 }
2151 /// Raises the complex number to a complex exponent `cpx` using the polar form.
2152 ///
2153 /// This computes
2154 ///
2155 /// ```text
2156 /// z^c = r^c * e^{i * c * θ}
2157 /// ```
2158 ///
2159 /// where `r` and `θ` are the magnitude and phase of `z`.
2160 /// The result is returned in canonical form.
2161 ///
2162 /// # Mathematical note
2163 ///
2164 /// If `z = 0` and `c = a + i b`:
2165 /// - If `b ≠ 0`, the limit `lim_{z→0} z^c` does not exist;
2166 /// - If `b = 0` and `a > 0`, the limit is `0`;
2167 /// - If `b = 0` and `a = 0`, the limit is `1`;
2168 /// - If `b = 0` and `a < 0`, the limit diverges to `+∞`.
2169 ///
2170 /// This implementation panics for the non-existent case (`b ≠ 0`) and for the divergent case (`a < 0`).
2171 ///
2172 /// # Performance
2173 ///
2174 /// ```rust
2175 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
2176 ///
2177 /// let batch = 100_000;
2178 /// let reps = 50;
2179 ///
2180 /// type F = f64;
2181 /// type C = Cpx<F>;
2182 ///
2183 /// let (mut zero, mut one, mut neg_one, mut j, mut neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
2184 ///
2185 /// let (val,val_2): (F,F) = (3.0,4.0);
2186 /// let (mut re, mut im, mut re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
2187 ///
2188 /// let phase: F = F::FRAC_PI_6;
2189 /// let (mut ph, mut pl, mut ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
2190 ///
2191 /// // assert!((..=6).contains(&measure_cycles( || { let _ = re_im.powc(zero); }, || { let _ = re_im; }, batch, reps)));
2192 /// // assert!((..=8).contains(&measure_cycles( || { let _ = one.powc(re_im); }, || { let _ = one; }, batch, reps)));
2193 /// // assert!((..=20).contains(&measure_cycles( || { let _ = zero.powc(one); }, || { let _ = zero; }, batch, reps)));
2194 /// // assert!((..=15).contains(&measure_cycles( || { let _ = neg_one.powc(neg_one); }, || { let _ = neg_one; }, batch, reps)));
2195 /// // assert!((..=28).contains(&measure_cycles( || { let _ = neg_one.powc(j); }, || { let _ = neg_one; }, batch, reps)));
2196 /// // assert!((..=30).contains(&measure_cycles( || { let _ = neg_one.powc(re); }, || { let _ = neg_one; }, batch, reps)));
2197 /// // assert!((..=65).contains(&measure_cycles( || { let _ = neg_one.powc(im); }, || { let _ = neg_one; }, batch, reps)));
2198 /// // assert!((..=35).contains(&measure_cycles( || { let _ = neg_one.powc(re_im); }, || { let _ = neg_one; }, batch, reps)));
2199 /// // assert!((..=110).contains(&measure_cycles( || { let _ = neg_one.powc(ph); }, || { let _ = neg_one; }, batch, reps)));
2200 /// // assert!((..=90).contains(&measure_cycles( || { let _ = neg_one.powc(pl); }, || { let _ = neg_one; }, batch, reps)));
2201 /// // assert!((..=30).contains(&measure_cycles( || { let _ = j.powc(re); }, || { let _ = j; }, batch, reps)));
2202 /// // assert!((..=65).contains(&measure_cycles( || { let _ = neg_j.powc(im); }, || { let _ = neg_j; }, batch, reps)));
2203 /// // assert!((..=35).contains(&measure_cycles( || { let _ = ph.powc(re_im); }, || { let _ = ph; }, batch, reps)));
2204 /// // assert!((..=45).contains(&measure_cycles( || { let _ = ln.powc(re_im); }, || { let _ = ln; }, batch, reps)));
2205 /// // assert!((..=45).contains(&measure_cycles( || { let _ = re.powc(re_im); }, || { let _ = re; }, batch, reps)));
2206 /// // assert!((..=65).contains(&measure_cycles( || { let _ = pl.powc(re_im); }, || { let _ = pl; }, batch, reps)));
2207 /// // assert!((..=60).contains(&measure_cycles( || { let _ = im.powc(re_im); }, || { let _ = im; }, batch, reps)));
2208 /// // assert!((..=150).contains(&measure_cycles( || { let _ = re_im.powc(re_im); }, || { let _ = re_im; }, batch, reps)));
2209 /// ```
2210 pub fn powc(self, c: Self) -> Self {
2211 use Cpx::*;
2212 match (self, c) {
2213 (_, Zero) | (One, _) => One,
2214 (_, One) => self,
2215 (_, NegOne) => self.recip(),
2216 (Zero, x) => match x {
2217 Zero => One,
2218 One => Zero,
2219 NegOne => Re { re: T::INFINITY },
2220 Re { re } => match re {
2221 x if x > T::ZERO => Zero,
2222 x if x < T::ZERO => Re { re: T::INFINITY },
2223 _ => One,
2224 },
2225 Im { im } => {
2226 if im == T::ZERO {
2227 One
2228 } else {
2229 panic!("Zero to the complex power is undefined")
2230 }
2231 }
2232 J | NegJ => panic!("Zero to the complex power is undefined"),
2233 _ => {
2234 let (re, im) = c.re_im();
2235 match im {
2236 x if x == T::ZERO => match re {
2237 x if x > T::ZERO => Zero,
2238 x if x < T::ZERO => Re { re: T::INFINITY },
2239 _ => One,
2240 },
2241 _ => panic!("Zero to the complex power is undefined"),
2242 }
2243 }
2244 },
2245 (NegOne, x) => match x {
2246 J => Re { re: T::EXP_NEG_PI },
2247 NegJ => Re { re: T::EXP_PI },
2248 Re { re } => Ph { ph: T::PI * re },
2249 Im { im } => Re {
2250 re: T::EXP_NEG_PI.powf(im),
2251 },
2252 ReIm { re, im } => Ln {
2253 re: -T::PI * im,
2254 im: T::PI * re,
2255 },
2256 Ph { ph } => Ln {
2257 re: -T::PI * ph.sin(),
2258 im: T::PI * ph.cos(),
2259 },
2260 Pl { rad, ph } => {
2261 let new_rad = T::PI * rad;
2262 Ln {
2263 re: -new_rad * ph.sin(),
2264 im: new_rad * ph.cos(),
2265 }
2266 }
2267 Ln { re, im } => {
2268 let rad = T::PI * re.exp();
2269 Ln {
2270 re: -rad * im.sin(),
2271 im: rad * im.cos(),
2272 }
2273 }
2274 _ => unreachable!(),
2275 },
2276 (J, x) => match x {
2277 J => Re {
2278 re: T::EXP_NEG_FRAC_PI_2,
2279 },
2280 NegJ => Re {
2281 re: T::EXP_FRAC_PI_2,
2282 },
2283 Re { re } => Ph {
2284 ph: T::FRAC_PI_2 * re,
2285 },
2286 Im { im } => Re {
2287 re: T::EXP_NEG_FRAC_PI_2.powf(im),
2288 },
2289 ReIm { re, im } => Ln {
2290 re: T::NEG_FRAC_PI_2 * im,
2291 im: T::FRAC_PI_2 * re,
2292 },
2293 Ph { ph } => Ln {
2294 re: T::NEG_FRAC_PI_2 * ph.sin(),
2295 im: T::FRAC_PI_2 * ph.cos(),
2296 },
2297 Pl { rad, ph } => {
2298 let (r2, i2) = (rad * ph.cos(), rad * ph.sin());
2299 Ln {
2300 re: T::NEG_FRAC_PI_2 * i2,
2301 im: T::FRAC_PI_2 * r2,
2302 }
2303 }
2304 Ln { re, im } => {
2305 let rad = re.exp();
2306 let (r2, i2) = (rad * im.cos(), rad * im.sin());
2307 Ln {
2308 re: T::NEG_FRAC_PI_2 * i2,
2309 im: T::FRAC_PI_2 * r2,
2310 }
2311 }
2312 _ => unreachable!(),
2313 },
2314 (NegJ, x) => match x {
2315 J => Re {
2316 re: T::EXP_FRAC_PI_2,
2317 },
2318 NegJ => Re {
2319 re: T::EXP_NEG_FRAC_PI_2,
2320 },
2321 Re { re } => Ph {
2322 ph: T::NEG_FRAC_PI_2 * re,
2323 },
2324 Im { im } => Re {
2325 re: T::EXP_FRAC_PI_2.powf(im),
2326 },
2327 ReIm { re, im } => Ln {
2328 re: T::FRAC_PI_2 * im,
2329 im: T::NEG_FRAC_PI_2 * re,
2330 },
2331 Ph { ph } => Ln {
2332 re: T::FRAC_PI_2 * ph.sin(),
2333 im: T::NEG_FRAC_PI_2 * ph.cos(),
2334 },
2335 Pl { rad, ph } => {
2336 let (r2, i2) = (rad * ph.cos(), rad * ph.sin());
2337 Ln {
2338 re: T::FRAC_PI_2 * i2,
2339 im: T::NEG_FRAC_PI_2 * r2,
2340 }
2341 }
2342 Ln { re, im } => {
2343 let rad = re.exp();
2344 let (r2, i2) = (rad * im.cos(), rad * im.sin());
2345 Ln {
2346 re: T::FRAC_PI_2 * i2,
2347 im: T::NEG_FRAC_PI_2 * r2,
2348 }
2349 }
2350 _ => unreachable!(),
2351 },
2352 (Ph { ph }, x) => {
2353 let new_ph = ph.principal_val();
2354 match x {
2355 J => Re {
2356 re: (-new_ph).exp(),
2357 },
2358 NegJ => Re { re: new_ph.exp() },
2359 Re { re } => Ph { ph: new_ph * re },
2360 Im { im } => Re {
2361 re: (-new_ph * im).exp(),
2362 },
2363 ReIm { re, im } => Ln {
2364 re: -new_ph * im,
2365 im: new_ph * re,
2366 },
2367 Ph { ph: ph2 } => Ln {
2368 re: -new_ph * ph2.sin(),
2369 im: new_ph * ph2.cos(),
2370 },
2371 Pl { rad, ph: ph2 } => {
2372 let (r2, i2) = (rad * ph2.cos(), rad * ph2.sin());
2373 Ln {
2374 re: -new_ph * i2,
2375 im: new_ph * r2,
2376 }
2377 }
2378 Ln { re, im } => {
2379 let rad = re.exp();
2380 let (r2, i2) = (rad * im.cos(), rad * im.sin());
2381 Ln {
2382 re: -new_ph * i2,
2383 im: new_ph * r2,
2384 }
2385 }
2386 _ => unreachable!(),
2387 }
2388 }
2389 (Ln { re, im }, x) => {
2390 let im = im.principal_val();
2391 match x {
2392 J => Ln { re: -im, im: re },
2393 NegJ => Ln { re: im, im: -re },
2394 Re { re: r2 } => Ln {
2395 re: re * r2,
2396 im: im * r2,
2397 },
2398 Im { im: i2 } => Ln {
2399 re: -im * i2,
2400 im: re * i2,
2401 },
2402 ReIm { re: r2, im: i2 } => Ln {
2403 re: re * r2 - im * i2,
2404 im: re * i2 + im * r2,
2405 },
2406 Ph { ph } => {
2407 let (r2, i2) = (ph.cos(), ph.sin());
2408 Ln {
2409 re: re * r2 - im * i2,
2410 im: re * i2 + im * r2,
2411 }
2412 }
2413 Pl { rad, ph } => {
2414 let (r2, i2) = (rad * ph.cos(), rad * ph.sin());
2415 Ln {
2416 re: re * r2 - im * i2,
2417 im: re * i2 + im * r2,
2418 }
2419 }
2420 Ln { re: r3, im: ph } => {
2421 let rad = r3.exp();
2422 let (r2, i2) = (rad * ph.cos(), rad * ph.sin());
2423 Ln {
2424 re: re * r2 - im * i2,
2425 im: re * i2 + im * r2,
2426 }
2427 }
2428 _ => unreachable!(),
2429 }
2430 }
2431 (Pl { rad, ph }, x) => {
2432 let ph = ph.principal_val();
2433 let ln_rad = rad.ln();
2434 match x {
2435 J => Ln {
2436 re: -ph,
2437 im: ln_rad,
2438 },
2439 NegJ => Ln {
2440 re: ph,
2441 im: -ln_rad,
2442 },
2443 Re { re } => Ln {
2444 re: ln_rad * re,
2445 im: ph * re,
2446 },
2447 Im { im } => Ln {
2448 re: -ph * im,
2449 im: ln_rad * im,
2450 },
2451 ReIm { re, im } => Ln {
2452 re: ln_rad * re - ph * im,
2453 im: ln_rad * im + ph * re,
2454 },
2455 Ph { ph: ph2 } => {
2456 let (r2, i2) = (ph2.cos(), ph2.sin());
2457 Ln {
2458 re: ln_rad * r2 - ph * i2,
2459 im: ln_rad * i2 + ph * r2,
2460 }
2461 }
2462 Pl { rad: rad2, ph: ph2 } => {
2463 let (r2, i2) = (rad2 * ph2.cos(), rad2 * ph2.sin());
2464 Ln {
2465 re: ln_rad * r2 - ph * i2,
2466 im: ln_rad * i2 + ph * r2,
2467 }
2468 }
2469 Ln { re, im } => {
2470 let rad = re.exp();
2471 let (r2, i2) = (rad * im.cos(), rad * im.sin());
2472 Ln {
2473 re: ln_rad * r2 - ph * i2,
2474 im: ln_rad * i2 + ph * r2,
2475 }
2476 }
2477 _ => unreachable!(),
2478 }
2479 }
2480 (Re { re }, x) => match re {
2481 _ if re > T::ZERO => {
2482 let ln_rad = re.ln();
2483 match x {
2484 J => Ph { ph: ln_rad },
2485 NegJ => Ph { ph: -ln_rad },
2486 Re { re: r2 } => Re { re: re.powf(r2) },
2487 Im { im } => Ph { ph: ln_rad * im },
2488 ReIm { re: r2, im } => Ln {
2489 re: ln_rad * r2,
2490 im: ln_rad * im,
2491 },
2492 Ph { ph } => {
2493 let (r2, i2) = (ph.cos(), ph.sin());
2494 Ln {
2495 re: ln_rad * r2,
2496 im: ln_rad * i2,
2497 }
2498 }
2499 Pl { rad, ph } => {
2500 let (r2, i2) = (rad * ph.cos(), rad * ph.sin());
2501 Ln {
2502 re: ln_rad * r2,
2503 im: ln_rad * i2,
2504 }
2505 }
2506 Ln { re: r3, im } => {
2507 let rad = r3.exp();
2508 let (r2, i2) = (rad * im.cos(), rad * im.sin());
2509 Ln {
2510 re: ln_rad * r2,
2511 im: ln_rad * i2,
2512 }
2513 }
2514 _ => unreachable!(),
2515 }
2516 }
2517 _ if re < T::ZERO => {
2518 let ln_rad = (-re).ln();
2519 match x {
2520 J => Pl {
2521 rad: T::EXP_NEG_PI,
2522 ph: ln_rad,
2523 },
2524 NegJ => Pl {
2525 rad: T::EXP_PI,
2526 ph: -ln_rad,
2527 },
2528 Re { re: r2 } => Ln {
2529 re: ln_rad * r2,
2530 im: T::PI * r2,
2531 },
2532 Im { im } => Ln {
2533 re: -T::PI * im,
2534 im: ln_rad * im,
2535 },
2536 ReIm { re: r2, im } => Ln {
2537 re: ln_rad * r2 + -T::PI * im,
2538 im: T::PI * r2 + ln_rad * im,
2539 },
2540 Ph { ph } => {
2541 let (r2, im) = (ph.cos(), ph.sin());
2542 Ln {
2543 re: ln_rad * r2 + -T::PI * im,
2544 im: T::PI * r2 + ln_rad * im,
2545 }
2546 }
2547 Pl { rad, ph } => {
2548 let (r2, im) = (rad * ph.cos(), rad * ph.sin());
2549 Ln {
2550 re: ln_rad * r2 + -T::PI * im,
2551 im: T::PI * r2 + ln_rad * im,
2552 }
2553 }
2554 Ln { re: r3, im } => {
2555 let rad = r3.exp();
2556 let (r2, im) = (rad * im.cos(), rad * im.sin());
2557 Ln {
2558 re: ln_rad * r2 + -T::PI * im,
2559 im: T::PI * r2 + ln_rad * im,
2560 }
2561 }
2562 _ => unreachable!(),
2563 }
2564 }
2565 _ => match x {
2566 J | NegJ => panic!("Zero to the complex power is undefined"),
2567 Re { re: r2 } => match r2 {
2568 _ if r2 > T::ZERO => Zero,
2569 _ if r2 < T::ZERO => Re { re: T::infinity() },
2570 _ => One,
2571 },
2572 Im { im } => match im {
2573 _ if im == T::ZERO => One,
2574 _ => panic!("Zero to the complex power is undefined"),
2575 },
2576 ReIm { re: r2, im } => match im {
2577 _ if im == T::ZERO => match r2 {
2578 _ if r2 > T::ZERO => Zero,
2579 _ if r2 < T::ZERO => Re { re: T::infinity() },
2580 _ => One,
2581 },
2582 _ => panic!("Zero to the complex power is undefined"),
2583 },
2584 Ph { ph } => match ph.principal_val() {
2585 x if x == T::ZERO => Zero,
2586 x if x == T::PI => Re { re: T::infinity() },
2587 _ => panic!("Zero to the complex power is undefined"),
2588 },
2589 Pl { rad, ph } => match rad {
2590 _ if rad == T::ZERO => One,
2591 _ => match ph.principal_val() {
2592 x if x == T::ZERO => Zero,
2593 x if x == T::PI => Re { re: T::infinity() },
2594 _ => panic!("Zero to the complex power is undefined"),
2595 },
2596 },
2597 Ln { re: r2, im: ph } => match r2 {
2598 _ if r2 < T::LN_THRESHOLD => One,
2599 _ => match ph.principal_val() {
2600 x if x == T::ZERO => Zero,
2601 x if x == T::PI => Re { re: T::infinity() },
2602 _ => panic!("Zero to the complex power is undefined"),
2603 },
2604 },
2605 _ => unreachable!(),
2606 },
2607 },
2608 (Im { im }, x) => match im {
2609 _ if im > T::ZERO => {
2610 let ln_im = im.ln();
2611 match x {
2612 J => Pl {
2613 rad: T::EXP_NEG_FRAC_PI_2,
2614 ph: ln_im,
2615 },
2616 NegJ => Pl {
2617 rad: T::EXP_FRAC_PI_2,
2618 ph: -ln_im,
2619 },
2620 Re { re } => Ln {
2621 re: ln_im * re,
2622 im: T::FRAC_PI_2 * re,
2623 },
2624 Im { im: i2 } => Ln {
2625 re: T::NEG_FRAC_PI_2 * i2,
2626 im: ln_im * i2,
2627 },
2628 ReIm { re, im: i2 } => Ln {
2629 re: ln_im * re + T::NEG_FRAC_PI_2 * i2,
2630 im: ln_im * i2 + T::FRAC_PI_2 * re,
2631 },
2632 Ph { ph } => {
2633 let (re, i2) = (ph.cos(), ph.sin());
2634 Ln {
2635 re: ln_im * re + T::NEG_FRAC_PI_2 * i2,
2636 im: ln_im * i2 + T::FRAC_PI_2 * re,
2637 }
2638 }
2639 Pl { rad, ph } => {
2640 let (re, i2) = (rad * ph.cos(), rad * ph.sin());
2641 Ln {
2642 re: ln_im * re + T::NEG_FRAC_PI_2 * i2,
2643 im: ln_im * i2 + T::FRAC_PI_2 * re,
2644 }
2645 }
2646 Ln { re, im: i3 } => {
2647 let rad = re.exp();
2648 let (re, i2) = (rad * i3.cos(), rad * i3.sin());
2649 Ln {
2650 re: ln_im * re + T::NEG_FRAC_PI_2 * i2,
2651 im: ln_im * i2 + T::FRAC_PI_2 * re,
2652 }
2653 }
2654 _ => unreachable!(),
2655 }
2656 }
2657 _ if im < T::ZERO => {
2658 let ln_im = (-im).ln();
2659 match x {
2660 J => Pl {
2661 rad: T::EXP_FRAC_PI_2,
2662 ph: -ln_im,
2663 },
2664 NegJ => Pl {
2665 rad: T::EXP_NEG_FRAC_PI_2,
2666 ph: ln_im,
2667 },
2668 Re { re } => Ln {
2669 re: ln_im * re,
2670 im: T::NEG_FRAC_PI_2 * re,
2671 },
2672 Im { im: i2 } => Ln {
2673 re: T::FRAC_PI_2 * i2,
2674 im: ln_im * i2,
2675 },
2676 ReIm { re, im: i2 } => Ln {
2677 re: ln_im * re + T::FRAC_PI_2 * i2,
2678 im: ln_im * i2 + T::NEG_FRAC_PI_2 * re,
2679 },
2680 Ph { ph } => {
2681 let (re, i2) = (ph.cos(), ph.sin());
2682 Ln {
2683 re: ln_im * re + T::FRAC_PI_2 * i2,
2684 im: ln_im * i2 + T::NEG_FRAC_PI_2 * re,
2685 }
2686 }
2687 Pl { rad, ph } => {
2688 let (re, i2) = (rad * ph.cos(), rad * ph.sin());
2689 Ln {
2690 re: ln_im * re + T::FRAC_PI_2 * i2,
2691 im: ln_im * i2 + T::NEG_FRAC_PI_2 * re,
2692 }
2693 }
2694 Ln { re, im: ph } => {
2695 let rad = re.exp();
2696 let (re, i2) = (rad * ph.cos(), rad * ph.sin());
2697 Ln {
2698 re: ln_im * re + T::FRAC_PI_2 * i2,
2699 im: ln_im * i2 + T::NEG_FRAC_PI_2 * re,
2700 }
2701 }
2702 _ => unreachable!(),
2703 }
2704 }
2705 _ => match x {
2706 J | NegJ => panic!("Zero to the complex power is undefined"),
2707 Re { re } => match re {
2708 _ if re > T::ZERO => Zero,
2709 _ if re < T::ZERO => Re { re: T::infinity() },
2710 _ => One,
2711 },
2712 Im { im: i2 } => match i2 {
2713 _ if im == T::ZERO => One,
2714 _ => panic!("Zero to the complex power is undefined"),
2715 },
2716 ReIm { re, im: i2 } => match i2 {
2717 _ if i2 == T::ZERO => match re {
2718 _ if re > T::ZERO => Zero,
2719 _ if re < T::ZERO => Re { re: T::infinity() },
2720 _ => One,
2721 },
2722 _ => panic!("Zero to the complex power is undefined"),
2723 },
2724 Ph { ph } => match ph.principal_val() {
2725 x if x == T::ZERO => Zero,
2726 x if x == T::PI => Re { re: T::infinity() },
2727 _ => panic!("Zero to the complex power is undefined"),
2728 },
2729 Pl { rad, ph } => match rad {
2730 x if x == T::ZERO => One,
2731 _ => match ph.principal_val() {
2732 x if x == T::ZERO => Zero,
2733 x if x == T::PI => Re { re: T::infinity() },
2734 _ => panic!("Zero to the complex power is undefined"),
2735 },
2736 },
2737 Ln { re, im: i2 } => match re {
2738 x if x < T::LN_THRESHOLD => One,
2739 _ => match i2.principal_val() {
2740 x if x == T::ZERO => Zero,
2741 x if x == T::PI => Re { re: T::infinity() },
2742 _ => panic!("Zero to the complex power is undefined"),
2743 },
2744 },
2745 _ => unreachable!(),
2746 },
2747 },
2748 (ReIm { re, im }, x) => {
2749 let rad = re.hypot(im);
2750 match rad {
2751 _ if rad == T::ZERO => match x {
2752 J | NegJ => panic!("Zero to the complex power is undefined"),
2753 Re { re } => match re {
2754 _ if re > T::ZERO => Zero,
2755 _ if re < T::ZERO => Re { re: T::infinity() },
2756 _ => One,
2757 },
2758 Im { im: i2 } => match i2 {
2759 _ if im == T::ZERO => One,
2760 _ => panic!("Zero to the complex power is undefined"),
2761 },
2762 ReIm { re, im: i2 } => match i2 {
2763 _ if i2 == T::ZERO => match re {
2764 _ if re > T::ZERO => Zero,
2765 _ if re < T::ZERO => Re { re: T::infinity() },
2766 _ => One,
2767 },
2768 _ => panic!("Zero to the complex power is undefined"),
2769 },
2770 Ph { ph } => match ph.principal_val() {
2771 x if x == T::ZERO => Zero,
2772 x if x == T::PI => Re { re: T::infinity() },
2773 _ => panic!("Zero to the complex power is undefined"),
2774 },
2775 Pl { rad: rad2, ph } => match rad2 {
2776 x if x == T::ZERO => One,
2777 _ => match ph.principal_val() {
2778 x if x == T::ZERO => Zero,
2779 x if x == T::PI => Re { re: T::infinity() },
2780 _ => panic!("Zero to the complex power is undefined"),
2781 },
2782 },
2783 Ln { re, im: i2 } => match re {
2784 x if x < T::LN_THRESHOLD => One,
2785 _ => match i2.principal_val() {
2786 x if x == T::ZERO => Zero,
2787 x if x == T::PI => Re { re: T::infinity() },
2788 _ => panic!("Zero to the complex power is undefined"),
2789 },
2790 },
2791 _ => unreachable!(),
2792 },
2793 _ if rad == T::ONE => {
2794 let ph = im.atan2(re);
2795 match x {
2796 J => Re { re: (-ph).exp() },
2797 NegJ => Re { re: ph.exp() },
2798 Re { re: r2 } => Ph { ph: ph * r2 },
2799 Im { im: i2 } => Re {
2800 re: (-ph * i2).exp(),
2801 },
2802 ReIm { re: r2, im: i2 } => Ln {
2803 re: -ph * i2,
2804 im: ph * r2,
2805 },
2806 Ph { ph } => {
2807 let (r2, i2) = (ph.cos(), ph.sin());
2808 Ln {
2809 re: -ph * i2,
2810 im: ph * r2,
2811 }
2812 }
2813 Pl { rad: rad2, ph } => {
2814 let (r2, i2) = (rad2 * ph.cos(), rad2 * ph.sin());
2815 Ln {
2816 re: -ph * i2,
2817 im: ph * r2,
2818 }
2819 }
2820 Ln {
2821 re: ln_rad2,
2822 im: ph,
2823 } => {
2824 let rad2 = ln_rad2.exp();
2825 let (r2, i2) = (rad2 * ph.cos(), rad2 * ph.sin());
2826 Ln {
2827 re: -ph * i2,
2828 im: ph * r2,
2829 }
2830 }
2831 _ => unreachable!(),
2832 }
2833 }
2834 _ => {
2835 let ln_rad = rad.ln();
2836 let ph = im.atan2(re);
2837 match x {
2838 J => Ln {
2839 re: -ph,
2840 im: ln_rad,
2841 },
2842 NegJ => Ln { re: ph, im: ln_rad },
2843 Re { re: r2 } => Ln {
2844 re: ln_rad * r2,
2845 im: ph * r2,
2846 },
2847 Im { im: i2 } => Ln {
2848 re: -ph * i2,
2849 im: ln_rad * i2,
2850 },
2851 ReIm { re: r2, im: i2 } => Ln {
2852 re: ln_rad * r2 - ph * i2,
2853 im: ln_rad * i2 + ph * r2,
2854 },
2855 Ph { ph } => {
2856 let (r2, i2) = (ph.cos(), ph.sin());
2857 Ln {
2858 re: ln_rad * r2 - ph * i2,
2859 im: ln_rad * i2 + ph * r2,
2860 }
2861 }
2862 Pl { rad: rad2, ph } => {
2863 let (r2, i2) = (rad2 * ph.cos(), rad2 * ph.sin());
2864 Ln {
2865 re: ln_rad * r2 - ph * i2,
2866 im: ln_rad * i2 + ph * r2,
2867 }
2868 }
2869 Ln {
2870 re: ln_rad2,
2871 im: ph,
2872 } => {
2873 let rad2 = ln_rad2.exp();
2874 let (r2, i2) = (rad2 * ph.cos(), rad2 * ph.sin());
2875 Ln {
2876 re: ln_rad * r2 - ph * i2,
2877 im: ln_rad * i2 + ph * r2,
2878 }
2879 }
2880 _ => unreachable!(),
2881 }
2882 }
2883 }
2884 }
2885 }
2886 }
2887}
2888
2889macro_rules! impl_cpx_hash {
2890 ($float:ty) => {
2891 impl core::hash::Hash for Cpx<$float> {
2892 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2893 use Cpx::*;
2894 let canonicalized = self.canonicalize_lazy();
2895 match canonicalized {
2896 Zero => 0u8.hash(state),
2897 One => 1u8.hash(state),
2898 NegOne => 2u8.hash(state),
2899 J => 3u8.hash(state),
2900 NegJ => 4u8.hash(state),
2901 Re { re } => {
2902 5u8.hash(state);
2903 re.to_bits().hash(state);
2904 }
2905 Im { im } => {
2906 6u8.hash(state);
2907 im.to_bits().hash(state);
2908 }
2909 ReIm { re, im } => {
2910 7u8.hash(state);
2911 re.to_bits().hash(state);
2912 im.to_bits().hash(state);
2913 }
2914 Ph { ph } => {
2915 8u8.hash(state);
2916 ph.to_bits().hash(state);
2917 }
2918 Pl { rad, ph } => {
2919 9u8.hash(state);
2920 rad.to_bits().hash(state);
2921 ph.to_bits().hash(state);
2922 }
2923 Ln { .. } => unreachable!(),
2924 }
2925 }
2926 }
2927 };
2928}
2929
2930impl_cpx_hash!(f32);
2931impl_cpx_hash!(f64);
2932
2933macro_rules! comm {
2934 (($a:pat, $b:pat)) => {
2935 ($a, $b) | ($b, $a)
2936 };
2937}
2938impl<T: FloatExt> PartialEq for Cpx<T> {
2939 /// # Examples
2940 ///
2941 /// ```
2942 /// # use cpx_coords::*;
2943 /// let a = Cpx::Zero;
2944 /// let b = Cpx::Zero;
2945 /// let c = Cpx::Re { re: 1.0 };
2946 /// let d = Cpx::Re { re: 1.0 };
2947 /// let e = Cpx::Im { im: 1.0 };
2948 /// let f = Cpx::Im { im: 2.0 };
2949 /// let g = Cpx::ReIm { re: 1.0, im: 0.0 };
2950 /// let h = Cpx::ReIm { re: 1.0, im: 0.0 };
2951 /// let i = Cpx::Pl { rad: 2.0, ph: 3.0 };
2952 /// let j = Cpx::Pl { rad: 2.0, ph: 3.0 };
2953 ///
2954 /// assert_eq!(a,b);
2955 /// assert_eq!(c,d);
2956 /// assert_eq!(g,h);
2957 /// assert_eq!(i,j);
2958 /// assert_ne!(e,f);
2959 /// assert_ne!(a,c);
2960 /// ```
2961 ///
2962 /// # Performance
2963 ///
2964 /// ```rust
2965 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
2966 ///
2967 /// let batch = 100_000;
2968 /// let reps = 50;
2969 ///
2970 /// type F = f64;
2971 /// type C = Cpx<F>;
2972 ///
2973 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
2974 ///
2975 /// let (val,val_2): (F,F) = (3.0,4.0);
2976 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
2977 ///
2978 /// let phase: F = F::FRAC_PI_6;
2979 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
2980 ///
2981 /// // Identity cases:
2982 /// // assert!((..=3).contains(&measure_cycles( || { let _ = zero == zero; }, || { let _ = zero; }, batch, reps)));
2983 /// // assert!((..=3).contains(&measure_cycles( || { let _ = neg_j == neg_j; }, || { let _ = neg_j; }, batch, reps)));
2984 /// // assert!((..=14).contains(&measure_cycles( || { let _ = re == re; }, || { let _ = re; }, batch, reps)));
2985 /// // assert!((..=8).contains(&measure_cycles( || { let _ = im == im; }, || { let _ = im; }, batch, reps)));
2986 /// // assert!((..=18).contains(&measure_cycles( || { let _ = ph == ph; }, || { let _ = ph; }, batch, reps)));
2987 /// // assert!((..=25).contains(&measure_cycles( || { let _ = pl == pl; }, || { let _ = pl; }, batch, reps)));
2988 /// // assert!((..=25).contains(&measure_cycles( || { let _ = ln == ln; }, || { let _ = ln; }, batch, reps)));
2989 /// // assert!((..=16).contains(&measure_cycles( || { let _ = re_im == re_im; }, || { let _ = re_im; }, batch, reps)));
2990 ///
2991 /// // Crossing cases:
2992 /// // assert!((..=5).contains(&measure_cycles( || { let _ = zero == one; }, || { let _ = zero; }, batch, reps)));
2993 /// // assert!((..=5).contains(&measure_cycles( || { let _ = zero == ph; }, || { let _ = zero; }, batch, reps)));
2994 /// // assert!((..=10).contains(&measure_cycles( || { let _ = zero == re; }, || { let _ = zero; }, batch, reps)));
2995 /// // assert!((..=12).contains(&measure_cycles( || { let _ = zero == re_im; }, || { let _ = zero; }, batch, reps)));
2996 /// // assert!((..=10).contains(&measure_cycles( || { let _ = zero == pl; }, || { let _ = zero; }, batch, reps)));
2997 /// // assert!((..=10).contains(&measure_cycles( || { let _ = zero == ln; }, || { let _ = zero; }, batch, reps)));
2998 /// // assert!((..=10).contains(&measure_cycles( || { let _ = one == re; }, || { let _ = one; }, batch, reps)));
2999 /// // assert!((..=15).contains(&measure_cycles( || { let _ = one == ph; }, || { let _ = one; }, batch, reps)));
3000 /// // assert!((..=10).contains(&measure_cycles( || { let _ = one == pl; }, || { let _ = one; }, batch, reps)));
3001 /// // assert!((..=10).contains(&measure_cycles( || { let _ = re == im; }, || { let _ = re; }, batch, reps)));
3002 /// // assert!((..=12).contains(&measure_cycles( || { let _ = re == re_im; }, || { let _ = re; }, batch, reps)));
3003 /// // assert!((..=15).contains(&measure_cycles( || { let _ = re == ph; }, || { let _ = re; }, batch, reps)));
3004 /// // assert!((..=18).contains(&measure_cycles( || { let _ = re == pl; }, || { let _ = re; }, batch, reps)));
3005 /// // assert!((..=18).contains(&measure_cycles( || { let _ = re == ln; }, || { let _ = re; }, batch, reps)));
3006 /// // assert!((..=10).contains(&measure_cycles( || { let _ = ph == pl; }, || { let _ = ph; }, batch, reps)));
3007 /// // assert!((..=10).contains(&measure_cycles( || { let _ = ph == ln; }, || { let _ = ph; }, batch, reps)));
3008 /// // assert!((..=45).contains(&measure_cycles( || { let _ = pl == ln; }, || { let _ = pl; }, batch, reps)));
3009 /// // assert!((..=30).contains(&measure_cycles( || { let _ = re_im == ph; }, || { let _ = re_im; }, batch, reps)));
3010 /// // assert!((..=30).contains(&measure_cycles( || { let _ = re_im == pl; }, || { let _ = re_im; }, batch, reps)));
3011 /// // assert!((..=50).contains(&measure_cycles( || { let _ = re_im == ln; }, || { let _ = re_im; }, batch, reps)));
3012 /// ```
3013 #[inline(always)]
3014 fn eq(&self, other: &Self) -> bool {
3015 use Cpx::*;
3016 if matches!(
3017 (self, other),
3018 (Zero, Zero) | (One, One) | (NegOne, NegOne) | (J, J) | (NegJ, NegJ)
3019 ) {
3020 return true;
3021 }
3022 if matches!(
3023 (self, other),
3024 comm!((Zero, One))
3025 | comm!((Zero, NegOne))
3026 | comm!((Zero, J))
3027 | comm!((Zero, NegJ))
3028 | comm!((Zero, Ph { .. }))
3029 | comm!((One, NegOne))
3030 | comm!((One, J))
3031 | comm!((One, NegJ))
3032 | comm!((One, Im { .. }))
3033 | comm!((NegOne, J))
3034 | comm!((NegOne, NegJ))
3035 | comm!((NegOne, Im { .. }))
3036 | comm!((J, NegJ))
3037 | comm!((J, Re { .. }))
3038 | comm!((NegJ, Re { .. }))
3039 ) {
3040 return false;
3041 }
3042
3043 match (*self, *other) {
3044 //(Zero, Zero) | (One, One) | (NegOne, NegOne) | (J, J) | (NegJ, NegJ) => true,
3045 (Re { re: v0 }, Re { re: v1 }) | (Im { im: v0 }, Im { im: v1 }) => v0 == v1,
3046 (ReIm { re: v0, im: v1 }, ReIm { re: v2, im: v3 }) => v0 == v2 && v1 == v3,
3047 (Ph { ph: v0 }, Ph { ph: v1 }) => (v0 - v1).principal_val() == T::ZERO,
3048 (Pl { rad: v0, ph: v1 }, Pl { rad: v2, ph: v3 }) => match (v0, v2) {
3049 _ if v0 == T::ZERO && v2 == T::ZERO => true,
3050 _ if v0 == v2 => (v1 - v3).principal_val() == T::ZERO,
3051 _ => false,
3052 },
3053 (Ln { re: v0, im: v1 }, Ln { re: v2, im: v3 }) => match (v0, v2) {
3054 _ if v0 < T::LN_THRESHOLD && v2 < T::LN_THRESHOLD => true,
3055 _ if v0 == v2 => (v1 - v3).principal_val() == T::ZERO,
3056 _ => false,
3057 },
3058 //comm!((Zero, One))
3059 //| comm!((Zero, NegOne))
3060 //| comm!((Zero, J))
3061 //| comm!((Zero, NegJ))
3062 //| comm!((Zero, Ph { .. }))
3063 //| comm!((One, NegOne))
3064 //| comm!((One, J))
3065 //| comm!((One, NegJ))
3066 //| comm!((One, Im { .. }))
3067 //| comm!((NegOne, J))
3068 //| comm!((NegOne, NegJ))
3069 //| comm!((NegOne, Im { .. }))
3070 //| comm!((J, NegJ))
3071 //| comm!((J, Re { .. }))
3072 //| comm!((NegJ, Re { .. })) => false,
3073 comm!((Zero, Re { re: val }))
3074 | comm!((Zero, Im { im: val }))
3075 | comm!((Zero, Pl { rad: val, .. })) => val == T::ZERO,
3076 comm!((Zero, Ln { re: val, .. })) => val < T::LN_THRESHOLD,
3077 comm!((Zero, ReIm { re, im })) => re == T::ZERO && im == T::ZERO,
3078 comm!((One, Re { re: val })) | comm!((J, Im { im: val })) => val == T::ONE,
3079 comm!((NegOne, Re { re: val })) | comm!((NegJ, Im { im: val })) => val == T::NEG_ONE,
3080 comm!((One, ReIm { re: v0, im: v1 })) | comm!((J, ReIm { re: v1, im: v0 })) => {
3081 v0 == T::ONE && v1 == T::ZERO
3082 }
3083 comm!((NegOne, ReIm { re: v0, im: v1 })) | comm!((NegJ, ReIm { re: v1, im: v0 })) => {
3084 v0 == T::NEG_ONE && v1 == T::ZERO
3085 }
3086 comm!((One, Ph { ph })) => ph.principal_val() == T::ZERO,
3087 comm!((NegOne, Ph { ph })) => ph.principal_val() == T::PI,
3088 comm!((J, Ph { ph })) => ph.principal_val() == T::FRAC_PI_2,
3089 comm!((NegJ, Ph { ph })) => ph.principal_val() == T::NEG_FRAC_PI_2,
3090 comm!((One, Pl { rad, ph })) => {
3091 if rad != T::ONE {
3092 false
3093 } else {
3094 ph.principal_val() == T::ZERO
3095 }
3096 }
3097 comm!((NegOne, Pl { rad, ph })) => {
3098 if rad != T::ONE {
3099 false
3100 } else {
3101 ph.principal_val() == T::PI
3102 }
3103 }
3104 comm!((J, Pl { rad, ph })) => {
3105 if rad != T::ONE {
3106 false
3107 } else {
3108 ph.principal_val() == T::FRAC_PI_2
3109 }
3110 }
3111 comm!((NegJ, Pl { rad, ph })) => {
3112 if rad != T::ONE {
3113 false
3114 } else {
3115 ph.principal_val() == T::NEG_FRAC_PI_2
3116 }
3117 }
3118 comm!((One, Ln { re, im })) => {
3119 if re != T::ZERO {
3120 false
3121 } else {
3122 im.principal_val() == T::ZERO
3123 }
3124 }
3125 comm!((NegOne, Ln { re, im })) => {
3126 if re != T::ZERO {
3127 false
3128 } else {
3129 im.principal_val() == T::PI
3130 }
3131 }
3132 comm!((J, Ln { re, im })) => {
3133 if re != T::ZERO {
3134 false
3135 } else {
3136 im.principal_val() == T::FRAC_PI_2
3137 }
3138 }
3139 comm!((NegJ, Ln { re, im })) => {
3140 if re != T::ZERO {
3141 false
3142 } else {
3143 im.principal_val() == T::NEG_FRAC_PI_2
3144 }
3145 }
3146 comm!((Re { re }, Im { im })) => re == T::ZERO && im == T::ZERO,
3147 comm!((Re { re: v0 }, ReIm { re: v1, im: v2 }))
3148 | comm!((Im { im: v0 }, ReIm { re: v2, im: v1 })) => {
3149 if v2 != T::ZERO {
3150 false
3151 } else {
3152 v0 == v1
3153 }
3154 }
3155 comm!((Re { re }, Ph { ph })) => match re {
3156 r if r == T::ONE => ph.principal_val() == T::ZERO,
3157 r if r == T::NEG_ONE => ph.principal_val() == T::PI,
3158 _ => false,
3159 },
3160 comm!((Im { im }, Ph { ph })) => match im {
3161 i if i == T::ONE => ph.principal_val() == T::FRAC_PI_2,
3162 i if i == T::NEG_ONE => ph.principal_val() == T::NEG_FRAC_PI_2,
3163 _ => false,
3164 },
3165 comm!((Re { re }, Pl { rad, ph })) => match ph.principal_val() {
3166 p if p == T::ZERO => re == rad,
3167 p if p == T::PI => re == -rad,
3168 _ => false,
3169 },
3170 comm!((Im { im }, Pl { rad, ph })) => match ph.principal_val() {
3171 p if p == T::FRAC_PI_2 => im == rad,
3172 p if p == T::NEG_FRAC_PI_2 => im == -rad,
3173 _ => false,
3174 },
3175 comm!((Re { re }, Ln { re: ln_rad, im: ph })) => match ph.principal_val() {
3176 p if p == T::ZERO => re == ln_rad.exp(),
3177 p if p == T::PI => re == -ln_rad.exp(),
3178 _ => false,
3179 },
3180 comm!((Im { im }, Ln { re: ln_rad, im: ph })) => match ph.principal_val() {
3181 p if p == T::FRAC_PI_2 => im == ln_rad.exp(),
3182 p if p == T::NEG_FRAC_PI_2 => im == -ln_rad.exp(),
3183 _ => false,
3184 },
3185 comm!((Ph { ph: v0 }, Pl { rad, ph: v1 })) => match rad {
3186 r if r == T::ONE => (v0 - v1).principal_val() == T::ZERO,
3187 _ => false,
3188 },
3189 comm!((Ph { ph: v0 }, Ln { re: ln_rad, im: v1 })) => match ln_rad {
3190 r if r == T::ZERO => (v0 - v1).principal_val() == T::ZERO,
3191 _ => false,
3192 },
3193 comm!((Pl { rad, ph: v0 }, Ln { re: ln_rad, im: v1 })) => {
3194 match (v0 - v1).principal_val() {
3195 _ if rad == T::ZERO && ln_rad < T::LN_THRESHOLD => true,
3196 p if p == T::ZERO => rad.ln() == ln_rad,
3197 _ => false,
3198 }
3199 }
3200 comm!((ReIm { re, im }, Ph { ph })) => {
3201 if re.hypot(im) != T::ONE {
3202 false
3203 } else {
3204 im.atan2(re) == ph.principal_val()
3205 }
3206 }
3207 comm!((ReIm { re, im }, Pl { rad, ph })) => {
3208 if re.hypot(im) != rad {
3209 false
3210 } else {
3211 im.atan2(re) == ph.principal_val()
3212 }
3213 }
3214 comm!((ReIm { re, im }, Ln { re: ln_rad, im: ph })) => {
3215 if re.hypot(im) != ln_rad.exp() {
3216 false
3217 } else {
3218 im.atan2(re) == ph.principal_val()
3219 }
3220 }
3221 _ => unreachable!(), //comm!((ReIm { re, im }, x)) => {
3222 // let (rhs_re, rhs_im) = x.re_im();
3223 // re == rhs_re && im == rhs_im
3224 //}
3225 }
3226 }
3227}
3228
3229impl<T: FloatExt> Neg for Cpx<T> {
3230 type Output = Self;
3231 /// # Examples
3232 ///
3233 /// ```
3234 /// use cpx_coords::*;
3235 /// let z: Cpx<f64> = Cpx::Zero;
3236 /// //assert_eq!(-z, Cpx::Zero);
3237 ///
3238 /// let o: Cpx<f64> = Cpx::One;
3239 /// //assert_eq!(-o, Cpx::NegOne);
3240 ///
3241 /// let n: Cpx<f64> = Cpx::NegOne;
3242 /// //assert_eq!(-n, Cpx::One);
3243 ///
3244 /// let j: Cpx<f64> = Cpx::J;
3245 /// //assert_eq!(-j, Cpx::NegJ);
3246 ///
3247 /// let neg_j: Cpx<f64> = Cpx::NegJ;
3248 /// //assert_eq!(-neg_j, Cpx::J);
3249 ///
3250 /// let r = Cpx::Re { re: 3.0f32 };
3251 /// //assert_eq!(-r, Cpx::Re { re: -3.0 });
3252 ///
3253 /// let i = Cpx::Im { im: 2.0f32 };
3254 /// //assert_eq!(-i, Cpx::Im { im: -2.0 });
3255 ///
3256 /// let p = Cpx::Ph { ph: f32::FRAC_PI_2 };
3257 /// assert_eq!(-p, Cpx::Ph { ph: -f32::FRAC_PI_2 });
3258 ///
3259 /// let c = Cpx::ReIm { re: 1.0f32, im: 2.0 };
3260 /// //assert_eq!(-c, Cpx::ReIm { re: -1.0, im: -2.0 });
3261 ///
3262 /// let l = Cpx::Ln { re: 1.0f32, im: 3.0 };
3263 /// //assert_eq!(-l, Cpx::Ln { re: 1.0, im: (3.0 + f32::PI).principal_val() });
3264 ///
3265 /// let pl = Cpx::Pl { rad: 2.0f32, ph: 0.5 };
3266 /// //assert_eq!(-pl, Cpx::Pl { rad: 2.0, ph: (0.5 + f32::PI).principal_val() });
3267 /// ```
3268 ///
3269 /// ```rust
3270 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
3271 ///
3272 /// let batch = 100_000;
3273 /// let reps = 50;
3274 ///
3275 /// type F = f64;
3276 /// type C = Cpx<F>;
3277 ///
3278 /// let (zero, one, neg_one, j, neg_j) = (C::Zero, C::One, C::NegOne, C::J, C::NegJ);
3279 ///
3280 /// let (val,val_2): (F,F) = (3.0,4.0);
3281 /// let (re, im, re_im) = (C::Re { re: val}, C::Im { im: val}, C::ReIm { re: val, im: val_2});
3282 ///
3283 /// let phase: F = F::FRAC_PI_6;
3284 /// let (ph, pl, ln) = (C::Ph { ph: phase}, C::Pl { rad: val.abs(), ph: phase}, C::Ln { re: val, im: phase});
3285 ///
3286 /// // assert!((..=1).contains(&measure_cycles( || { let _ = -zero; }, || { let _ = zero; }, batch, reps)));
3287 /// // assert!((..=1).contains(&measure_cycles( || { let _ = -one; }, || { let _ = one; }, batch, reps)));
3288 /// // assert!((..=2).contains(&measure_cycles( || { let _ = -j; }, || { let _ = j; }, batch, reps)));
3289 /// // assert!((..=7).contains(&measure_cycles( || { let _ = -re; }, || { let _ = re; }, batch, reps)));
3290 /// // assert!((..=5).contains(&measure_cycles( || { let _ = -im; }, || { let _ = im; }, batch, reps)));
3291 /// // assert!((..=11).contains(&measure_cycles( || { let _ = -re_im; }, || { let _ = re_im; }, batch, reps)));
3292 /// // assert!((..=6).contains(&measure_cycles( || { let _ = -ph; }, || { let _ = ph; }, batch, reps)));
3293 /// // assert!((..=6).contains(&measure_cycles( || { let _ = -pl; }, || { let _ = pl; }, batch, reps)));
3294 /// // assert!((..=6).contains(&measure_cycles( || { let _ = -ln; }, || { let _ = ln; }, batch, reps)));
3295 /// ```
3296 #[inline(always)]
3297 fn neg(self) -> Self {
3298 use Cpx::*;
3299 if let Zero = self {
3300 return Zero;
3301 }
3302 if let One = self {
3303 return NegOne;
3304 }
3305 if let NegOne = self {
3306 return One;
3307 }
3308 if let J = self {
3309 return NegJ;
3310 }
3311 if let NegJ = self {
3312 return J;
3313 }
3314 match self {
3315 Re { re } => Re { re: -re },
3316 Im { im } => Im { im: -im },
3317 ReIm { re, im } => ReIm { re: -re, im: -im },
3318 Ph { ph } => match ph {
3319 _ if ph > T::ZERO => Ph { ph: ph - T::PI },
3320 _ => Ph { ph: ph + T::PI },
3321 },
3322 Pl { rad, ph } => match ph {
3323 _ if ph > T::ZERO => Pl {
3324 rad,
3325 ph: ph - T::PI,
3326 },
3327 _ => Pl {
3328 rad,
3329 ph: ph + T::PI,
3330 },
3331 },
3332 Ln { re, im: ph } => match ph {
3333 _ if ph > T::ZERO => Ln { re, im: ph - T::PI },
3334 _ => Ln { re, im: ph + T::PI },
3335 },
3336 _ => unreachable!(),
3337 }
3338 }
3339}
3340
3341impl<T> Add for Cpx<T>
3342where
3343 T: FloatExt,
3344{
3345 type Output = Self;
3346
3347 /// # Examples
3348 ///
3349 /// ```
3350 /// # use cpx_coords::*;
3351 /// use Cpx::*;
3352 ///
3353 /// let z = Zero;
3354 /// let o = One;
3355 /// let n = NegOne;
3356 /// let j = J;
3357 /// let nj = NegJ;
3358 ///
3359 /// assert_eq!(z + o, o);
3360 /// assert_eq!(o + n, Zero);
3361 /// assert_eq!(j + nj, Zero);
3362 ///
3363 /// assert_eq!(o + j, ReIm { re: 1.0, im: 1.0 });
3364 /// assert_eq!(n + nj, ReIm { re: -1.0, im: -1.0 });
3365 ///
3366 /// let r1 = Re { re: 1.5 };
3367 /// let r2 = Re { re: 0.5 };
3368 /// assert_eq!(r1 + r2, Re { re: 2.0 });
3369 ///
3370 /// let i1 = Im { im: 2.0 };
3371 /// let i2 = Im { im: 3.0 };
3372 /// assert_eq!(i1 + i2, Im { im: 5.0 });
3373 ///
3374 /// let cc1 = ReIm { re: 1.0, im: 2.0 };
3375 /// let cc2 = ReIm { re: 3.0, im: -1.0 };
3376 /// assert_eq!(cc1 + cc2, ReIm { re: 4.0, im: 1.0 });
3377 /// ```
3378 ///
3379 /// ## Performance
3380 ///
3381 /// ```rust
3382 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
3383 ///
3384 /// let batch = 100_000;
3385 /// let reps = 50;
3386 ///
3387 /// type F = f64;
3388 /// type C = Cpx<F>;
3389 ///
3390 /// let zero = C::Zero;
3391 /// let one = C::One;
3392 /// let neg_one = C::NegOne;
3393 /// let j = C::J;
3394 /// let neg_j = C::NegJ;
3395 /// let re = C::Re { re: 2.0};
3396 /// let im = C::Im { im: 3.0};
3397 /// let ph = C::Ph { ph: F::FRAC_PI_4};
3398 /// let ph2 = C::Ph { ph: F::FRAC_PI_6};
3399 /// let re_im = C::ReIm { re: 3.0, im: 4.0};
3400 /// let pl = C::Pl { rad: 2.0, ph: F::FRAC_PI_6};
3401 /// let ln = C::Ln { re: 1.0, im: F::FRAC_PI_6};
3402 ///
3403 /// // assert!((..=5).contains(&measure_cycles( || { let _ = zero + zero; }, || { let _ = zero; }, batch, reps)));
3404 /// // assert!((..=5).contains(&measure_cycles( || { let _ = one + one; }, || { let _ = one; }, batch, reps)));
3405 /// // assert!((..=6).contains(&measure_cycles( || { let _ = one + j; }, || { let _ = one; }, batch, reps)));
3406 /// // assert!((..=6).contains(&measure_cycles( || { let _ = one + im; }, || { let _ = one; }, batch, reps)));
3407 /// // assert!((..=12).contains(&measure_cycles( || { let _ = one + re; }, || { let _ = one; }, batch, reps)));
3408 /// // assert!((..=12).contains(&measure_cycles( || { let _ = one + re_im; }, || { let _ = one; }, batch, reps)));
3409 /// // assert!((..=12).contains(&measure_cycles( || { let _ = re + re; }, || { let _ = re; }, batch, reps)));
3410 /// // assert!((..=12).contains(&measure_cycles( || { let _ = re + re_im; }, || { let _ = re; }, batch, reps)));
3411 /// // assert!((..=15).contains(&measure_cycles( || { let _ = re_im + re_im; }, || { let _ = re_im; }, batch, reps)));
3412 /// // assert!((..=55).contains(&measure_cycles( || { let _ = ph + ph2; }, || { let _ = ph; }, batch, reps)));
3413 /// // assert!((..=40).contains(&measure_cycles( || { let _ = ph + one; }, || { let _ = ph; }, batch, reps)));
3414 /// // assert!((..=50).contains(&measure_cycles( || { let _ = ph + neg_one; }, || { let _ = ph; }, batch, reps)));
3415 /// // assert!((..=55).contains(&measure_cycles( || { let _ = ph + j; }, || { let _ = ph; }, batch, reps)));
3416 ///
3417 /// // assert!((..=90).contains(&measure_cycles( || { let _ = pl + one; }, || { let _ = pl; }, batch, reps)));
3418 /// // assert!((..=120).contains(&measure_cycles( || { let _ = ln + one; }, || { let _ = ln; }, batch, reps)));
3419 /// // assert!((..=180).contains(&measure_cycles( || { let _ = pl + pl; }, || { let _ = pl; }, batch, reps)));
3420 /// // assert!((..=280).contains(&measure_cycles( || { let _ = ln + ln; }, || { let _ = ln; }, batch, reps)));
3421 /// ```
3422 #[inline(always)]
3423 fn add(self, other: Self) -> Self {
3424 use Cpx::*;
3425
3426 match (self, other) {
3427 comm!((Zero, x)) => x,
3428 comm!((One, NegOne)) | comm!((J, NegJ)) => Zero,
3429 (One, One) => Re { re: T::TWO },
3430 comm!((One, J)) => ReIm {
3431 re: T::ONE,
3432 im: T::ONE,
3433 },
3434 comm!((One, NegJ)) => ReIm {
3435 re: T::ONE,
3436 im: T::NEG_ONE,
3437 },
3438 (NegOne, NegOne) => Re { re: -T::TWO },
3439 comm!((NegOne, J)) => ReIm {
3440 re: T::NEG_ONE,
3441 im: T::ONE,
3442 },
3443 comm!((NegOne, NegJ)) => ReIm {
3444 re: T::NEG_ONE,
3445 im: T::NEG_ONE,
3446 },
3447 (J, J) => Im { im: T::TWO },
3448 (NegJ, NegJ) => Im { im: -T::TWO },
3449 comm!((One, Re { re })) => Re { re: re + T::ONE },
3450 comm!((One, Im { im })) => ReIm { re: T::ONE, im },
3451 comm!((One, ReIm { re, im })) => ReIm {
3452 re: re + T::ONE,
3453 im,
3454 },
3455 comm!((NegOne, Re { re })) => Re {
3456 re: re + T::NEG_ONE,
3457 },
3458 comm!((NegOne, Im { im })) => ReIm { re: T::NEG_ONE, im },
3459 comm!((NegOne, ReIm { re, im })) => ReIm {
3460 re: re + T::NEG_ONE,
3461 im,
3462 },
3463 comm!((J, Re { re })) => ReIm { re, im: T::ONE },
3464 comm!((J, Im { im })) => Im { im: im + T::ONE },
3465 comm!((J, ReIm { re, im })) => ReIm {
3466 re,
3467 im: im + T::ONE,
3468 },
3469 comm!((NegJ, Re { re })) => ReIm { re, im: T::NEG_ONE },
3470 comm!((NegJ, Im { im })) => Im {
3471 im: im + T::NEG_ONE,
3472 },
3473 comm!((NegJ, ReIm { re, im })) => ReIm {
3474 re,
3475 im: im + T::NEG_ONE,
3476 },
3477 (Re { re }, Re { re: r2 }) => Re { re: re + r2 },
3478 (Im { im }, Im { im: i2 }) => Im { im: im + i2 },
3479 comm!((Re { re }, Im { im })) => ReIm { re, im },
3480 comm!((ReIm { re, im }, Re { re: r2 })) => ReIm { re: re + r2, im },
3481 comm!((ReIm { re, im }, Im { im: i2 })) => ReIm { re, im: im + i2 },
3482 (ReIm { re, im }, ReIm { re: r2, im: i2 }) => ReIm {
3483 re: re + r2,
3484 im: im + i2,
3485 },
3486 (Ph { ph: ph1 }, Ph { ph: ph2 }) => {
3487 // Optimized summation of two unit-phase values in polar form.
3488 // Instead of converting to Cartesian and back (~170 cycles),
3489 // this uses the identity:
3490 // e^{iφ₁} + e^{iφ₂} = 2cos((φ₁ - φ₂)/2) · e^{i(φ₁ + φ₂)/2}
3491 // Reduces cost to ~55 cycles by requiring only one cosine call.
3492 let (diff, ph) = (
3493 (ph1 - ph2).principal_val() / T::TWO,
3494 (ph1 + ph2).principal_val() / T::TWO,
3495 );
3496 let rad = T::TWO * diff.cos();
3497 Pl { rad, ph }
3498 }
3499 comm!((Ph { ph: ph1 }, One)) => {
3500 let diff = ph1.principal_val() / T::TWO;
3501 let rad = T::TWO * diff.cos();
3502 Pl { rad, ph: diff } // In this case, average phase equals phase difference.
3503 }
3504 comm!((Ph { ph: ph1 }, NegOne)) => {
3505 let diff = (ph1 - T::PI).principal_val() / T::TWO;
3506 let rad = T::TWO * diff.cos();
3507 Pl { rad, ph: diff } // In this case, average phase equals phase difference.
3508 }
3509 comm!((Ph { ph: ph1 }, J)) => {
3510 let (diff, ph) = (
3511 (ph1 + T::NEG_FRAC_PI_2).principal_val() / T::TWO,
3512 (ph1 + T::FRAC_PI_2).principal_val() / T::TWO,
3513 );
3514 let rad = T::TWO * diff.cos();
3515 Pl { rad, ph }
3516 }
3517 comm!((Ph { ph: ph1 }, NegJ)) => {
3518 let (diff, ph) = (
3519 (ph1 + T::FRAC_PI_2).principal_val() / T::TWO,
3520 (ph1 + T::NEG_FRAC_PI_2).principal_val() / T::TWO,
3521 );
3522 let rad = T::TWO * diff.cos();
3523 Pl { rad, ph }
3524 }
3525 comm!((One, x)) => {
3526 let (re, im) = x.re_im();
3527 ReIm {
3528 re: re + T::ONE,
3529 im,
3530 }
3531 }
3532 comm!((NegOne, x)) => {
3533 let (re, im) = x.re_im();
3534 ReIm {
3535 re: re + T::NEG_ONE,
3536 im,
3537 }
3538 }
3539 comm!((J, x)) => {
3540 let (re, im) = x.re_im();
3541 ReIm {
3542 re,
3543 im: im + T::ONE,
3544 }
3545 }
3546 comm!((NegJ, x)) => {
3547 let (re, im) = x.re_im();
3548 ReIm {
3549 re,
3550 im: im + T::NEG_ONE,
3551 }
3552 }
3553 comm!((Re { re }, x)) => {
3554 let (r2, im) = x.re_im();
3555 ReIm { re: re + r2, im }
3556 }
3557 comm!((Im { im }, x)) => {
3558 let (re, i2) = x.re_im();
3559 ReIm { re, im: im + i2 }
3560 }
3561 comm!((ReIm { re, im }, x)) => {
3562 let (r2, i2) = x.re_im();
3563 ReIm {
3564 re: re + r2,
3565 im: im + i2,
3566 }
3567 }
3568 (x, y) => {
3569 let (re, im) = x.re_im();
3570 let (r2, i2) = y.re_im();
3571 ReIm {
3572 re: re + r2,
3573 im: im + i2,
3574 }
3575 }
3576 }
3577 }
3578}
3579impl<T> Sub for Cpx<T>
3580where
3581 T: FloatExt,
3582{
3583 type Output = Self;
3584 /// # Examples
3585 ///
3586 /// ```
3587 /// use cpx_coords::*;
3588 /// use Cpx::*;
3589 ///
3590 /// let a = Re { re: 2.0 };
3591 /// let b = Re { re: 1.5 };
3592 /// let c = Im { im: 3.0 };
3593 /// let d = Im { im: 0.5 };
3594 /// let z = Zero;
3595 ///
3596 /// assert_eq!(a - b, Re { re: 0.5 });
3597 /// assert_eq!(c - d, Im { im: 2.5 });
3598 ///
3599 /// let cc1 = ReIm { re: 3.0, im: 4.0 };
3600 /// let cc2 = ReIm { re: 1.0, im: 1.0 };
3601 /// assert_eq!(cc1 - cc2, ReIm { re: 2.0, im: 3.0 });
3602 ///
3603 /// assert_eq!(a - z, a);
3604 /// assert_eq!(z - a, Re { re: -2.0 });
3605 /// ```
3606 #[inline(always)]
3607 fn sub(self, other: Self) -> Self {
3608 self + (-other)
3609 }
3610}
3611
3612impl<T> Mul for Cpx<T>
3613where
3614 T: FloatExt,
3615{
3616 type Output = Self;
3617 /// Implements multiplication for symbolic complex types [`Cpx<T>`].
3618 ///
3619 /// # Examples
3620 ///
3621 /// ```rust
3622 /// use cpx_coords::{Cpx, FloatExt};
3623 /// use Cpx::*;
3624 ///
3625 /// type F = f64;
3626 ///
3627 /// let result = Zero * Re { re: 1.0};
3628 /// assert_eq!(result, Zero);
3629 ///
3630 /// let result = One * Im { im: -1.0};
3631 /// assert_eq!(result, Im { im: -1.0});
3632 ///
3633 /// let result = NegOne * Ph { ph: F::FRAC_PI_2 };
3634 /// assert_eq!(result, -Ph { ph: F::FRAC_PI_2 });
3635 ///
3636 /// let result = J::<F> * J;
3637 /// assert_eq!(result, NegOne);
3638 ///
3639 /// let result = J::<F> * NegJ;
3640 /// assert_eq!(result, One);
3641 ///
3642 /// let result = J * Re { re: 1.0};
3643 /// assert_eq!(result, J);
3644 ///
3645 /// let result = J * Re { re: -1.0};
3646 /// assert_eq!(result, NegJ);
3647 ///
3648 /// let result = J * Re { re: 3.0};
3649 /// assert_eq!(result, Im { im: 3.0});
3650 ///
3651 /// let result = J * Im { im: 1.0};
3652 /// assert_eq!(result, NegOne);
3653 ///
3654 /// let result = J * Im { im: -1.0};
3655 /// assert_eq!(result, One);
3656 ///
3657 /// let result = J * Im { im: 2.0};
3658 /// assert_eq!(result, Re { re: -2.0});
3659 ///
3660 /// let result = NegJ * Re { re: 1.0};
3661 /// assert_eq!(result, NegJ);
3662 ///
3663 /// let result = NegJ * Re { re: -1.0};
3664 /// assert_eq!(result, J);
3665 ///
3666 /// let result = NegJ * Re { re: 1.5};
3667 /// assert_eq!(result, Im { im: -1.5});
3668 ///
3669 /// let result = NegJ * Im { im: 1.0};
3670 /// assert_eq!(result, One);
3671 ///
3672 /// let result = NegJ * Im { im: -1.0};
3673 /// assert_eq!(result, NegOne);
3674 ///
3675 /// let result = NegJ * Im { im: 0.25};
3676 /// assert_eq!(result, Re { re: 0.25});
3677 ///
3678 /// let result = Re { re: 2.0} * Re { re: 3.0};
3679 /// assert_eq!(result, Re { re: 6.0});
3680 ///
3681 /// let result = Re { re: 1.0} * Im { im: 3.0};
3682 /// assert_eq!(result, Im { im: 3.0});
3683 ///
3684 /// let result = Im { im: 2.0} * Im { im: 4.0};
3685 /// assert_eq!(result, Re { re: -8.0});
3686 ///
3687 /// let result = Ph { ph: F::FRAC_PI_4} * Ph { ph: F::FRAC_PI_4};
3688 /// assert_eq!(result, Ph { ph: F::FRAC_PI_2});
3689 ///
3690 /// let result = Ph { ph: F::FRAC_PI_4} * J;
3691 /// assert_eq!(result, Ph { ph: F::FRAC_PI_4 * 3.0});
3692 ///
3693 /// let result = Ph { ph: F::FRAC_PI_4} * NegJ;
3694 /// assert_eq!(result, Ph { ph: -F::FRAC_PI_4});
3695 ///
3696 /// let result = Ph { ph: F::FRAC_PI_4} * Re { re: 2.0};
3697 /// assert_eq!(result, Pl { rad: 2.0, ph: F::FRAC_PI_4});
3698 ///
3699 /// let result = Ph { ph: F::FRAC_PI_4} * Im { im: -2.0};
3700 /// assert_eq!(result, Pl { rad: 2.0, ph: -F::FRAC_PI_4});
3701 ///
3702 /// let result = Ln { re: 2.0_f64.ln(), im: F::FRAC_PI_6} * Ln { re: 3.0_f64.ln(), im: F::FRAC_PI_6};
3703 /// assert_eq!(result.rad(), 6.0_f64);
3704 /// assert_eq!(result.ph(), F::FRAC_PI_3);
3705 ///
3706 /// let result = Ln { re: 2.0_f64.ln(), im: F::FRAC_PI_4} * J;
3707 /// assert_eq!(result.ph(), (F::FRAC_PI_4 + F::FRAC_PI_2).principal_val());
3708 ///
3709 /// let result = Ln { re: 2.0_f64.ln(), im: F::FRAC_PI_4} * Re { re: -1.0};
3710 /// assert_eq!(result.ph(), (F::FRAC_PI_4 + F::PI).principal_val());
3711 ///
3712 /// let result = Pl { rad: 2.0, ph: F::FRAC_PI_4} * J;
3713 /// assert_eq!(result.ph(), (F::FRAC_PI_4 + F::FRAC_PI_2).principal_val());
3714 ///
3715 /// let result = Pl { rad: 3.0, ph: F::FRAC_PI_4} * Pl { rad: 4.0, ph: F::FRAC_PI_2};
3716 /// assert_eq!(result.rad(), 12.0);
3717 /// assert_eq!(result.ph(), (F::FRAC_PI_4 + F::FRAC_PI_2).principal_val());
3718 ///
3719 /// let result = Pl { rad: 2.0, ph: F::FRAC_PI_4} * Ln { re: 2.0_f64.ln(), im: F::FRAC_PI_2};
3720 /// assert_eq!(result.rad(), 4.0);
3721 /// assert_eq!(result.ph(), (F::FRAC_PI_4 + F::FRAC_PI_2).principal_val());
3722 /// ```
3723 ///
3724 /// ## Performance Characteristics
3725 ///
3726 /// ### Per-Variant Multiplication Costs
3727 ///
3728 /// As summarized in the table below, the baseline cost of multiplying two general complex numbers in Cartesian form (`Ccs * Ccs`) is ≤40 CPU cycles.
3729 /// In contrast, multiplications involving the most common symbolic constants, `Zero`, `One`, `J`, `NegJ`, typically used in Pauli matrices,
3730 /// complete in ≤10 cycles due to constant folding and early-return optimizations.
3731 ///
3732 /// When extending to Hadamard-like operations involving `FRAC_1_SQRT_2`, the relevant cases (`Real`, `Imag`) remain efficient, with costs ≤20 cycles.
3733 /// This implies that multiplications involving normalized real or imaginary values are 2×–4× faster than the general Cartesian case.
3734 ///
3735 /// To support a broader class of normalized complex numbers, we include `Phase` and `PL` (polar form with non-unit modulus).
3736 /// Multiplications between `Phase`, `PL`, and the symbolic constants (`Zero`, `One`, `J`, `NegJ`, `Real`, `Imag`) also complete in ≤20 cycles,
3737 /// maintaining the 2× performance advantage over general Cartesian operations.
3738 ///
3739 /// The most expensive multiplication path, consuming ~70–75 cycles, arises from fallback cases such as `Phase * Ccs`, where computing the phase
3740 /// requires a call to `atan2`. Despite its cost, we retain the `Ccs` form for its essential role in supporting complex addition and numeric fallback.
3741 ///
3742 /// Additionally, we include the `Ln` coordinate form to represent complex logarithms. While `Ln * Ln` multiplication is comparable in cost
3743 /// to `PL * PL` (~20 cycles), its primary benefit lies in facilitating exponential and power computations. Specifically, raising a complex number
3744 /// to another (`Cpx::powc`) is implemented efficiently via the identity:
3745 ///
3746 /// ```text
3747 /// base^exp = exp(ln(base) * exp)
3748 /// ```
3749 ///
3750 /// Thus, `Ln` serves as a useful intermediate that pairs naturally with `Ccs` in exponentiation logic.
3751 ///
3752 /// ### Benchmark Harness (Partial)
3753 ///
3754 /// ```rust
3755 /// use cpx_coords::{Cpx, FloatExt, measure_cycles};
3756 ///
3757 /// let batch = 100_000;
3758 /// let reps = 50;
3759 ///
3760 /// type F = f64;
3761 /// type C = Cpx<F>;
3762 ///
3763 /// let zero = C::Zero;
3764 /// let one = C::One;
3765 /// let neg_one = C::NegOne;
3766 /// let j = C::J;
3767 /// let neg_j = C::NegJ;
3768 /// let re = C::Re { re: 2.0};
3769 /// let im = C::Im { im: 3.0};
3770 /// let ph = C::Ph { ph: F::FRAC_PI_4};
3771 /// let ph2 = C::Ph { ph: F::FRAC_PI_6};
3772 /// let re_im = C::ReIm { re: 3.0, im: 4.0};
3773 /// let pl = C::Pl { rad: 2.0, ph: F::FRAC_PI_6};
3774 /// let ln = C::Ln { re: 1.0, im: F::FRAC_PI_6};
3775 ///
3776 /// // assert!((..=2).contains(&measure_cycles( || { let _ = zero * zero; }, || { let _ = zero; }, batch, reps)));
3777 /// // assert!((..=2).contains(&measure_cycles( || { let _ = one * one; }, || { let _ = one; }, batch, reps)));
3778 /// // assert!((..=2).contains(&measure_cycles( || { let _ = one * j; }, || { let _ = one; }, batch, reps)));
3779 /// // assert!((..=3).contains(&measure_cycles( || { let _ = j * one; }, || { let _ = one; }, batch, reps)));
3780 /// // assert!((..=3).contains(&measure_cycles( || { let _ = one * im; }, || { let _ = one; }, batch, reps)));
3781 /// // assert!((..=3).contains(&measure_cycles( || { let _ = one * re_im; }, || { let _ = one; }, batch, reps)));
3782 ///
3783 /// // assert!((..=3).contains(&measure_cycles( || { let _ = neg_one * one; }, || { let _ = neg_one; }, batch, reps)));
3784 /// assert!((..=17).contains(&measure_cycles( || { let _ = neg_one * j; }, || { let _ = neg_one; }, batch, reps)));
3785 /// // assert!((..=5).contains(&measure_cycles( || { let _ = j * neg_one; }, || { let _ = neg_one; }, batch, reps)));
3786 /// // assert!((..=20).contains(&measure_cycles( || { let _ = neg_one * im; }, || { let _ = neg_one; }, batch, reps)));
3787 /// // assert!((..=28).contains(&measure_cycles( || { let _ = neg_one * re_im; }, || { let _ = neg_one; }, batch, reps)));
3788 ///
3789 /// // assert!((17..=20).contains(&measure_cycles( || { let _ = re * neg_one; }, || { let _ = -re; }, batch, reps)));
3790 /// // assert!((0..=5).contains(&measure_cycles( || { let _ = re * neg_one; }, || { let _ = -re; }, batch, reps)));
3791 /// // assert!((17..=20).contains(&measure_cycles( || { let _ = im * neg_one; }, || { let _ = -im; }, batch, reps)));
3792 /// // assert!((20..=25).contains(&measure_cycles( || { let _ = re_im * neg_one; }, || { let _ = -re_im; }, batch, reps)));
3793 /// // assert!((20..=25).contains(&measure_cycles( || { let _ = ph * neg_one; }, || { let _ = -ph; }, batch, reps)));
3794 /// // assert!((20..=25).contains(&measure_cycles( || { let _ = pl * neg_one; }, || { let _ = -pl; }, batch, reps)));
3795 ///
3796 /// // assert!((..=8).contains(&measure_cycles( || { let _ = j * j; }, || { let _ = j; }, batch, reps)));
3797 /// // assert!((..=10).contains(&measure_cycles( || { let _ = j * re; }, || { let _ = j; }, batch, reps)));
3798 ///
3799 /// // assert!((..=13).contains(&measure_cycles( || { let _ = re * re; }, || { let _ = re; }, batch, reps)));
3800 /// // assert!((..=18).contains(&measure_cycles( || { let _ = re * re_im; }, || { let _ = re; }, batch, reps)));
3801 /// // assert!((..=35).contains(&measure_cycles( || { let _ = re_im * re_im; }, || { let _ = re_im; }, batch, reps)));
3802 ///
3803 /// // assert!((..=15).contains(&measure_cycles( || { let _ = ph * ph2; }, || { let _ = ph; }, batch, reps)));
3804 /// // assert!((..=55).contains(&measure_cycles( || { let _ = ph * j; }, || { let _ = ph; }, batch, reps)));
3805 ///
3806 /// // assert!((..=180).contains(&measure_cycles( || { let _ = pl * pl; }, || { let _ = pl; }, batch, reps)));
3807 /// // assert!((..=230).contains(&measure_cycles( || { let _ = ln * ln; }, || { let _ = ln; }, batch, reps)));
3808 /// ```
3809 #[inline(always)]
3810 fn mul(self, other: Self) -> Self {
3811 use Cpx::*;
3812 // Fast path for common constants
3813 if matches!(self, Zero) || matches!(other, Zero) {
3814 return Zero;
3815 }
3816 if let One = self {
3817 return other;
3818 }
3819 if let One = other {
3820 return self;
3821 }
3822 if let NegOne = self {
3823 return -other;
3824 }
3825 if let NegOne = other {
3826 return -self;
3827 }
3828
3829 match (self, other) {
3830 //comm!((Zero, _)) => Zero,
3831 //comm!((One, x)) => x,w
3832 //comm!((NegOne, Re { re })) => Re { re: -re},
3833 //comm!((NegOne, x)) => -x,
3834 comm!((J, NegJ)) => One,
3835 (J, J) | (NegJ, NegJ) => NegOne,
3836 comm!((J, Re { re })) => Im { im: re },
3837 comm!((J, Im { im })) => Re { re: -im },
3838 comm!((J, ReIm { re, im })) => ReIm { re: -im, im: re },
3839 comm!((NegJ, Re { re })) => Im { im: -re },
3840 comm!((NegJ, Im { im })) => Re { re: im },
3841 comm!((NegJ, ReIm { re, im })) => ReIm { re: im, im: -re },
3842 comm!((J, Ph { ph })) => Ph {
3843 ph: ph + T::FRAC_PI_2,
3844 },
3845 comm!((J, Pl { rad, ph })) => Pl {
3846 rad,
3847 ph: ph + T::FRAC_PI_2,
3848 },
3849 comm!((J, Ln { re, im })) => Ln {
3850 re,
3851 im: im + T::FRAC_PI_2,
3852 },
3853 comm!((NegJ, Ph { ph })) => Ph {
3854 ph: ph + T::NEG_FRAC_PI_2,
3855 },
3856 comm!((NegJ, Pl { rad, ph })) => Pl {
3857 rad,
3858 ph: ph + T::NEG_FRAC_PI_2,
3859 },
3860 comm!((NegJ, Ln { re, im })) => Ln {
3861 re,
3862 im: im + T::NEG_FRAC_PI_2,
3863 },
3864 (Re { re }, Re { re: r2 }) => Re { re: re * r2 },
3865 comm!((Re { re }, Im { im })) => Im { im: re * im },
3866 comm!((Re { re }, ReIm { re: r2, im })) => ReIm {
3867 re: re * r2,
3868 im: re * im,
3869 },
3870 comm!((Re { re }, Ph { ph })) => match re {
3871 _ if re < T::ZERO => Pl {
3872 rad: -re,
3873 ph: ph + T::PI,
3874 },
3875 _ => Pl { rad: re, ph },
3876 },
3877 comm!((Re { re }, Pl { rad, ph })) => match rad * re {
3878 x if x < T::ZERO => Pl {
3879 rad: -x,
3880 ph: ph + T::PI,
3881 },
3882 x => Pl { rad: x, ph },
3883 },
3884 comm!((Re { re: r2 }, Ln { re, im })) => match r2 * re.exp() {
3885 x if x < T::ZERO => Pl {
3886 rad: -x,
3887 ph: im + T::PI,
3888 },
3889 x => Pl { rad: x, ph: im },
3890 },
3891 (Im { im }, Im { im: i2 }) => Re { re: -im * i2 },
3892 comm!((Im { im }, ReIm { re, im: i2 })) => ReIm {
3893 re: -im * i2,
3894 im: im * re,
3895 },
3896 comm!((Im { im }, Ph { ph })) => match im {
3897 x if x < T::ZERO => Pl {
3898 rad: -x,
3899 ph: ph + T::NEG_FRAC_PI_2,
3900 },
3901 x => Pl {
3902 rad: x,
3903 ph: ph + T::FRAC_PI_2,
3904 },
3905 },
3906 comm!((Im { im }, Pl { rad, ph })) => match rad * im {
3907 x if x < T::ZERO => Pl {
3908 rad: -x,
3909 ph: ph + T::NEG_FRAC_PI_2,
3910 },
3911 x => Pl {
3912 rad: x,
3913 ph: ph + T::FRAC_PI_2,
3914 },
3915 },
3916 comm!((Im { im }, Ln { re, im: i2 })) => match im * re.exp() {
3917 x if x < T::ZERO => Pl {
3918 rad: -x,
3919 ph: i2 + T::NEG_FRAC_PI_2,
3920 },
3921 x => Pl {
3922 rad: x,
3923 ph: i2 + T::FRAC_PI_2,
3924 },
3925 },
3926 comm!((Ph { ph: ph2 }, Pl { rad, ph })) => Pl { rad, ph: ph + ph2 },
3927 comm!((Ph { ph }, Ln { re, im })) => Ln { re, im: im + ph },
3928 comm!((Ph { ph }, ReIm { re, im })) => Pl {
3929 rad: re.hypot(im),
3930 ph: ph + im.atan2(re),
3931 },
3932 (Ph { ph }, Ph { ph: p2 }) => Ph { ph: ph + p2 },
3933 (Pl { rad, ph }, Pl { rad: rad2, ph: ph2 }) => Pl {
3934 rad: rad * rad2,
3935 ph: ph + ph2,
3936 },
3937 (Ln { re, im }, Ln { re: r2, im: i2 }) => Ln {
3938 re: re + r2,
3939 im: im + i2,
3940 },
3941 comm!((Pl { rad, ph }, Ln { re, im })) => Pl {
3942 rad: rad * re.exp(),
3943 ph: ph + im,
3944 },
3945 comm!((Pl { rad, ph }, ReIm { re, im })) => Pl {
3946 rad: rad * re.hypot(im),
3947 ph: ph + im.atan2(re),
3948 },
3949 comm!((Ln { re, im }, ReIm { re: r2, im: i2 })) => Pl {
3950 rad: re.exp() + r2.hypot(i2),
3951 ph: im + i2.atan2(r2),
3952 },
3953 (ReIm { re, im }, ReIm { re: r2, im: i2 }) => ReIm {
3954 re: (re * r2) - (im * i2),
3955 im: (re * i2) + (im * r2),
3956 },
3957 _ => unreachable!(),
3958 }
3959 }
3960}
3961
3962#[allow(clippy::suspicious_arithmetic_impl)]
3963impl<T> Div for Cpx<T>
3964where
3965 T: FloatExt,
3966{
3967 type Output = Self;
3968
3969 fn div(self, other: Self) -> Self {
3970 self * other.recip()
3971 }
3972}
3973
3974impl<T> Add<T> for Cpx<T>
3975where
3976 T: FloatExt,
3977{
3978 type Output = Self;
3979 fn add(self, other: T) -> Self {
3980 use Cpx::*;
3981 self + Re { re: other }
3982 }
3983}
3984
3985impl<T> Sub<T> for Cpx<T>
3986where
3987 T: FloatExt,
3988{
3989 type Output = Self;
3990 fn sub(self, other: T) -> Self {
3991 self + (-other)
3992 }
3993}
3994
3995impl<T> Mul<T> for Cpx<T>
3996where
3997 T: FloatExt,
3998{
3999 type Output = Self;
4000 fn mul(self, other: T) -> Self {
4001 self * Cpx::Re { re: other }
4002 }
4003}
4004
4005impl<T> Div<T> for Cpx<T>
4006where
4007 T: FloatExt,
4008{
4009 type Output = Self;
4010 fn div(self, other: T) -> Self {
4011 self / Cpx::Re { re: other }
4012 }
4013}
4014macro_rules! impl_cpx_assign_ops {
4015 // Implements trait for Cpx<T> <op>= Cpx<T>
4016 ($trait:ident, $method:ident, $op:tt, Self) => {
4017 impl<T> core::ops::$trait for Cpx<T>
4018 where
4019 T: FloatExt,
4020 {
4021 fn $method(&mut self, rhs: Self) {
4022 *self = *self $op rhs;
4023 }
4024 }
4025 };
4026
4027 // Implements trait for Cpx<T> <op>= T
4028 ($trait:ident, $method:ident, $op:tt, $rhs:ty) => {
4029 impl<T> core::ops::$trait<$rhs> for Cpx<T>
4030 where
4031 T: FloatExt,
4032 {
4033 fn $method(&mut self, rhs: $rhs) {
4034 *self = *self $op rhs;
4035 }
4036 }
4037 };
4038}
4039
4040impl_cpx_assign_ops!(AddAssign, add_assign, +, Self);
4041impl_cpx_assign_ops!(SubAssign, sub_assign, -, Self);
4042impl_cpx_assign_ops!(MulAssign, mul_assign, *, Self);
4043impl_cpx_assign_ops!(DivAssign, div_assign, /, Self);
4044
4045impl_cpx_assign_ops!(AddAssign, add_assign, +, T);
4046impl_cpx_assign_ops!(SubAssign, sub_assign, -, T);
4047impl_cpx_assign_ops!(MulAssign, mul_assign, *, T);
4048impl_cpx_assign_ops!(DivAssign, div_assign, /, T);
4049
4050macro_rules! measure_batch {
4051 ($func:expr, $batch:expr) => {{
4052 unsafe { __cpuid(0) };
4053 let start = unsafe { _rdtsc() };
4054 for _ in 0..$batch {
4055 $func();
4056 black_box(());
4057 }
4058 let end = unsafe { __rdtscp(&mut 0) };
4059 unsafe { __cpuid(0) };
4060 end.saturating_sub(start)
4061 }};
4062}
4063
4064macro_rules! measure_cycles_common {
4065 ($f:expr, $g:expr, $batch:expr, $reps:expr) => {{
4066 let mut total: u64 = 0;
4067 for _ in 0..$reps {
4068 let total_f = measure_batch!($f, $batch);
4069 let total_g = measure_batch!($g, $batch);
4070 let delta = total_f.saturating_sub(total_g);
4071 total += delta / $batch;
4072 }
4073 total / $reps as u64
4074 }};
4075}
4076
4077#[inline(never)]
4078pub fn measure_cycles<F, G>(f: F, g: G, batch: u64, reps: u32) -> u64
4079where
4080 F: Fn(),
4081 G: Fn(),
4082{
4083 use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc};
4084 use core::hint::black_box;
4085
4086 measure_cycles_common!(f, g, batch, reps)
4087}
4088
4089#[inline(never)]
4090pub fn measure_cycles_mut<F, G>(mut f: F, mut g: G, batch: u64, reps: u32) -> u64
4091where
4092 F: FnMut(),
4093 G: FnMut(),
4094{
4095 use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc};
4096 use core::hint::black_box;
4097
4098 measure_cycles_common!(f, g, batch, reps)
4099}
4100
4101/// Private module to seal the `UnsignedIndex` trait.
4102mod sealed {
4103 pub trait Sealed {}
4104 impl Sealed for u32 {}
4105 impl Sealed for u64 {}
4106}
4107
4108use core::convert::{TryFrom, TryInto};
4109use core::fmt::Debug;
4110
4111pub trait CpxAsKey:
4112 sealed::Sealed
4113 + Copy
4114 + Ord
4115 + PartialOrd
4116 + Hash
4117 + Eq
4118 + PartialEq
4119 + TryFrom<usize>
4120 + TryInto<usize>
4121 + 'static
4122{
4123}
4124
4125impl CpxAsKey for u32 {}
4126impl CpxAsKey for u64 {}
4127
4128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4129pub enum CpxKey<T: CpxAsKey> {
4130 Zero,
4131 One,
4132 NegOne,
4133 J,
4134 NegJ,
4135 Re { re_bits: T },
4136 Im { im_bits: T },
4137 Ph { ph_bits: T },
4138 ReIm { re_bits: T, im_bits: T },
4139 Ln { re_bits: T, im_bits: T },
4140 Pl { rad_bits: T, ph_bits: T },
4141}
4142
4143macro_rules! impl_cpx_to_key {
4144 ($float:ty => $uint:ty) => {
4145 impl From<Cpx<$float>> for CpxKey<$uint> {
4146 fn from(z: Cpx<$float>) -> Self {
4147 match z {
4148 Cpx::Zero => CpxKey::Zero,
4149 Cpx::One => CpxKey::One,
4150 Cpx::NegOne => CpxKey::NegOne,
4151 Cpx::J => CpxKey::J,
4152 Cpx::NegJ => CpxKey::NegJ,
4153 Cpx::Re { re } => CpxKey::Re {
4154 re_bits: re.to_bits(),
4155 },
4156 Cpx::Im { im } => CpxKey::Im {
4157 im_bits: im.to_bits(),
4158 },
4159 Cpx::Ph { ph } => CpxKey::Ph {
4160 ph_bits: ph.to_bits(),
4161 },
4162 Cpx::ReIm { re, im } => CpxKey::ReIm {
4163 re_bits: re.to_bits(),
4164 im_bits: im.to_bits(),
4165 },
4166 Cpx::Ln { re, im } => CpxKey::Ln {
4167 re_bits: re.to_bits(),
4168 im_bits: im.to_bits(),
4169 },
4170 Cpx::Pl { rad, ph } => CpxKey::Pl {
4171 rad_bits: rad.to_bits(),
4172 ph_bits: ph.to_bits(),
4173 },
4174 }
4175 }
4176 }
4177 };
4178}
4179
4180impl_cpx_to_key!(f32 => u32);
4181impl_cpx_to_key!(f64 => u64);
4182
4183macro_rules! impl_key_to_cpx {
4184 ($uint:ty => $float:ty) => {
4185 impl From<CpxKey<$uint>> for Cpx<$float> {
4186 fn from(z: CpxKey<$uint>) -> Self {
4187 match z {
4188 CpxKey::Zero => Cpx::Zero,
4189 CpxKey::One => Cpx::One,
4190 CpxKey::NegOne => Cpx::NegOne,
4191 CpxKey::J => Cpx::J,
4192 CpxKey::NegJ => Cpx::NegJ,
4193 CpxKey::Re { re_bits } => Cpx::Re {
4194 re: <$float>::from_bits(re_bits),
4195 },
4196 CpxKey::Im { im_bits } => Cpx::Im {
4197 im: <$float>::from_bits(im_bits),
4198 },
4199 CpxKey::Ph { ph_bits } => Cpx::Ph {
4200 ph: <$float>::from_bits(ph_bits),
4201 },
4202 CpxKey::ReIm { re_bits, im_bits } => Cpx::ReIm {
4203 re: <$float>::from_bits(re_bits),
4204 im: <$float>::from_bits(im_bits),
4205 },
4206 CpxKey::Ln { re_bits, im_bits } => Cpx::Ln {
4207 re: <$float>::from_bits(re_bits),
4208 im: <$float>::from_bits(im_bits),
4209 },
4210 CpxKey::Pl { rad_bits, ph_bits } => Cpx::Pl {
4211 rad: <$float>::from_bits(rad_bits),
4212 ph: <$float>::from_bits(ph_bits),
4213 },
4214 }
4215 }
4216 }
4217 };
4218}
4219
4220impl_key_to_cpx!(u32 => f32);
4221impl_key_to_cpx!(u64 => f64);
4222
4223impl<T: CpxAsKey> CpxKey<T> {
4224 /// ````
4225 /// use cpx_coords::{Cpx, FloatExt, CpxKey, CpxAsKey};
4226 ///
4227 /// // A simple test that CpxKey can be treated as the Key of BTreeMap. Therefore, a map (CpxKey, separable_state) can be used to denote superposition state.
4228 /// /// use std::collections::BTreeMap;
4229 /// //let mut map: BTreeMap<CpxKey<u32>, i32> = BTreeMap::new();
4230 /// //map.insert(CpxKey::One, 42);
4231 ///
4232 /// // A simple test that CpxKey can be treated as the Key of HashMap. Therefore, a map (CpxKey, separable_state) can be used to denote superposition state.
4233 /// //use std::collections::HashMap;
4234 /// //let mut map: HashMap<CpxKey<u32>,i32> = HashMap::new();
4235 /// //map.insert(CpxKey::One, 42);
4236 ///
4237 /// type F = f64;
4238 /// let re_im: Cpx<F> = Cpx::ReIm { re: 0.6, im: 0.8};
4239 /// let key: CpxKey<u64> = CpxKey::from(re_im.clone());
4240 /// let recovered: Cpx<F> = Cpx::from(key);
4241 /// assert_eq!(re_im,recovered);
4242 /// ````
4243 pub fn null(self) -> Self {
4244 self
4245 }
4246}