Skip to content

Commit d4ce0bd

Browse files
YakoYakoYokuYokuobsgolem
authored andcommitted
Created ComplexFloat trait that provides a common trait for floating point and complex numbers. Added a couple of methods to Complex that were present in Float but missing in Complex. Added tests for many methods on the new trait and for all new methods on Complex. Resolves #2.
Authored originally by Martin Reboredo <yakoyoku@gmail.com> Updated by Josiah Bills <josiah@adoniram.net>
1 parent 3a89daa commit d4ce0bd

File tree

2 files changed

+438
-4
lines changed

2 files changed

+438
-4
lines changed

src/complex_float.rs

Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
// Keeps us from accidentally creating a recursive impl rather than a real one.
2+
#![deny(unconditional_recursion)]
3+
4+
use num_traits::{float::FloatCore, Float, FloatConst};
5+
6+
use crate::Complex;
7+
8+
/// Generic trait for floating point complex numbers
9+
/// This trait defines methods which are common to complex floating point numbers and regular floating point numbers.
10+
#[cfg(any(feature = "std", feature = "libm"))]
11+
pub trait ComplexFloat {
12+
type Real;
13+
14+
/// Returns `true` if this value is `NaN` and false otherwise.
15+
fn is_nan(self) -> bool;
16+
17+
/// Returns `true` if this value is positive infinity or negative infinity and
18+
/// false otherwise.
19+
fn is_infinite(self) -> bool;
20+
21+
/// Returns `true` if this number is neither infinite nor `NaN`.
22+
fn is_finite(self) -> bool;
23+
24+
/// Returns `true` if the number is neither zero, infinite,
25+
/// [subnormal][subnormal], or `NaN`.
26+
/// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number
27+
fn is_normal(self) -> bool;
28+
29+
/// Take the reciprocal (inverse) of a number, `1/x`.
30+
fn recip(self) -> Self;
31+
32+
/// Raises `self` to a signed integer power.
33+
fn powi(self, exp: i32) -> Self;
34+
35+
/// Raises `self` to a real power.
36+
fn powf(self, exp: Self::Real) -> Self;
37+
38+
/// Raises `self` to a complex power.
39+
fn powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real>;
40+
41+
/// Take the square root of a number.
42+
fn sqrt(self) -> Self;
43+
44+
/// Returns `e^(self)`, (the exponential function).
45+
fn exp(self) -> Self;
46+
47+
/// Returns `2^(self)`.
48+
fn exp2(self) -> Self;
49+
50+
/// Returns the natural logarithm of the number.
51+
fn ln(self) -> Self;
52+
53+
/// Returns the logarithm of the number with respect to an arbitrary base.
54+
fn log(self, base: Self::Real) -> Self;
55+
56+
/// Returns the base 2 logarithm of the number.
57+
fn log2(self) -> Self;
58+
59+
/// Returns the base 10 logarithm of the number.
60+
fn log10(self) -> Self;
61+
62+
/// Take the cubic root of a number.
63+
fn cbrt(self) -> Self;
64+
65+
/// Computes the sine of a number (in radians).
66+
fn sin(self) -> Self;
67+
68+
/// Computes the cosine of a number (in radians).
69+
fn cos(self) -> Self;
70+
71+
/// Computes the tangent of a number (in radians).
72+
fn tan(self) -> Self;
73+
74+
/// Computes the arcsine of a number. Return value is in radians in
75+
/// the range [-pi/2, pi/2] or NaN if the number is outside the range
76+
/// [-1, 1].
77+
fn asin(self) -> Self;
78+
79+
/// Computes the arccosine of a number. Return value is in radians in
80+
/// the range [0, pi] or NaN if the number is outside the range
81+
/// [-1, 1].
82+
fn acos(self) -> Self;
83+
84+
/// Computes the arctangent of a number. Return value is in radians in the
85+
/// range [-pi/2, pi/2];
86+
fn atan(self) -> Self;
87+
88+
/// Hyperbolic sine function.
89+
fn sinh(self) -> Self;
90+
91+
/// Hyperbolic cosine function.
92+
fn cosh(self) -> Self;
93+
94+
/// Hyperbolic tangent function.
95+
fn tanh(self) -> Self;
96+
97+
/// Inverse hyperbolic sine function.
98+
fn asinh(self) -> Self;
99+
100+
/// Inverse hyperbolic cosine function.
101+
fn acosh(self) -> Self;
102+
103+
/// Inverse hyperbolic tangent function.
104+
fn atanh(self) -> Self;
105+
106+
/// Returns the real part of the number.
107+
fn re(self) -> Self::Real;
108+
109+
/// Returns the imaginary part of the number which equals to zero.
110+
fn im(self) -> Self::Real;
111+
112+
/// Returns the absolute value of the number.
113+
fn abs(self) -> Self::Real;
114+
115+
/// Computes the argument of the number.
116+
fn arg(self) -> Self::Real;
117+
118+
/// Comutes the complex conjugate of `self`.
119+
///
120+
/// Formula: `a+bi -> a-bi`
121+
fn conj(self) -> Self;
122+
}
123+
124+
macro_rules! forward {
125+
($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
126+
=> {$(
127+
#[inline]
128+
fn $method(self $( , $arg : $ty )* ) -> $ret {
129+
$base::$method(self $( , $arg )* )
130+
}
131+
)*};
132+
}
133+
134+
macro_rules! forward_ref {
135+
($( Self :: $method:ident ( & self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
136+
=> {$(
137+
#[inline]
138+
fn $method(self $( , $arg : $ty )* ) -> $ret {
139+
Self::$method(&self $( , $arg )* )
140+
}
141+
)*};
142+
}
143+
144+
#[cfg(any(feature = "std", feature = "libm"))]
145+
impl<T> ComplexFloat for T
146+
where
147+
T: Float + FloatConst,
148+
{
149+
type Real = T;
150+
151+
fn re(self) -> Self::Real {
152+
self
153+
}
154+
155+
fn im(self) -> Self::Real {
156+
T::zero()
157+
}
158+
159+
fn abs(self) -> Self::Real {
160+
self.abs()
161+
}
162+
163+
fn arg(self) -> Self::Real {
164+
if self > T::zero() {
165+
T::zero()
166+
} else if self < T::zero() {
167+
T::PI()
168+
} else {
169+
T::nan()
170+
}
171+
}
172+
173+
fn powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real> {
174+
Complex::new(self, Self::Real::zero()).powc(exp)
175+
}
176+
177+
fn conj(self) -> Self {
178+
self
179+
}
180+
181+
forward! {
182+
Float::is_normal(self) -> bool;
183+
Float::is_infinite(self) -> bool;
184+
Float::is_finite(self) -> bool;
185+
Float::is_nan(self) -> bool;
186+
Float::recip(self) -> Self;
187+
Float::powi(self, n: i32) -> Self;
188+
Float::powf(self, f: Self) -> Self;
189+
Float::sqrt(self) -> Self;
190+
Float::cbrt(self) -> Self;
191+
Float::exp(self) -> Self;
192+
Float::exp2(self) -> Self;
193+
Float::ln(self) -> Self;
194+
Float::log(self, base: Self) -> Self;
195+
Float::log2(self) -> Self;
196+
Float::log10(self) -> Self;
197+
Float::sin(self) -> Self;
198+
Float::cos(self) -> Self;
199+
Float::tan(self) -> Self;
200+
Float::asin(self) -> Self;
201+
Float::acos(self) -> Self;
202+
Float::atan(self) -> Self;
203+
Float::sinh(self) -> Self;
204+
Float::cosh(self) -> Self;
205+
Float::tanh(self) -> Self;
206+
Float::asinh(self) -> Self;
207+
Float::acosh(self) -> Self;
208+
Float::atanh(self) -> Self;
209+
}
210+
}
211+
212+
#[cfg(any(feature = "std", feature = "libm"))]
213+
impl<T: Float + FloatCore + FloatConst> ComplexFloat for Complex<T> {
214+
type Real = T;
215+
216+
fn re(self) -> Self::Real {
217+
self.re
218+
}
219+
220+
fn im(self) -> Self::Real {
221+
self.im
222+
}
223+
224+
fn abs(self) -> Self::Real {
225+
self.norm()
226+
}
227+
228+
fn recip(self) -> Self {
229+
self.finv()
230+
}
231+
232+
forward! {
233+
Complex::arg(self) -> Self::Real;
234+
Complex::powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real>;
235+
Complex::exp2(self) -> Self;
236+
Complex::log(self, base: Self::Real) -> Self;
237+
Complex::log2(self) -> Self;
238+
Complex::log10(self) -> Self;
239+
Complex::is_normal(self) -> bool;
240+
Complex::is_infinite(self) -> bool;
241+
Complex::is_finite(self) -> bool;
242+
Complex::is_nan(self) -> bool;
243+
Complex::powf(self, f: Self::Real) -> Self;
244+
Complex::sqrt(self) -> Self;
245+
Complex::cbrt(self) -> Self;
246+
Complex::exp(self) -> Self;
247+
Complex::ln(self) -> Self;
248+
Complex::sin(self) -> Self;
249+
Complex::cos(self) -> Self;
250+
Complex::tan(self) -> Self;
251+
Complex::asin(self) -> Self;
252+
Complex::acos(self) -> Self;
253+
Complex::atan(self) -> Self;
254+
Complex::sinh(self) -> Self;
255+
Complex::cosh(self) -> Self;
256+
Complex::tanh(self) -> Self;
257+
Complex::asinh(self) -> Self;
258+
Complex::acosh(self) -> Self;
259+
Complex::atanh(self) -> Self;
260+
}
261+
262+
forward_ref! {
263+
Self::powi(&self, n: i32) -> Self;
264+
Self::conj(&self) -> Self;
265+
}
266+
}
267+
268+
#[cfg(test)]
269+
mod test {
270+
use crate::{
271+
complex_float::ComplexFloat,
272+
test::{_0_0i, _0_1i, _1_0i, _1_1i, float::close},
273+
Complex,
274+
};
275+
276+
fn closef(a: f64, b: f64) -> bool {
277+
close_to_tolf(a, b, 1e-10)
278+
}
279+
280+
fn close_to_tolf(a: f64, b: f64, tol: f64) -> bool {
281+
// returns true if a and b are reasonably close
282+
let close = (a == b) || (a - b).abs() < tol;
283+
if !close {
284+
println!("{:?} != {:?}", a, b);
285+
}
286+
close
287+
}
288+
289+
#[test]
290+
fn test_exp2() {
291+
assert!(close(ComplexFloat::exp2(_0_0i), _1_0i));
292+
assert!(closef(<f64 as ComplexFloat>::exp2(0.), 1.));
293+
}
294+
295+
#[test]
296+
fn test_exp() {
297+
assert!(close(ComplexFloat::exp(_0_0i), _1_0i));
298+
assert!(closef(ComplexFloat::exp(0.), 1.));
299+
}
300+
301+
#[test]
302+
fn test_powi() {
303+
assert!(close(ComplexFloat::powi(_0_1i, 4), _1_0i));
304+
assert!(closef(ComplexFloat::powi(-1., 4), 1.));
305+
}
306+
307+
#[test]
308+
fn test_powz() {
309+
assert!(close(ComplexFloat::powc(_1_0i, _0_1i), _1_0i));
310+
assert!(close(ComplexFloat::powc(1., _0_1i), _1_0i));
311+
}
312+
313+
#[test]
314+
fn test_log2() {
315+
assert!(close(ComplexFloat::log2(_1_0i), _0_0i));
316+
assert!(closef(ComplexFloat::log2(1.), 0.));
317+
}
318+
319+
#[test]
320+
fn test_log10() {
321+
assert!(close(ComplexFloat::log10(_1_0i), _0_0i));
322+
assert!(closef(ComplexFloat::log10(1.), 0.));
323+
}
324+
325+
#[test]
326+
fn test_conj() {
327+
assert_eq!(ComplexFloat::conj(_0_1i), Complex::new(0., -1.));
328+
assert_eq!(ComplexFloat::conj(1.), 1.);
329+
}
330+
331+
#[test]
332+
fn test_is_nan() {
333+
assert!(!ComplexFloat::is_nan(_1_0i));
334+
assert!(!ComplexFloat::is_nan(1.));
335+
336+
assert!(ComplexFloat::is_nan(Complex::new(f64::NAN, f64::NAN)));
337+
assert!(ComplexFloat::is_nan(f64::NAN));
338+
}
339+
340+
#[test]
341+
fn test_is_infinite() {
342+
assert!(!ComplexFloat::is_infinite(_1_0i));
343+
assert!(!ComplexFloat::is_infinite(1.));
344+
345+
assert!(ComplexFloat::is_infinite(Complex::new(
346+
f64::INFINITY,
347+
f64::INFINITY
348+
)));
349+
assert!(ComplexFloat::is_infinite(f64::INFINITY));
350+
}
351+
352+
#[test]
353+
fn test_is_finite() {
354+
assert!(ComplexFloat::is_finite(_1_0i));
355+
assert!(ComplexFloat::is_finite(1.));
356+
357+
assert!(!ComplexFloat::is_finite(Complex::new(
358+
f64::INFINITY,
359+
f64::INFINITY
360+
)));
361+
assert!(!ComplexFloat::is_finite(f64::INFINITY));
362+
}
363+
364+
#[test]
365+
fn test_is_normal() {
366+
assert!(ComplexFloat::is_normal(_1_1i));
367+
assert!(ComplexFloat::is_normal(1.));
368+
369+
assert!(!ComplexFloat::is_normal(Complex::new(
370+
f64::INFINITY,
371+
f64::INFINITY
372+
)));
373+
assert!(!ComplexFloat::is_normal(f64::INFINITY));
374+
}
375+
376+
#[test]
377+
fn test_arg() {
378+
assert!(closef(
379+
ComplexFloat::arg(_0_1i),
380+
core::f64::consts::FRAC_PI_2
381+
));
382+
383+
assert!(closef(ComplexFloat::arg(-1.), core::f64::consts::PI));
384+
assert!(closef(ComplexFloat::arg(1.), 0.));
385+
}
386+
}

0 commit comments

Comments
 (0)