Skip to main content

use_complex/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Complex-number primitives for `RustUse`.
5
6use core::fmt;
7use core::ops::{Add, Div, Mul, Neg, Sub};
8
9/// A complex number stored in rectangular form.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub struct Complex<T> {
12    /// The real component.
13    pub re: T,
14    /// The imaginary component.
15    pub im: T,
16}
17
18/// A standalone imaginary value.
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub struct Imaginary<T>(pub T);
21
22impl<T> Complex<T> {
23    /// Creates a complex number from real and imaginary parts.
24    #[must_use]
25    pub const fn new(re: T, im: T) -> Self {
26        Self { re, im }
27    }
28
29    /// Returns the real component.
30    #[must_use]
31    pub const fn real(&self) -> &T {
32        &self.re
33    }
34
35    /// Returns the imaginary component.
36    #[must_use]
37    pub const fn imaginary(&self) -> &T {
38        &self.im
39    }
40}
41
42impl<T> Imaginary<T> {
43    /// Creates a new imaginary value.
44    #[must_use]
45    pub const fn new(value: T) -> Self {
46        Self(value)
47    }
48
49    /// Returns the stored coefficient.
50    #[must_use]
51    pub const fn value(&self) -> &T {
52        &self.0
53    }
54}
55
56impl<T> Complex<T>
57where
58    T: Default,
59{
60    /// Creates a real-only complex value.
61    #[must_use]
62    pub fn from_real(re: T) -> Self {
63        Self::new(re, T::default())
64    }
65
66    /// Creates an imaginary-only complex value.
67    #[must_use]
68    pub fn from_imaginary(im: T) -> Self {
69        Self::new(T::default(), im)
70    }
71}
72
73impl<T> Complex<T>
74where
75    T: Default + From<u8>,
76{
77    /// Returns `0 + 0i`.
78    #[must_use]
79    pub fn zero() -> Self {
80        Self::new(T::default(), T::default())
81    }
82
83    /// Returns `1 + 0i`.
84    #[must_use]
85    pub fn one() -> Self {
86        Self::from_real(T::from(1_u8))
87    }
88
89    /// Returns `0 + 1i`.
90    #[must_use]
91    pub fn i() -> Self {
92        Self::from_imaginary(T::from(1_u8))
93    }
94}
95
96impl<T> Complex<T>
97where
98    T: Copy + Neg<Output = T>,
99{
100    /// Returns the complex conjugate.
101    #[must_use]
102    pub fn conjugate(&self) -> Self {
103        Self::new(self.re, -self.im)
104    }
105}
106
107impl<T> Complex<T>
108where
109    T: Copy + Add<Output = T> + Mul<Output = T>,
110{
111    /// Returns the squared magnitude, `re^2 + im^2`.
112    #[must_use]
113    pub fn magnitude_squared(&self) -> T {
114        (self.re * self.re) + (self.im * self.im)
115    }
116}
117
118macro_rules! impl_float_methods {
119	($($ty:ty),* $(,)?) => {
120		$(
121			impl Complex<$ty> {
122				/// Returns the magnitude, `sqrt(re^2 + im^2)`.
123				#[must_use]
124				pub fn magnitude(&self) -> $ty {
125					self.magnitude_squared().sqrt()
126				}
127
128				/// Returns the argument in radians.
129				#[must_use]
130				pub fn argument(&self) -> $ty {
131					self.im.atan2(self.re)
132				}
133
134				/// Builds a complex value from polar coordinates.
135				#[must_use]
136				pub fn from_polar(magnitude: $ty, argument: $ty) -> Self {
137					Self::new(magnitude * argument.cos(), magnitude * argument.sin())
138				}
139
140				/// Returns `(magnitude, argument)` in polar form.
141				#[must_use]
142				pub fn to_polar(&self) -> ($ty, $ty) {
143					(self.magnitude(), self.argument())
144				}
145			}
146		)*
147	};
148}
149
150impl_float_methods!(f32, f64);
151
152impl<T> From<T> for Complex<T>
153where
154    T: Default,
155{
156    fn from(value: T) -> Self {
157        Self::from_real(value)
158    }
159}
160
161impl<T> From<Imaginary<T>> for Complex<T>
162where
163    T: Default,
164{
165    fn from(value: Imaginary<T>) -> Self {
166        Self::from_imaginary(value.0)
167    }
168}
169
170impl<T> From<T> for Imaginary<T> {
171    fn from(value: T) -> Self {
172        Self(value)
173    }
174}
175
176impl<T> Add for Complex<T>
177where
178    T: Add<Output = T>,
179{
180    type Output = Self;
181
182    fn add(self, rhs: Self) -> Self::Output {
183        Self::new(self.re + rhs.re, self.im + rhs.im)
184    }
185}
186
187impl<T> Sub for Complex<T>
188where
189    T: Sub<Output = T>,
190{
191    type Output = Self;
192
193    fn sub(self, rhs: Self) -> Self::Output {
194        Self::new(self.re - rhs.re, self.im - rhs.im)
195    }
196}
197
198#[allow(clippy::suspicious_arithmetic_impl)]
199impl<T> Mul for Complex<T>
200where
201    T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
202{
203    type Output = Self;
204
205    fn mul(self, rhs: Self) -> Self::Output {
206        let re = (self.re * rhs.re) - (self.im * rhs.im);
207        let im = (self.re * rhs.im) + (self.im * rhs.re);
208
209        Self::new(re, im)
210    }
211}
212
213#[allow(clippy::suspicious_arithmetic_impl)]
214impl<T> Div for Complex<T>
215where
216    T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
217{
218    type Output = Self;
219
220    fn div(self, rhs: Self) -> Self::Output {
221        let denominator = (rhs.re * rhs.re) + (rhs.im * rhs.im);
222        let re = ((self.re * rhs.re) + (self.im * rhs.im)) / denominator;
223        let im = ((self.im * rhs.re) - (self.re * rhs.im)) / denominator;
224
225        Self::new(re, im)
226    }
227}
228
229impl<T> Neg for Complex<T>
230where
231    T: Neg<Output = T>,
232{
233    type Output = Self;
234
235    fn neg(self) -> Self::Output {
236        Self::new(-self.re, -self.im)
237    }
238}
239
240impl<T> fmt::Display for Complex<T>
241where
242    T: Copy + Default + fmt::Display + Neg<Output = T> + PartialEq + PartialOrd,
243{
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        let zero = T::default();
246
247        if self.im == zero {
248            return write!(f, "{}", self.re);
249        }
250
251        if self.re == zero {
252            if self.im < zero {
253                return write!(f, "-{}i", -self.im);
254            }
255
256            return write!(f, "{}i", self.im);
257        }
258
259        if self.im < zero {
260            return write!(f, "{} - {}i", self.re, -self.im);
261        }
262
263        write!(f, "{} + {}i", self.re, self.im)
264    }
265}
266
267impl<T> fmt::Display for Imaginary<T>
268where
269    T: Copy + Default + fmt::Display + Neg<Output = T> + PartialEq + PartialOrd,
270{
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        let zero = T::default();
273
274        if self.0 < zero {
275            return write!(f, "-{}i", -self.0);
276        }
277
278        if self.0 == zero {
279            return write!(f, "0i");
280        }
281
282        write!(f, "{}i", self.0)
283    }
284}
285
286pub mod prelude;
287
288#[cfg(test)]
289mod tests {
290    use super::{Complex, Imaginary};
291    use core::f64::consts::{FRAC_PI_2, FRAC_PI_3};
292
293    fn assert_close(lhs: f64, rhs: f64) {
294        let difference = (lhs - rhs).abs();
295
296        assert!(
297            difference <= 1.0e-10,
298            "left={lhs}, right={rhs}, diff={difference}"
299        );
300    }
301
302    #[test]
303    fn constructs_complex_values_and_accessors() {
304        let value = Complex::new(3_i32, 4_i32);
305
306        assert_eq!(value.re, 3);
307        assert_eq!(value.im, 4);
308        assert_eq!(value.real(), &3);
309        assert_eq!(value.imaginary(), &4);
310    }
311
312    #[test]
313    fn supports_imaginary_only_values() {
314        let imaginary = Imaginary::new(4_i32);
315        let complex = Complex::from(imaginary);
316
317        assert_eq!(imaginary.value(), &4);
318        assert_eq!(imaginary.to_string(), "4i");
319        assert_eq!(complex, Complex::new(0, 4));
320        assert_eq!(Complex::<i32>::from_imaginary(7), Complex::new(0, 7));
321    }
322
323    #[test]
324    fn provides_zero_one_and_i() {
325        assert_eq!(Complex::<i32>::zero(), Complex::new(0, 0));
326        assert_eq!(Complex::<i32>::one(), Complex::new(1, 0));
327        assert_eq!(Complex::<i32>::i(), Complex::new(0, 1));
328        assert_eq!(Complex::<i32>::from_real(9), Complex::new(9, 0));
329    }
330
331    #[test]
332    fn adds_and_subtracts_complex_values() {
333        let lhs = Complex::new(3_i32, 4_i32);
334        let rhs = Complex::new(1_i32, -2_i32);
335
336        assert_eq!(lhs + rhs, Complex::new(4, 2));
337        assert_eq!(lhs - rhs, Complex::new(2, 6));
338    }
339
340    #[test]
341    fn multiplies_complex_values() {
342        let lhs = Complex::new(3_i32, 4_i32);
343        let rhs = Complex::new(1_i32, -2_i32);
344
345        assert_eq!(lhs * rhs, Complex::new(11, -2));
346    }
347
348    #[test]
349    fn divides_complex_values() {
350        let lhs = Complex::new(3.0_f64, 4.0_f64);
351        let rhs = Complex::new(1.0_f64, -2.0_f64);
352        let quotient = lhs / rhs;
353
354        assert_close(quotient.re, -1.0);
355        assert_close(quotient.im, 2.0);
356    }
357
358    #[test]
359    fn negates_and_conjugates() {
360        let value = Complex::new(3_i32, -4_i32);
361
362        assert_eq!(-value, Complex::new(-3, 4));
363        assert_eq!(value.conjugate(), Complex::new(3, 4));
364    }
365
366    #[test]
367    fn computes_magnitude_squared_for_integer_values() {
368        let value = Complex::new(3_i32, 4_i32);
369
370        assert_eq!(value.magnitude_squared(), 25);
371    }
372
373    #[test]
374    fn computes_magnitude_and_argument_for_floats() {
375        let value = Complex::new(3.0_f64, 4.0_f64);
376
377        assert_close(value.magnitude(), 5.0);
378        assert_close(Complex::new(0.0_f64, 4.0_f64).argument(), FRAC_PI_2);
379    }
380
381    #[test]
382    fn converts_to_and_from_polar_form() {
383        let value = Complex::<f64>::from_polar(5.0_f64, FRAC_PI_3);
384        let (magnitude, argument) = value.to_polar();
385
386        assert_close(magnitude, 5.0);
387        assert_close(argument, FRAC_PI_3);
388    }
389
390    #[test]
391    fn formats_complex_and_imaginary_values() {
392        assert_eq!(Complex::new(3_i32, 4_i32).to_string(), "3 + 4i");
393        assert_eq!(Complex::new(3_i32, -4_i32).to_string(), "3 - 4i");
394        assert_eq!(Complex::new(0_i32, 4_i32).to_string(), "4i");
395        assert_eq!(Complex::new(3_i32, 0_i32).to_string(), "3");
396        assert_eq!(Imaginary::new(-4_i32).to_string(), "-4i");
397    }
398
399    #[test]
400    fn integer_division_works_when_components_divide_evenly() {
401        let lhs = Complex::new(4_i32, 2_i32);
402        let rhs = Complex::new(2_i32, 0_i32);
403
404        assert_eq!(lhs / rhs, Complex::new(2, 1));
405    }
406
407    #[test]
408    fn floating_point_values_round_trip_through_helpers() {
409        let from_real = Complex::<f64>::from(3.0);
410        let from_imaginary = Complex::<f64>::from(Imaginary::new(2.5));
411
412        assert_close(from_real.re, 3.0);
413        assert_close(from_real.im, 0.0);
414        assert_close(from_imaginary.re, 0.0);
415        assert_close(from_imaginary.im, 2.5);
416    }
417}