num-complex
Advanced tools
| // Keeps us from accidentally creating a recursive impl rather than a real one. | ||
| #![deny(unconditional_recursion)] | ||
| use core::ops::Neg; | ||
| use num_traits::{Float, FloatConst, Num, NumCast}; | ||
| use crate::Complex; | ||
| mod private { | ||
| use num_traits::{Float, FloatConst}; | ||
| use crate::Complex; | ||
| pub trait Seal {} | ||
| impl<T> Seal for T where T: Float + FloatConst {} | ||
| impl<T: Float + FloatConst> Seal for Complex<T> {} | ||
| } | ||
| /// Generic trait for floating point complex numbers. | ||
| /// | ||
| /// This trait defines methods which are common to complex floating point | ||
| /// numbers and regular floating point numbers. | ||
| /// | ||
| /// This trait is sealed to prevent it from being implemented by anything other | ||
| /// than floating point scalars and [Complex] floats. | ||
| pub trait ComplexFloat: Num + NumCast + Copy + Neg<Output = Self> + private::Seal { | ||
| /// The type used to represent the real coefficients of this complex number. | ||
| type Real: Float + FloatConst; | ||
| /// Returns `true` if this value is `NaN` and false otherwise. | ||
| fn is_nan(self) -> bool; | ||
| /// Returns `true` if this value is positive infinity or negative infinity and | ||
| /// false otherwise. | ||
| fn is_infinite(self) -> bool; | ||
| /// Returns `true` if this number is neither infinite nor `NaN`. | ||
| fn is_finite(self) -> bool; | ||
| /// Returns `true` if the number is neither zero, infinite, | ||
| /// [subnormal](http://en.wikipedia.org/wiki/Denormal_number), or `NaN`. | ||
| fn is_normal(self) -> bool; | ||
| /// Take the reciprocal (inverse) of a number, `1/x`. See also [Complex::finv]. | ||
| fn recip(self) -> Self; | ||
| /// Raises `self` to a signed integer power. | ||
| fn powi(self, exp: i32) -> Self; | ||
| /// Raises `self` to a real power. | ||
| fn powf(self, exp: Self::Real) -> Self; | ||
| /// Raises `self` to a complex power. | ||
| fn powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real>; | ||
| /// Take the square root of a number. | ||
| fn sqrt(self) -> Self; | ||
| /// Returns `e^(self)`, (the exponential function). | ||
| fn exp(self) -> Self; | ||
| /// Returns `2^(self)`. | ||
| fn exp2(self) -> Self; | ||
| /// Returns `base^(self)`. | ||
| fn expf(self, base: Self::Real) -> Self; | ||
| /// Returns the natural logarithm of the number. | ||
| fn ln(self) -> Self; | ||
| /// Returns the logarithm of the number with respect to an arbitrary base. | ||
| fn log(self, base: Self::Real) -> Self; | ||
| /// Returns the base 2 logarithm of the number. | ||
| fn log2(self) -> Self; | ||
| /// Returns the base 10 logarithm of the number. | ||
| fn log10(self) -> Self; | ||
| /// Take the cubic root of a number. | ||
| fn cbrt(self) -> Self; | ||
| /// Computes the sine of a number (in radians). | ||
| fn sin(self) -> Self; | ||
| /// Computes the cosine of a number (in radians). | ||
| fn cos(self) -> Self; | ||
| /// Computes the tangent of a number (in radians). | ||
| fn tan(self) -> Self; | ||
| /// Computes the arcsine of a number. Return value is in radians in | ||
| /// the range [-pi/2, pi/2] or NaN if the number is outside the range | ||
| /// [-1, 1]. | ||
| fn asin(self) -> Self; | ||
| /// Computes the arccosine of a number. Return value is in radians in | ||
| /// the range [0, pi] or NaN if the number is outside the range | ||
| /// [-1, 1]. | ||
| fn acos(self) -> Self; | ||
| /// Computes the arctangent of a number. Return value is in radians in the | ||
| /// range [-pi/2, pi/2]; | ||
| fn atan(self) -> Self; | ||
| /// Hyperbolic sine function. | ||
| fn sinh(self) -> Self; | ||
| /// Hyperbolic cosine function. | ||
| fn cosh(self) -> Self; | ||
| /// Hyperbolic tangent function. | ||
| fn tanh(self) -> Self; | ||
| /// Inverse hyperbolic sine function. | ||
| fn asinh(self) -> Self; | ||
| /// Inverse hyperbolic cosine function. | ||
| fn acosh(self) -> Self; | ||
| /// Inverse hyperbolic tangent function. | ||
| fn atanh(self) -> Self; | ||
| /// Returns the real part of the number. | ||
| fn re(self) -> Self::Real; | ||
| /// Returns the imaginary part of the number. | ||
| fn im(self) -> Self::Real; | ||
| /// Returns the absolute value of the number. See also [Complex::norm] | ||
| fn abs(self) -> Self::Real; | ||
| /// Returns the L1 norm `|re| + |im|` -- the [Manhattan distance] from the origin. | ||
| /// | ||
| /// [Manhattan distance]: https://en.wikipedia.org/wiki/Taxicab_geometry | ||
| fn l1_norm(&self) -> Self::Real; | ||
| /// Computes the argument of the number. | ||
| fn arg(self) -> Self::Real; | ||
| /// Computes the complex conjugate of the number. | ||
| /// | ||
| /// Formula: `a+bi -> a-bi` | ||
| fn conj(self) -> Self; | ||
| } | ||
| macro_rules! forward { | ||
| ($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) | ||
| => {$( | ||
| #[inline] | ||
| fn $method(self $( , $arg : $ty )* ) -> $ret { | ||
| $base::$method(self $( , $arg )* ) | ||
| } | ||
| )*}; | ||
| } | ||
| macro_rules! forward_ref { | ||
| ($( Self :: $method:ident ( & self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) | ||
| => {$( | ||
| #[inline] | ||
| fn $method(self $( , $arg : $ty )* ) -> $ret { | ||
| Self::$method(&self $( , $arg )* ) | ||
| } | ||
| )*}; | ||
| } | ||
| impl<T> ComplexFloat for T | ||
| where | ||
| T: Float + FloatConst, | ||
| { | ||
| type Real = T; | ||
| fn re(self) -> Self::Real { | ||
| self | ||
| } | ||
| fn im(self) -> Self::Real { | ||
| T::zero() | ||
| } | ||
| fn l1_norm(&self) -> Self::Real { | ||
| self.abs() | ||
| } | ||
| fn arg(self) -> Self::Real { | ||
| if self.is_nan() { | ||
| self | ||
| } else if self.is_sign_negative() { | ||
| T::PI() | ||
| } else { | ||
| T::zero() | ||
| } | ||
| } | ||
| fn powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real> { | ||
| Complex::new(self, T::zero()).powc(exp) | ||
| } | ||
| fn conj(self) -> Self { | ||
| self | ||
| } | ||
| fn expf(self, base: Self::Real) -> Self { | ||
| base.powf(self) | ||
| } | ||
| forward! { | ||
| Float::is_normal(self) -> bool; | ||
| Float::is_infinite(self) -> bool; | ||
| Float::is_finite(self) -> bool; | ||
| Float::is_nan(self) -> bool; | ||
| Float::recip(self) -> Self; | ||
| Float::powi(self, n: i32) -> Self; | ||
| Float::powf(self, f: Self) -> Self; | ||
| Float::sqrt(self) -> Self; | ||
| Float::cbrt(self) -> Self; | ||
| Float::exp(self) -> Self; | ||
| Float::exp2(self) -> Self; | ||
| Float::ln(self) -> Self; | ||
| Float::log(self, base: Self) -> Self; | ||
| Float::log2(self) -> Self; | ||
| Float::log10(self) -> Self; | ||
| Float::sin(self) -> Self; | ||
| Float::cos(self) -> Self; | ||
| Float::tan(self) -> Self; | ||
| Float::asin(self) -> Self; | ||
| Float::acos(self) -> Self; | ||
| Float::atan(self) -> Self; | ||
| Float::sinh(self) -> Self; | ||
| Float::cosh(self) -> Self; | ||
| Float::tanh(self) -> Self; | ||
| Float::asinh(self) -> Self; | ||
| Float::acosh(self) -> Self; | ||
| Float::atanh(self) -> Self; | ||
| Float::abs(self) -> Self; | ||
| } | ||
| } | ||
| impl<T: Float + FloatConst> ComplexFloat for Complex<T> { | ||
| type Real = T; | ||
| fn re(self) -> Self::Real { | ||
| self.re | ||
| } | ||
| fn im(self) -> Self::Real { | ||
| self.im | ||
| } | ||
| fn abs(self) -> Self::Real { | ||
| self.norm() | ||
| } | ||
| fn recip(self) -> Self { | ||
| self.finv() | ||
| } | ||
| // `Complex::l1_norm` uses `Signed::abs` to let it work | ||
| // for integers too, but we can just use `Float::abs`. | ||
| fn l1_norm(&self) -> Self::Real { | ||
| self.re.abs() + self.im.abs() | ||
| } | ||
| // `Complex::is_*` methods use `T: FloatCore`, but we | ||
| // have `T: Float` that can do them as well. | ||
| fn is_nan(self) -> bool { | ||
| self.re.is_nan() || self.im.is_nan() | ||
| } | ||
| fn is_infinite(self) -> bool { | ||
| !self.is_nan() && (self.re.is_infinite() || self.im.is_infinite()) | ||
| } | ||
| fn is_finite(self) -> bool { | ||
| self.re.is_finite() && self.im.is_finite() | ||
| } | ||
| fn is_normal(self) -> bool { | ||
| self.re.is_normal() && self.im.is_normal() | ||
| } | ||
| forward! { | ||
| Complex::arg(self) -> Self::Real; | ||
| Complex::powc(self, exp: Complex<Self::Real>) -> Complex<Self::Real>; | ||
| Complex::exp2(self) -> Self; | ||
| Complex::log(self, base: Self::Real) -> Self; | ||
| Complex::log2(self) -> Self; | ||
| Complex::log10(self) -> Self; | ||
| Complex::powf(self, f: Self::Real) -> Self; | ||
| Complex::sqrt(self) -> Self; | ||
| Complex::cbrt(self) -> Self; | ||
| Complex::exp(self) -> Self; | ||
| Complex::expf(self, base: Self::Real) -> Self; | ||
| Complex::ln(self) -> Self; | ||
| Complex::sin(self) -> Self; | ||
| Complex::cos(self) -> Self; | ||
| Complex::tan(self) -> Self; | ||
| Complex::asin(self) -> Self; | ||
| Complex::acos(self) -> Self; | ||
| Complex::atan(self) -> Self; | ||
| Complex::sinh(self) -> Self; | ||
| Complex::cosh(self) -> Self; | ||
| Complex::tanh(self) -> Self; | ||
| Complex::asinh(self) -> Self; | ||
| Complex::acosh(self) -> Self; | ||
| Complex::atanh(self) -> Self; | ||
| } | ||
| forward_ref! { | ||
| Self::powi(&self, n: i32) -> Self; | ||
| Self::conj(&self) -> Self; | ||
| } | ||
| } | ||
| #[cfg(test)] | ||
| mod test { | ||
| use crate::{ | ||
| complex_float::ComplexFloat, | ||
| test::{_0_0i, _0_1i, _1_0i, _1_1i, float::close}, | ||
| Complex, | ||
| }; | ||
| use std::f64; // for constants before Rust 1.43. | ||
| fn closef(a: f64, b: f64) -> bool { | ||
| close_to_tolf(a, b, 1e-10) | ||
| } | ||
| fn close_to_tolf(a: f64, b: f64, tol: f64) -> bool { | ||
| // returns true if a and b are reasonably close | ||
| let close = (a == b) || (a - b).abs() < tol; | ||
| if !close { | ||
| println!("{:?} != {:?}", a, b); | ||
| } | ||
| close | ||
| } | ||
| #[test] | ||
| fn test_exp2() { | ||
| assert!(close(ComplexFloat::exp2(_0_0i), _1_0i)); | ||
| assert!(closef(<f64 as ComplexFloat>::exp2(0.), 1.)); | ||
| } | ||
| #[test] | ||
| fn test_exp() { | ||
| assert!(close(ComplexFloat::exp(_0_0i), _1_0i)); | ||
| assert!(closef(ComplexFloat::exp(0.), 1.)); | ||
| } | ||
| #[test] | ||
| fn test_powi() { | ||
| assert!(close(ComplexFloat::powi(_0_1i, 4), _1_0i)); | ||
| assert!(closef(ComplexFloat::powi(-1., 4), 1.)); | ||
| } | ||
| #[test] | ||
| fn test_powz() { | ||
| assert!(close(ComplexFloat::powc(_1_0i, _0_1i), _1_0i)); | ||
| assert!(close(ComplexFloat::powc(1., _0_1i), _1_0i)); | ||
| } | ||
| #[test] | ||
| fn test_log2() { | ||
| assert!(close(ComplexFloat::log2(_1_0i), _0_0i)); | ||
| assert!(closef(ComplexFloat::log2(1.), 0.)); | ||
| } | ||
| #[test] | ||
| fn test_log10() { | ||
| assert!(close(ComplexFloat::log10(_1_0i), _0_0i)); | ||
| assert!(closef(ComplexFloat::log10(1.), 0.)); | ||
| } | ||
| #[test] | ||
| fn test_conj() { | ||
| assert_eq!(ComplexFloat::conj(_0_1i), Complex::new(0., -1.)); | ||
| assert_eq!(ComplexFloat::conj(1.), 1.); | ||
| } | ||
| #[test] | ||
| fn test_is_nan() { | ||
| assert!(!ComplexFloat::is_nan(_1_0i)); | ||
| assert!(!ComplexFloat::is_nan(1.)); | ||
| assert!(ComplexFloat::is_nan(Complex::new(f64::NAN, f64::NAN))); | ||
| assert!(ComplexFloat::is_nan(f64::NAN)); | ||
| } | ||
| #[test] | ||
| fn test_is_infinite() { | ||
| assert!(!ComplexFloat::is_infinite(_1_0i)); | ||
| assert!(!ComplexFloat::is_infinite(1.)); | ||
| assert!(ComplexFloat::is_infinite(Complex::new( | ||
| f64::INFINITY, | ||
| f64::INFINITY | ||
| ))); | ||
| assert!(ComplexFloat::is_infinite(f64::INFINITY)); | ||
| } | ||
| #[test] | ||
| fn test_is_finite() { | ||
| assert!(ComplexFloat::is_finite(_1_0i)); | ||
| assert!(ComplexFloat::is_finite(1.)); | ||
| assert!(!ComplexFloat::is_finite(Complex::new( | ||
| f64::INFINITY, | ||
| f64::INFINITY | ||
| ))); | ||
| assert!(!ComplexFloat::is_finite(f64::INFINITY)); | ||
| } | ||
| #[test] | ||
| fn test_is_normal() { | ||
| assert!(ComplexFloat::is_normal(_1_1i)); | ||
| assert!(ComplexFloat::is_normal(1.)); | ||
| assert!(!ComplexFloat::is_normal(Complex::new( | ||
| f64::INFINITY, | ||
| f64::INFINITY | ||
| ))); | ||
| assert!(!ComplexFloat::is_normal(f64::INFINITY)); | ||
| } | ||
| #[test] | ||
| fn test_arg() { | ||
| assert!(closef( | ||
| ComplexFloat::arg(_0_1i), | ||
| core::f64::consts::FRAC_PI_2 | ||
| )); | ||
| assert!(closef(ComplexFloat::arg(-1.), core::f64::consts::PI)); | ||
| assert!(closef(ComplexFloat::arg(-0.), core::f64::consts::PI)); | ||
| assert!(closef(ComplexFloat::arg(0.), 0.)); | ||
| assert!(closef(ComplexFloat::arg(1.), 0.)); | ||
| assert!(ComplexFloat::arg(f64::NAN).is_nan()); | ||
| } | ||
| } |
| { | ||
| "git": { | ||
| "sha1": "a1e195e7c2d12ac9d5000bba5d1441c4d7a68c4a" | ||
| "sha1": "8963efc495767cb92d71cc6ca8f9f43ab3ec4920" | ||
| }, | ||
| "path_in_vcs": "" | ||
| } |
+1
-1
@@ -15,3 +15,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "num-complex" | ||
| version = "0.4.1" | ||
| version = "0.4.2" | ||
| authors = ["The Rust Project Developers"] | ||
@@ -18,0 +18,0 @@ exclude = [ |
+12
-1
@@ -0,1 +1,12 @@ | ||
| # Release 0.4.2 (2022-06-17) | ||
| - [The new `ComplexFloat` trait][95] provides a generic abstraction between | ||
| floating-point `T` and `Complex<T>`. | ||
| - [`Complex::exp` now handles edge cases with NaN and infinite parts][104]. | ||
| **Contributors**: @cuviper, @JorisDeRidder, @obsgolem, @YakoYakoYokuYoku | ||
| [95]: https://github.com/rust-num/num-complex/pull/95 | ||
| [104]: https://github.com/rust-num/num-complex/pull/104 | ||
| # Release 0.4.1 (2022-04-29) | ||
@@ -8,3 +19,3 @@ | ||
| **Contributors**: @bluss, @bradleyharden, @cglosser, @cuviper | ||
| **Contributors**: @bluss, @bradleyharden, @cuviper, @rayhem | ||
@@ -11,0 +22,0 @@ [90]: https://github.com/rust-num/num-complex/pull/90 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display