1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
6
7#[repr(C)]
8#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct Complex<T> {
10 re: T,
11 im: T,
12}
13
14impl<T> Complex<T> {
15 pub const fn new(re: T, im: T) -> Self {
16 Self { re, im }
17 }
18
19 pub fn real(&self) -> &T {
20 &self.re
21 }
22
23 pub fn imaginary(&self) -> &T {
24 &self.im
25 }
26
27 pub fn into_parts(self) -> (T, T) {
28 (self.re, self.im)
29 }
30
31 pub fn real_value(self) -> T
32 where
33 T: Copy,
34 {
35 self.re
36 }
37
38 pub fn imaginary_value(self) -> T
39 where
40 T: Copy,
41 {
42 self.im
43 }
44
45 pub fn set_real(&mut self, value: T) {
46 self.re = value;
47 }
48
49 pub fn set_imaginary(&mut self, value: T) {
50 self.im = value;
51 }
52}
53
54impl Complex<f32> {
55 pub const fn zero() -> Self {
56 Self::new(0.0, 0.0)
57 }
58
59 pub const fn one() -> Self {
60 Self::new(1.0, 0.0)
61 }
62}
63
64impl Complex<f64> {
65 pub const fn zero() -> Self {
66 Self::new(0.0, 0.0)
67 }
68
69 pub const fn one() -> Self {
70 Self::new(1.0, 0.0)
71 }
72
73 pub const fn from_real(value: f64) -> Self {
74 Self::new(value, 0.0)
75 }
76
77 #[cfg(feature = "std")]
78 pub fn argument(self) -> f64 {
79 self.im.atan2(self.re)
80 }
81}
82
83#[cfg(feature = "std")]
84impl Complex<f32> {
85 pub fn argument(self) -> f32 {
86 self.im.atan2(self.re)
87 }
88}
89
90impl<T> Complex<T>
91where
92 T: Copy + Neg<Output = T>,
93{
94 pub fn conjugate(self) -> Self {
95 Self::new(self.re, -self.im)
96 }
97}
98
99impl<T> Complex<T>
100where
101 T: Copy + Add<Output = T> + Mul<Output = T>,
102{
103 pub fn norm_squared(self) -> T {
104 self.re * self.re + self.im * self.im
105 }
106}
107
108#[cfg(feature = "std")]
109impl Complex<f64> {
110 pub fn norm(self) -> f64 {
111 self.re.hypot(self.im)
112 }
113}
114
115impl<T> Neg for Complex<T>
116where
117 T: Neg<Output = T>,
118{
119 type Output = Self;
120
121 fn neg(self) -> Self {
122 Self::new(-self.re, -self.im)
123 }
124}
125
126impl<T> Add for Complex<T>
127where
128 T: Add<Output = T>,
129{
130 type Output = Self;
131
132 fn add(self, rhs: Self) -> Self {
133 Self::new(self.re + rhs.re, self.im + rhs.im)
134 }
135}
136
137impl<T> Sub for Complex<T>
138where
139 T: Sub<Output = T>,
140{
141 type Output = Self;
142
143 fn sub(self, rhs: Self) -> Self {
144 Self::new(self.re - rhs.re, self.im - rhs.im)
145 }
146}
147
148impl<T> Mul for Complex<T>
149where
150 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
151{
152 type Output = Self;
153
154 fn mul(self, rhs: Self) -> Self {
155 Self::new(
156 self.re * rhs.re - self.im * rhs.im,
157 self.re * rhs.im + self.im * rhs.re,
158 )
159 }
160}
161
162impl<T> Div for Complex<T>
163where
164 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
165{
166 type Output = Self;
167
168 fn div(self, rhs: Self) -> Self {
169 let denominator = rhs.re * rhs.re + rhs.im * rhs.im;
170 Self::new(
171 (self.re * rhs.re + self.im * rhs.im) / denominator,
172 (self.im * rhs.re - self.re * rhs.im) / denominator,
173 )
174 }
175}
176
177impl<T> Mul<T> for Complex<T>
178where
179 T: Copy + Mul<Output = T>,
180{
181 type Output = Self;
182
183 fn mul(self, rhs: T) -> Self {
184 Self::new(self.re * rhs, self.im * rhs)
185 }
186}
187
188impl<T> Div<T> for Complex<T>
189where
190 T: Copy + Div<Output = T>,
191{
192 type Output = Self;
193
194 fn div(self, rhs: T) -> Self {
195 Self::new(self.re / rhs, self.im / rhs)
196 }
197}
198
199impl<T> AddAssign for Complex<T>
200where
201 T: AddAssign,
202{
203 fn add_assign(&mut self, rhs: Self) {
204 self.re += rhs.re;
205 self.im += rhs.im;
206 }
207}
208
209impl<T> SubAssign for Complex<T>
210where
211 T: SubAssign,
212{
213 fn sub_assign(&mut self, rhs: Self) {
214 self.re -= rhs.re;
215 self.im -= rhs.im;
216 }
217}
218
219impl<T> MulAssign for Complex<T>
220where
221 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
222{
223 fn mul_assign(&mut self, rhs: Self) {
224 *self = *self * rhs;
225 }
226}
227
228impl<T> DivAssign for Complex<T>
229where
230 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
231{
232 fn div_assign(&mut self, rhs: Self) {
233 *self = *self / rhs;
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::Complex;
240
241 #[test]
242 fn complex_arithmetic_preserves_components() {
243 let left = Complex::new(1.0_f64, 2.0);
244 let right = Complex::new(3.0_f64, -1.0);
245 assert_eq!(left + right, Complex::new(4.0, 1.0));
246 assert_eq!(left - right, Complex::new(-2.0, 3.0));
247 assert_eq!(left * right, Complex::new(5.0, 5.0));
248 assert_eq!(left.conjugate(), Complex::new(1.0, -2.0));
249 }
250
251 #[test]
252 fn division_recovers_the_input_value() {
253 let value = Complex::new(1.0_f64, 2.0);
254 let factor = Complex::new(3.0_f64, -1.0);
255 assert_eq!((value * factor) / factor, value);
256 }
257
258 #[test]
259 fn norm_squared_is_available_in_each_runtime_configuration() {
260 assert_eq!(Complex::new(3.0_f64, 4.0).norm_squared(), 25.0);
261 }
262
263 #[test]
264 fn components_are_observed_and_replaced_through_methods() {
265 let mut value = Complex::new(1.0_f64, -2.0);
266 assert_eq!(value.real_value(), 1.0);
267 assert_eq!(value.imaginary_value(), -2.0);
268 value.set_real(3.0);
269 value.set_imaginary(4.0);
270 assert_eq!(value.into_parts(), (3.0, 4.0));
271 }
272
273 #[cfg(feature = "std")]
274 #[test]
275 fn norm_is_the_euclidean_magnitude() {
276 assert_eq!(Complex::new(3.0_f64, 4.0).norm(), 5.0);
277 }
278
279 #[cfg(feature = "std")]
280 #[test]
281 fn argument_uses_the_component_quadrants() {
282 assert_eq!(
283 Complex::new(0.0_f64, 1.0).argument(),
284 core::f64::consts::FRAC_PI_2
285 );
286 }
287}