Skip to main content

abels_complex/complex/
polar.rs

1pub type ComplexPolar32 = ComplexPolar<f32>;
2pub type ComplexPolar64 = ComplexPolar<f64>;
3use crate::traits::Number;
4use core::fmt;
5
6use super::Complex as Rectangular;
7use core::ops::*;
8
9/// Creates a complex number in polar form.
10#[inline(always)]
11#[must_use]
12pub const fn complex_polar<FT>(abs: FT, arg: FT) -> ComplexPolar<FT> {
13    ComplexPolar::new(abs, arg)
14}
15
16/// A complex number in polar form.
17#[derive(Clone, Copy, PartialEq, Debug, Default)]
18#[repr(C)]
19pub struct ComplexPolar<FT> {
20    pub abs: FT,
21    pub arg: FT,
22}
23
24impl<FT> ComplexPolar<FT> {
25    /// Creates a complex number.
26    pub const fn new(abs: FT, arg: FT) -> Self {
27        Self { abs, arg }
28    }
29}
30
31impl<FT: Number> ComplexPolar<FT> {
32    pub const ZERO: Self = Self::new(FT::ZERO, FT::ZERO);
33    pub const ONE: Self = Self::new(FT::ONE, FT::ZERO);
34
35    /// Computes the conjugate.                        
36    pub fn conjugate(self) -> Self {
37        Self::new(self.abs, -self.arg)
38    }
39
40    /// Computes the real component.
41    pub fn re(self) -> FT {
42        self.abs * self.arg.cos()
43    }
44
45    /// Computes the imaginary component.
46    pub fn im(self) -> FT {
47        self.abs * self.arg.sin()
48    }
49
50    /// Computes the squared absolute value.
51    pub fn abs_sq(self) -> FT {
52        self.abs * self.abs
53    }
54
55    /// Computes the reciprocal.
56    pub fn recip(self) -> Self {
57        Self::new(self.abs.recip(), -self.arg)
58    }
59
60    /// Computes the principle square root.
61    pub fn sqrt(self) -> Self {
62        let two = FT::ONE + FT::ONE;
63        Self::new(self.abs.sqrt(), self.arg / two)
64    }
65
66    /// Convert to rectangular form.
67    pub fn to_rectangular(self) -> Rectangular<FT> {
68        let (sin, cos) = self.arg.sin_cos();
69        Rectangular::new(cos, sin) * self.abs
70    }
71
72    /// Computes `e^self` where `e` is the base of the natural logarithm.
73    pub fn exp(self) -> Self {
74        self.to_rectangular().exp()
75    }
76
77    /// Computes `2^self`.
78    pub fn exp2(self) -> Self {
79        self.to_rectangular().exp2()
80    }
81
82    /// Computes the principle natural logarithm.
83    pub fn ln(self) -> Rectangular<FT> {
84        Rectangular::new(self.abs.ln(), self.arg)
85    }
86
87    /// Computes the principle logarithm in base 2.
88    pub fn log2(self) -> Rectangular<FT> {
89        self.ln() / FT::LN_2()
90    }
91
92    /// Computes the principle logarithm in base 10.
93    pub fn log10(self) -> Rectangular<FT> {
94        self.ln() / FT::LN_10()
95    }
96
97    /// Raises `self` to a floating point power.
98    pub fn powf(self, x: FT) -> Self {
99        if x < FT::ZERO && self.abs == FT::ZERO {
100            return Self::ZERO;
101        }
102        Self::new(self.abs.powf(x), self.arg * x)
103    }
104
105    /// Raises `self` to an integer power.
106    pub fn powi(self, n: i32) -> Self {
107        if n < 0 && self.abs == FT::ZERO {
108            return Self::ZERO;
109        }
110        Self::new(self.abs.powi(n), self.arg * FT::from_i32(n))
111    }
112
113    /// Normalizes the absolute value and the argument into the range `[0, ∞)` and `(-π, +π]` respectively.
114    pub fn normalize(mut self) -> Self {
115        self.arg = self.arg.rem_euclid(&FT::TAU());
116        if self.abs < FT::ZERO {
117            self.abs = -self.abs;
118            if self.arg <= FT::ZERO {
119                self.arg += FT::PI();
120            } else {
121                self.arg -= FT::PI();
122            }
123        } else if self.arg > FT::PI() {
124            self.arg -= FT::TAU();
125        } else if self.arg <= -FT::PI() {
126            self.arg += FT::TAU();
127        }
128        self
129    }
130}
131
132impl<FT: Number> Mul for ComplexPolar<FT> {
133    type Output = Self;
134    fn mul(mut self, other: Self) -> Self {
135        self *= other;
136        self
137    }
138}
139
140impl<FT: Number> Mul<FT> for ComplexPolar<FT> {
141    type Output = Self;
142    fn mul(mut self, re: FT) -> Self::Output {
143        self *= re;
144        self
145    }
146}
147
148impl<FT: Number> MulAssign for ComplexPolar<FT> {
149    fn mul_assign(&mut self, other: Self) {
150        self.abs *= other.abs;
151        self.arg += other.arg;
152    }
153}
154
155impl<FT: Number> MulAssign<FT> for ComplexPolar<FT> {
156    fn mul_assign(&mut self, re: FT) {
157        self.abs *= re;
158    }
159}
160
161impl<FT: Number> Div for ComplexPolar<FT> {
162    type Output = Self;
163    fn div(mut self, other: Self) -> Self {
164        self /= other;
165        self
166    }
167}
168
169impl<FT: Number> Div<FT> for ComplexPolar<FT> {
170    type Output = Self;
171    fn div(mut self, re: FT) -> Self {
172        self /= re;
173        self
174    }
175}
176
177impl<FT: Number> DivAssign for ComplexPolar<FT> {
178    fn div_assign(&mut self, other: Self) {
179        *self *= other.recip();
180    }
181}
182
183impl<FT: Number> DivAssign<FT> for ComplexPolar<FT> {
184    fn div_assign(&mut self, re: FT) {
185        self.abs /= re;
186    }
187}
188
189impl<FT: Number> Neg for ComplexPolar<FT> {
190    type Output = Self;
191    fn neg(mut self) -> Self {
192        self.abs = -self.abs;
193        self
194    }
195}
196
197impl<FT: Number + fmt::Display> fmt::Display for ComplexPolar<FT> {
198    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199        fn fmt_x<FT: fmt::Display>(f: &mut fmt::Formatter, x: FT) -> fmt::Result {
200            if let Some(p) = f.precision() {
201                write!(f, "{x:.*}", p)
202            } else {
203                write!(f, "{x}")
204            }
205        }
206        let pi_radians = self.arg / FT::PI();
207        fmt_x(f, self.abs)?;
208        if pi_radians == FT::ZERO || self.abs == FT::ZERO {
209            Ok(())
210        } else if pi_radians == FT::ONE {
211            write!(f, "e^iπ")
212        } else {
213            write!(f, "e^")?;
214            fmt_x(f, pi_radians)?;
215            write!(f, "iπ")
216        }
217    }
218}
219
220impl<FT: Number> From<FT> for ComplexPolar<FT> {
221    fn from(value: FT) -> Self {
222        Self::new(value, FT::ZERO)
223    }
224}
225
226#[cfg(feature = "approx")]
227use approx::{AbsDiffEq, RelativeEq, UlpsEq};
228
229#[cfg(feature = "approx")]
230impl<FT: AbsDiffEq + Copy> AbsDiffEq for ComplexPolar<FT>
231where
232    <FT as AbsDiffEq>::Epsilon: Copy,
233{
234    type Epsilon = <FT as AbsDiffEq>::Epsilon;
235    fn default_epsilon() -> Self::Epsilon {
236        FT::default_epsilon()
237    }
238    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
239        FT::abs_diff_eq(&self.abs, &other.abs, epsilon)
240            && FT::abs_diff_eq(&self.arg, &other.arg, epsilon)
241    }
242}
243
244#[cfg(feature = "approx")]
245impl<FT: RelativeEq + Copy> RelativeEq for ComplexPolar<FT>
246where
247    <FT as AbsDiffEq>::Epsilon: Copy,
248{
249    fn default_max_relative() -> Self::Epsilon {
250        FT::default_max_relative()
251    }
252    fn relative_eq(
253        &self,
254        other: &Self,
255        epsilon: Self::Epsilon,
256        max_relative: Self::Epsilon,
257    ) -> bool {
258        FT::relative_eq(&self.abs, &other.abs, epsilon, max_relative)
259            && FT::relative_eq(&self.arg, &other.arg, epsilon, max_relative)
260    }
261}
262
263#[cfg(feature = "approx")]
264impl<FT: UlpsEq + Copy> UlpsEq for ComplexPolar<FT>
265where
266    <FT as AbsDiffEq>::Epsilon: Copy,
267{
268    fn default_max_ulps() -> u32 {
269        FT::default_max_ulps()
270    }
271    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
272        FT::ulps_eq(&self.abs, &other.abs, epsilon, max_ulps)
273            && FT::ulps_eq(&self.arg, &other.arg, epsilon, max_ulps)
274    }
275}