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}