1use std::ops::{Add, Div, Mul, Neg, Sub};
4
5#[derive(Clone, Copy, Debug)]
6pub struct C64 {
7 pub re: f64,
8 pub im: f64,
9}
10
11impl PartialEq for C64 {
13 fn eq(&self, other: &C64) -> bool {
14 self.bits() == other.bits()
15 }
16}
17impl Eq for C64 {}
18
19impl C64 {
20 pub const ZERO: C64 = C64 { re: 0.0, im: 0.0 };
21 pub const ONE: C64 = C64 { re: 1.0, im: 0.0 };
22 pub const I: C64 = C64 { re: 0.0, im: 1.0 };
23
24 pub fn new(re: f64, im: f64) -> C64 {
25 C64 { re, im }
26 }
27
28 pub fn real(re: f64) -> C64 {
29 C64 { re, im: 0.0 }
30 }
31
32 pub fn bits(self) -> (u64, u64) {
33 (canonical(self.re), canonical(self.im))
34 }
35
36 pub fn is_finite(self) -> bool {
37 self.re.is_finite() && self.im.is_finite()
38 }
39
40 pub fn is_zero(self) -> bool {
41 self.bits() == C64::ZERO.bits()
42 }
43
44 pub fn is_real(self) -> bool {
45 canonical(self.im) == canonical(0.0)
46 }
47
48 pub fn conj(self) -> C64 {
49 C64::new(self.re, -self.im)
50 }
51
52 pub fn norm_sqr(self) -> f64 {
53 self.re * self.re + self.im * self.im
54 }
55
56 pub fn abs(self) -> f64 {
57 libm::hypot(self.re, self.im)
58 }
59
60 pub fn inv(self) -> C64 {
63 let d = self.norm_sqr();
64 match (self.re == 0.0, self.im == 0.0) {
65 (false, true) => C64::new(1.0 / self.re, -self.im / d),
66 (true, false) => C64::new(self.re / d, -1.0 / self.im),
67 _ => C64::new(self.re / d, -self.im / d),
68 }
69 }
70
71 pub fn exp(self) -> C64 {
72 let r = crate::exp(self.re);
73 let (sin, cos) = libm::sincos(self.im);
74 C64::new(r * cos, r * sin)
75 }
76
77 pub fn exp_m1(self) -> C64 {
79 let half = libm::sin(self.im * 0.5);
80 C64::new(
81 libm::expm1(self.re) * libm::cos(self.im) - 2.0 * half * half,
82 crate::exp(self.re) * libm::sin(self.im),
83 )
84 }
85
86 pub fn powi(self, n: u32) -> C64 {
87 let mut out = C64::ONE;
88 for _ in 0..n {
89 out = out * self;
90 }
91 out
92 }
93
94 pub fn scale(self, k: f64) -> C64 {
95 C64::new(self.re * k, self.im * k)
96 }
97
98 pub fn over(self, k: f64) -> C64 {
99 C64::new(self.re / k, self.im / k)
100 }
101}
102
103pub fn canonical(x: f64) -> u64 {
104 if x == 0.0 {
105 0f64.to_bits()
106 } else {
107 x.to_bits()
108 }
109}
110
111impl Add for C64 {
112 type Output = C64;
113 fn add(self, o: C64) -> C64 {
114 C64::new(self.re + o.re, self.im + o.im)
115 }
116}
117impl Sub for C64 {
118 type Output = C64;
119 fn sub(self, o: C64) -> C64 {
120 C64::new(self.re - o.re, self.im - o.im)
121 }
122}
123impl Mul for C64 {
124 type Output = C64;
125 fn mul(self, o: C64) -> C64 {
126 C64::new(
127 self.re * o.re - self.im * o.im,
128 self.re * o.im + self.im * o.re,
129 )
130 }
131}
132impl Div for C64 {
133 type Output = C64;
134 fn div(self, o: C64) -> C64 {
135 if o.im == 0.0 && o.re != 0.0 {
136 return C64::new(self.re / o.re, self.im / o.re);
137 }
138 if o.re == 0.0 && o.im != 0.0 {
139 return C64::new(self.im / o.im, -self.re / o.im);
140 }
141 let d = o.norm_sqr();
142 C64::new(
143 (self.re * o.re + self.im * o.im) / d,
144 (self.im * o.re - self.re * o.im) / d,
145 )
146 }
147}
148impl Neg for C64 {
149 type Output = C64;
150 fn neg(self) -> C64 {
151 C64::new(-self.re, -self.im)
152 }
153}