// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms.
//! Complex numbers. //! //! ## Compatibility //! //! The `num-complex` crate is tested for rustc 1.60 and greater.
use core::fmt; #[cfg(test)] use core::hash; use core::iter::{Product, Sum}; use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; use core::str::FromStr; #[cfg(feature = "std")] use std::error::Error;
use num_traits::{ConstOne, ConstZero, Inv, MulAdd, Num, One, Pow, Signed, Zero};
use num_traits::float::FloatCore; #[cfg(any(feature = "std", feature = "libm"))] use num_traits::float::{Float, FloatConst};
#[cfg(feature = "rand")] mod crand; #[cfg(feature = "rand")] pubusecrate::crand::ComplexDistribution;
// FIXME #1284: handle complex NaN & infinity etc. This // probably doesn't map to C's _Complex correctly.
/// A complex number in Cartesian form. /// /// ## Representation and Foreign Function Interface Compatibility /// /// `Complex<T>` is memory layout compatible with an array `[T; 2]`. /// /// Note that `Complex<F>` where F is a floating point type is **only** memory /// layout compatible with C's complex types, **not** necessarily calling /// convention compatible. This means that for FFI you can only pass /// `Complex<F>` behind a pointer, not as a value. /// /// ## Examples /// /// Example of extern function declaration. /// /// ``` /// use num_complex::Complex; /// use std::os::raw::c_int; /// /// extern "C" { /// fn zaxpy_(n: *const c_int, alpha: *const Complex<f64>, /// x: *const Complex<f64>, incx: *const c_int, /// y: *mut Complex<f64>, incy: *const c_int); /// } /// ``` #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Default)] #[repr(C)] #[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)] #[cfg_attr(feature = "rkyv", archive(as = "Complex<T::Archived>"))] #[cfg_attr(feature = "bytecheck", derive(bytecheck::CheckBytes))] pubstruct Complex<T> { /// Real portion of the complex number pub re: T, /// Imaginary portion of the complex number pub im: T,
}
/// Alias for a [`Complex<f32>`] pubtype Complex32 = Complex<f32>;
/// Create a new [`Complex<f32>`] with arguments that can convert [`Into<f32>`]. /// /// ``` /// use num_complex::{c32, Complex32}; /// assert_eq!(c32(1u8, 2), Complex32::new(1.0, 2.0)); /// ``` /// /// Note: ambiguous integer literals in Rust will [default] to `i32`, which does **not** implement /// `Into<f32>`, so a call like `c32(1, 2)` will result in a type error. The example above uses a /// suffixed `1u8` to set its type, and then the `2` can be inferred as the same type. /// /// [default]: https://doc.rust-lang.org/reference/expressions/literal-expr.html#integer-literal-expressions #[inline] pubfn c32<T: Into<f32>>(re: T, im: T) -> Complex32 {
Complex::new(re.into(), im.into())
}
/// Alias for a [`Complex<f64>`] pubtype Complex64 = Complex<f64>;
/// Create a new [`Complex<f64>`] with arguments that can convert [`Into<f64>`]. /// /// ``` /// use num_complex::{c64, Complex64}; /// assert_eq!(c64(1, 2), Complex64::new(1.0, 2.0)); /// ``` #[inline] pubfn c64<T: Into<f64>>(re: T, im: T) -> Complex64 {
Complex::new(re.into(), im.into())
}
impl<T> Complex<T> { /// Create a new `Complex` #[inline] pubconstfn new(re: T, im: T) -> Self {
Complex { re, im }
}
}
impl<T: Clone + Num> Complex<T> { /// Returns the imaginary unit. /// /// See also [`Complex::I`]. #[inline] pubfn i() -> Self { Self::new(T::zero(), T::one())
}
/// Returns the square of the norm (since `T` doesn't necessarily /// have a sqrt function), i.e. `re^2 + im^2`. #[inline] pubfn norm_sqr(&self) -> T { self.re.clone() * self.re.clone() + self.im.clone() * self.im.clone()
}
/// Raises `self` to a signed integer power. #[inline] pubfn powi(&self, exp: i32) -> Self {
Pow::pow(self, exp)
}
}
impl<T: Clone + Signed> Complex<T> { /// Returns the L1 norm `|re| + |im|` -- the [Manhattan distance] from the origin. /// /// [Manhattan distance]: https://en.wikipedia.org/wiki/Taxicab_geometry #[inline] pubfn l1_norm(&self) -> T { self.re.abs() + self.im.abs()
}
}
#[cfg(any(feature = "std", feature = "libm"))] impl<T: Float> Complex<T> { /// Create a new Complex with a given phase: `exp(i * phase)`. /// See [cis (mathematics)](https://en.wikipedia.org/wiki/Cis_(mathematics)). #[inline] pubfn cis(phase: T) -> Self { Self::new(phase.cos(), phase.sin())
}
/// Calculate |self| #[inline] pubfn norm(self) -> T { self.re.hypot(self.im)
} /// Calculate the principal Arg of self. #[inline] pubfn arg(self) -> T { self.im.atan2(self.re)
} /// Convert to polar form (r, theta), such that /// `self = r * exp(i * theta)` #[inline] pubfn to_polar(self) -> (T, T) {
(self.norm(), self.arg())
} /// Convert a polar representation into a complex number. #[inline] pubfn from_polar(r: T, theta: T) -> Self { Self::new(r * theta.cos(), r * theta.sin())
}
/// Computes `e^(self)`, where `e` is the base of the natural logarithm. #[inline] pubfn exp(self) -> Self { // formula: e^(a + bi) = e^a (cos(b) + i*sin(b)) = from_polar(e^a, b)
let Complex { re, mut im } = self; // Treat the corner cases +∞, -∞, and NaN if re.is_infinite() { if re < T::zero() { if !im.is_finite() { returnSelf::new(T::zero(), T::zero());
}
} elseif im == T::zero() || !im.is_finite() { if im.is_infinite() {
im = T::nan();
} returnSelf::new(re, im);
}
} elseif re.is_nan() && im == T::zero() { returnself;
}
Self::from_polar(re.exp(), im)
}
/// Computes the principal value of natural logarithm of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0]`, continuous from above. /// /// The branch satisfies `-π ≤ arg(ln(z)) ≤ π`. #[inline] pubfn ln(self) -> Self { // formula: ln(z) = ln|z| + i*arg(z) let (r, theta) = self.to_polar(); Self::new(r.ln(), theta)
}
/// Computes the principal value of the square root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/2 ≤ arg(sqrt(z)) ≤ π/2`. #[inline] pubfn sqrt(self) -> Self { ifself.im.is_zero() { ifself.re.is_sign_positive() { // simple positive real √r, and copy `im` for its sign Self::new(self.re.sqrt(), self.im)
} else { // √(r e^(iπ)) = √r e^(iπ/2) = i√r // √(r e^(-iπ)) = √r e^(-iπ/2) = -i√r let re = T::zero(); let im = (-self.re).sqrt(); ifself.im.is_sign_positive() { Self::new(re, im)
} else { Self::new(re, -im)
}
}
} elseifself.re.is_zero() { // √(r e^(iπ/2)) = √r e^(iπ/4) = √(r/2) + i√(r/2) // √(r e^(-iπ/2)) = √r e^(-iπ/4) = √(r/2) - i√(r/2) let one = T::one(); let two = one + one; let x = (self.im.abs() / two).sqrt(); ifself.im.is_sign_positive() { Self::new(x, x)
} else { Self::new(x, -x)
}
} else { // formula: sqrt(r e^(it)) = sqrt(r) e^(it/2) let one = T::one(); let two = one + one; let (r, theta) = self.to_polar(); Self::from_polar(r.sqrt(), theta / two)
}
}
/// Computes the principal value of the cube root of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 0)`, continuous from above. /// /// The branch satisfies `-π/3 ≤ arg(cbrt(z)) ≤ π/3`. /// /// Note that this does not match the usual result for the cube root of /// negative real numbers. For example, the real cube root of `-8` is `-2`, /// but the principal complex cube root of `-8` is `1 + i√3`. #[inline] pubfn cbrt(self) -> Self { ifself.im.is_zero() { ifself.re.is_sign_positive() { // simple positive real ∛r, and copy `im` for its sign Self::new(self.re.cbrt(), self.im)
} else { // ∛(r e^(iπ)) = ∛r e^(iπ/3) = ∛r/2 + i∛r√3/2 // ∛(r e^(-iπ)) = ∛r e^(-iπ/3) = ∛r/2 - i∛r√3/2 let one = T::one(); let two = one + one; let three = two + one; let re = (-self.re).cbrt() / two; let im = three.sqrt() * re; ifself.im.is_sign_positive() { Self::new(re, im)
} else { Self::new(re, -im)
}
}
} elseifself.re.is_zero() { // ∛(r e^(iπ/2)) = ∛r e^(iπ/6) = ∛r√3/2 + i∛r/2 // ∛(r e^(-iπ/2)) = ∛r e^(-iπ/6) = ∛r√3/2 - i∛r/2 let one = T::one(); let two = one + one; let three = two + one; let im = self.im.abs().cbrt() / two; let re = three.sqrt() * im; ifself.im.is_sign_positive() { Self::new(re, im)
} else { Self::new(re, -im)
}
} else { // formula: cbrt(r e^(it)) = cbrt(r) e^(it/3) let one = T::one(); let three = one + one + one; let (r, theta) = self.to_polar(); Self::from_polar(r.cbrt(), theta / three)
}
}
/// Raises `self` to a floating point power. #[inline] pubfn powf(self, exp: T) -> Self { if exp.is_zero() { returnSelf::one();
} // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.to_polar(); Self::from_polar(r.powf(exp), theta * exp)
}
/// Returns the logarithm of `self` with respect to an arbitrary base. #[inline] pubfn log(self, base: T) -> Self { // formula: log_y(x) = log_y(ρ e^(i θ)) // = log_y(ρ) + log_y(e^(i θ)) = log_y(ρ) + ln(e^(i θ)) / ln(y) // = log_y(ρ) + i θ / ln(y) let (r, theta) = self.to_polar(); Self::new(r.log(base), theta / base.ln())
}
/// Computes the principal value of the inverse sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Re(asin(z)) ≤ π/2`. #[inline] pubfn asin(self) -> Self { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) let i = Self::i();
-i * ((Self::one() - self * self).sqrt() + i * self).ln()
}
/// Computes the principal value of the inverse cosine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1)`, continuous from above. /// * `(1, ∞)`, continuous from below. /// /// The branch satisfies `0 ≤ Re(acos(z)) ≤ π`. #[inline] pubfn acos(self) -> Self { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) let i = Self::i();
-i * (i * (Self::one() - self * self).sqrt() + self).ln()
}
/// Computes the principal value of the inverse tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i]`, continuous from the left. /// * `[i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Re(atan(z)) ≤ π/2`. #[inline] pubfn atan(self) -> Self { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) let i = Self::i(); let one = Self::one(); let two = one + one; ifself == i { returnSelf::new(T::zero(), T::infinity());
} elseifself == -i { returnSelf::new(T::zero(), -T::infinity());
}
((one + i * self).ln() - (one - i * self).ln()) / (two * i)
}
/// Computes the principal value of inverse hyperbolic sine of `self`. /// /// This function has two branch cuts: /// /// * `(-∞i, -i)`, continuous from the left. /// * `(i, ∞i)`, continuous from the right. /// /// The branch satisfies `-π/2 ≤ Im(asinh(z)) ≤ π/2`. #[inline] pubfn asinh(self) -> Self { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) let one = Self::one();
(self + (one + self * self).sqrt()).ln()
}
/// Computes the principal value of inverse hyperbolic cosine of `self`. /// /// This function has one branch cut: /// /// * `(-∞, 1)`, continuous from above. /// /// The branch satisfies `-π ≤ Im(acosh(z)) ≤ π` and `0 ≤ Re(acosh(z)) < ∞`. #[inline] pubfn acosh(self) -> Self { // formula: arccosh(z) = 2 ln(sqrt((z+1)/2) + sqrt((z-1)/2)) let one = Self::one(); let two = one + one;
two * (((self + one) / two).sqrt() + ((self - one) / two).sqrt()).ln()
}
/// Computes the principal value of inverse hyperbolic tangent of `self`. /// /// This function has two branch cuts: /// /// * `(-∞, -1]`, continuous from above. /// * `[1, ∞)`, continuous from below. /// /// The branch satisfies `-π/2 ≤ Im(atanh(z)) ≤ π/2`. #[inline] pubfn atanh(self) -> Self { // formula: arctanh(z) = (ln(1+z) - ln(1-z))/2 let one = Self::one(); let two = one + one; ifself == one { returnSelf::new(T::infinity(), T::zero());
} elseifself == -one { returnSelf::new(-T::infinity(), T::zero());
}
((one + self).ln() - (one - self).ln()) / two
}
/// Returns `1/self` using floating-point operations. /// /// This may be more accurate than the generic `self.inv()` in cases /// where `self.norm_sqr()` would overflow to ∞ or underflow to 0. /// /// # Examples /// /// ``` /// use num_complex::Complex64; /// let c = Complex64::new(1e300, 1e300); /// /// // The generic `inv()` will overflow. /// assert!(!c.inv().is_normal()); /// /// // But we can do better for `Float` types. /// let inv = c.finv(); /// assert!(inv.is_normal()); /// println!("{:e}", inv); /// /// let expected = Complex64::new(5e-301, -5e-301); /// assert!((inv - expected).norm() < 1e-315); /// ``` #[inline] pubfn finv(self) -> Complex<T> { let norm = self.norm(); self.conj() / norm / norm
}
/// Returns `self/other` using floating-point operations. /// /// This may be more accurate than the generic `Div` implementation in cases /// where `other.norm_sqr()` would overflow to ∞ or underflow to 0. /// /// # Examples /// /// ``` /// use num_complex::Complex64; /// let a = Complex64::new(2.0, 3.0); /// let b = Complex64::new(1e300, 1e300); /// /// // Generic division will overflow. /// assert!(!(a / b).is_normal()); /// /// // But we can do better for `Float` types. /// let quotient = a.fdiv(b); /// assert!(quotient.is_normal()); /// println!("{:e}", quotient); /// /// let expected = Complex64::new(2.5e-300, 5e-301); /// assert!((quotient - expected).norm() < 1e-315); /// ``` #[inline] pubfn fdiv(self, other: Complex<T>) -> Complex<T> { self * other.finv()
}
}
/// Computes the principal value of log base 2 of `self`. #[inline] pubfn log2(self) -> Self { Self::ln(self) / T::LN_2()
}
/// Computes the principal value of log base 10 of `self`. #[inline] pubfn log10(self) -> Self { Self::ln(self) / T::LN_10()
}
}
impl<T: FloatCore> Complex<T> { /// Checks if the given complex number is NaN #[inline] pubfn is_nan(self) -> bool { self.re.is_nan() || self.im.is_nan()
}
/// Checks if the given complex number is infinite #[inline] pubfn is_infinite(self) -> bool {
!self.is_nan() && (self.re.is_infinite() || self.im.is_infinite())
}
/// Checks if the given complex number is finite #[inline] pubfn is_finite(self) -> bool { self.re.is_finite() && self.im.is_finite()
}
/// Checks if the given complex number is normal #[inline] pubfn is_normal(self) -> bool { self.re.is_normal() && self.im.is_normal()
}
}
// Safety: `Complex<T>` is `repr(C)` and contains only instances of `T`, so we // can guarantee it contains no *added* padding. Thus, if `T: Zeroable`, // `Complex<T>` is also `Zeroable` #[cfg(feature = "bytemuck")] unsafeimpl<T: bytemuck::Zeroable> bytemuck::Zeroable for Complex<T> {}
// Safety: `Complex<T>` is `repr(C)` and contains only instances of `T`, so we // can guarantee it contains no *added* padding. Thus, if `T: Pod`, // `Complex<T>` is also `Pod` #[cfg(feature = "bytemuck")] unsafeimpl<T: bytemuck::Pod> bytemuck::Pod for Complex<T> {}
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c) impl<T: Clone + Num> Mul<Complex<T>> for Complex<T> { type Output = Self;
#[inline] fn mul(self, other: Self) -> Self::Output { let re = self.re.clone() * other.re.clone() - self.im.clone() * other.im.clone(); let im = self.re * other.im + self.im * other.re; Self::Output::new(re, im)
}
}
// (a + i b) * (c + i d) + (e + i f) == ((a*c + e) - b*d) + i (a*d + (b*c + f)) impl<T: Clone + Num + MulAdd<Output = T>> MulAdd<Complex<T>> for Complex<T> { type Output = Complex<T>;
#[inline] fn mul_add(self, other: Complex<T>, add: Complex<T>) -> Complex<T> { let re = self.re.clone().mul_add(other.re.clone(), add.re)
- (self.im.clone() * other.im.clone()); // FIXME: use mulsub when available in rust let im = self.re.mul_add(other.im, self.im.mul_add(other.re, add.im));
Complex::new(re, im)
}
} impl<'a, 'b, T: Clone + Num + MulAdd<Output = T>> MulAdd<&'b Complex<T>> for &'span>a Complex<T> { type Output = Complex<T>;
// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d) // == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)] impl<T: Clone + Num> Div<Complex<T>> for Complex<T> { type Output = Self;
#[inline] fn div(self, other: Self) -> Self::Output { let norm_sqr = other.norm_sqr(); let re = self.re.clone() * other.re.clone() + self.im.clone() * other.im.clone(); let im = self.im * other.re - self.re * other.im; Self::Output::new(re / norm_sqr.clone(), im / norm_sqr)
}
}
forward_all_binop!(impl Rem, rem);
impl<T: Clone + Num> Complex<T> { /// Find the gaussian integer corresponding to the true ratio rounded towards zero. fn div_trunc(&self, divisor: &Self) -> Self { let Complex { re, im } = self / divisor;
Complex::new(re.clone() - re % T::one(), im.clone() - im % T::one())
}
}
impl<T: Clone + Num> Rem<Complex<T>> for Complex<T> { type Output = Self;
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c) impl<T: Clone + NumAssign> MulAssign for Complex<T> { fn mul_assign(&mutself, other: Self) { let a = self.re.clone();
// (a + i b) * (c + i d) + (e + i f) == ((a*c + e) - b*d) + i (b*c + (a*d + f)) impl<T: Clone + NumAssign + MulAddAssign> MulAddAssign for Complex<T> { fn mul_add_assign(&mutself, other: Complex<T>, add: Complex<T>) { let a = self.re.clone();
impl<T> fmt::LowerExp for Complex<T> where
T: fmt::LowerExp + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "e", "", self.re, self.im, T)
}
}
impl<T> fmt::UpperExp for Complex<T> where
T: fmt::UpperExp + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "E", "", self.re, self.im, T)
}
}
impl<T> fmt::LowerHex for Complex<T> where
T: fmt::LowerHex + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "x", "0x", self.re, self.im, T)
}
}
impl<T> fmt::UpperHex for Complex<T> where
T: fmt::UpperHex + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "X", "0x", self.re, self.im, T)
}
}
impl<T> fmt::Octal for Complex<T> where
T: fmt::Octal + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "o", "0o", self.re, self.im, T)
}
}
impl<T> fmt::Binary for Complex<T> where
T: fmt::Binary + Num + PartialOrd + Clone,
{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write_complex!(f, "b", "0b", self.re, self.im, T)
}
}
fn from_str_generic<T, E, F>(s: &str, from: F) -> Result<Complex<T>, ParseComplexError<E>> where
F: Fn(&str) -> Result<T, E>,
T: Clone + Num,
{ let imag = match s.rfind('j') {
None => 'i',
_ => 'j',
};
letmut neg_b = false; letmut a = s; letmut b = "";
for (i, w) in s.as_bytes().windows(2).enumerate() { let p = w[0]; let c = w[1];
// ignore '+'/'-' if part of an exponent if (c == b'+' || c == b'-') && !(p == b'e' || p == b'E') { // trim whitespace around the separator
a = s[..=i].trim_end_matches(char::is_whitespace);
b = s[i + 2..].trim_start_matches(char::is_whitespace);
neg_b = c == b'-';
// split off real and imaginary parts if b.is_empty() { // input was either pure real or pure imaginary
b = if a.ends_with(imag) { "0" } else { "0i" };
}
let re; let neg_re; let im; let neg_im; if a.ends_with(imag) {
im = a;
neg_im = false;
re = b;
neg_re = neg_b;
} elseif b.ends_with(imag) {
re = a;
neg_re = false;
im = b;
neg_im = neg_b;
} else { return Err(ParseComplexError::expr_error());
}
// parse re let re = from(re).map_err(ParseComplexError::from_error)?; let re = if neg_re { T::zero() - re } else { re };
// pop imaginary unit off letmut im = &im[..im.len() - 1]; // handle im == "i" or im == "-i" if im.is_empty() || im == "+" {
im = "1";
} elseif im == "-" {
im = "-1";
}
// parse im let im = from(im).map_err(ParseComplexError::from_error)?; let im = if neg_im { T::zero() - im } else { im };
Ok(Complex::new(re, im))
}
impl<T> FromStr for Complex<T> where
T: FromStr + Num + Clone,
{ type Err = ParseComplexError<T::Err>;
/// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T` fn from_str(s: &str) -> Result<Self, Self::Err> {
from_str_generic(s, T::from_str)
}
}
impl<T: Num + Clone> Num for Complex<T> { type FromStrRadixErr = ParseComplexError<T::FromStrRadixErr>;
/// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T` /// /// `radix` must be <= 18; larger radix would include *i* and *j* as digits, /// which cannot be supported. /// /// The conversion returns an error if 18 <= radix <= 36; it panics if radix > 36. /// /// The elements of `T` are parsed using `Num::from_str_radix` too, and errors /// (or panics) from that are reflected here as well. fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
assert!(
radix <= 36, "from_str_radix: radix is too high (maximum 36)"
);
// larger radix would include 'i' and 'j' as digits, which cannot be supported if radix > 18 { return Err(ParseComplexError::unsupported_radix());
}
fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool { // returns true if a and b are reasonably close let close = (a == b) || (a - b).norm() < tol; if !close {
println!("{:?} != {:?}", a, b);
}
close
}
// Version that also works if re or im are +inf, -inf, or nan fn close_naninf(a: Complex64, b: Complex64) -> bool {
close_naninf_to_tol(a, b, 1.0e-10)
}
// The test values below were taken from https://en.cppreference.com/w/cpp/numeric/complex/exp
assert!(close_naninf(_1_infi.exp(), _nan_nani));
assert!(close_naninf(_neg1_infi.exp(), _nan_nani));
assert!(close_naninf(_1_nani.exp(), _nan_nani));
assert!(close_naninf(_neg1_nani.exp(), _nan_nani));
assert!(close_naninf(_inf_0i.exp(), _inf_0i));
assert!(close_naninf(_neginf_1i.exp(), 0.0 * Complex::cis(1.0)));
assert!(close_naninf(_neginf_neg1i.exp(), 0.0 * Complex::cis(-1.0)));
assert!(close_naninf(
_inf_1i.exp(),
f64::INFINITY * Complex::cis(1.0)
));
assert!(close_naninf(
_inf_neg1i.exp(),
f64::INFINITY * Complex::cis(-1.0)
));
assert!(close_naninf(_neginf_infi.exp(), _0_0i)); // Note: ±0±0i: signs of zeros are unspecified
assert!(close_naninf(_inf_infi.exp(), _inf_nani)); // Note: ±∞+NaN*i: sign of the real part is unspecified
assert!(close_naninf(_neginf_nani.exp(), _0_0i)); // Note: ±0±0i: signs of zeros are unspecified
assert!(close_naninf(_inf_nani.exp(), _inf_nani)); // Note: ±∞+NaN*i: sign of the real part is unspecified
assert!(close_naninf(_nan_0i.exp(), _nan_0i));
assert!(close_naninf(_nan_1i.exp(), _nan_nani));
assert!(close_naninf(_nan_neg1i.exp(), _nan_nani));
assert!(close_naninf(_nan_nani.exp(), _nan_nani));
}
#[test] fn test_ln() {
assert!(close(_1_0i.ln(), _0_0i));
assert!(close(_0_1i.ln(), _0_1i.scale(f64::consts::PI / 2.0)));
assert!(close(_0_0i.ln(), Complex::new(f64::neg_infinity(), 0.0)));
assert!(close(
(_neg1_1i * _05_05i).ln(),
_neg1_1i.ln() + _05_05i.ln()
)); for &c in all_consts.iter() { // ln(conj(z() = conj(ln(z))
assert!(close(c.conj().ln(), c.ln().conj())); // for this branch, -pi <= arg(ln(z)) <= pi
assert!(-f64::consts::PI <= c.ln().arg() && c.ln().arg() <= f64::consts::PI);
}
}
#[test] fn test_powc() { let a = Complex::new(2.0, -3.0); let b = Complex::new(3.0, 0.0);
assert!(close(a.powc(b), a.powf(b.re)));
assert!(close(b.powc(a), a.expf(b.re))); let c = Complex::new(1.0 / 3.0, 0.1);
assert!(close_to_tol(
a.powc(c),
Complex::new(1.65826, -0.33502), 1e-5
)); let z = Complex::new(0.0, 0.0);
assert!(close(z.powc(b), z));
assert!(z.powc(Complex64::new(0., INFINITY)).is_nan());
assert!(z.powc(Complex64::new(10., INFINITY)).is_nan());
assert!(z.powc(Complex64::new(INFINITY, INFINITY)).is_nan());
assert!(close(z.powc(Complex64::new(INFINITY, 0.)), z));
assert!(z.powc(Complex64::new(-1., 0.)).re.is_infinite());
assert!(z.powc(Complex64::new(-1., 0.)).im.is_nan());
for c in all_consts.iter() {
assert_eq!(c.powc(_0_0i), _1_0i);
}
assert_eq!(_nan_nani.powc(_0_0i), _1_0i);
}
#[test] fn test_powf() { let c = Complex64::new(2.0, -1.0); let expected = Complex64::new(-0.8684746, -16.695934);
assert!(close_to_tol(c.powf(3.5), expected, 1e-5));
assert!(close_to_tol(Pow::pow(c, 3.5_f64), expected, 1e-5));
assert!(close_to_tol(Pow::pow(c, 3.5_f32), expected, 1e-5));
for c in all_consts.iter() {
assert_eq!(c.powf(0.0), _1_0i);
}
assert_eq!(_nan_nani.powf(0.0), _1_0i);
}
#[test] fn test_log() { let c = Complex::new(2.0, -1.0); let r = c.log(10.0);
assert!(close_to_tol(r, Complex::new(0.349485, -0.20135958), 1e-5));
}
#[test] fn test_some_expf_cases() { let c = Complex::new(2.0, -1.0); let r = c.expf(10.0);
assert!(close_to_tol(r, Complex::new(-66.82015, -74.39803), 1e-5));
let c = Complex::new(5.0, -2.0); let r = c.expf(3.4);
assert!(close_to_tol(r, Complex::new(-349.25, -290.63), 1e-2));
let c = Complex::new(-1.5, 2.0 / 3.0); let r = c.expf(1.0 / 3.0);
assert!(close_to_tol(r, Complex::new(3.8637, -3.4745), 1e-2));
}
// sin(asin(z)) = z
assert!(close(c.asin().sin(), c)); // cos(acos(z)) = z
assert!(close(c.acos().cos(), c)); // tan(atan(z)) = z // i and -i are branch points if c != _0_1i && c != _0_1i.scale(-1.0) {
assert!(close(c.atan().tan(), c));
}
// sinh(asinh(z)) = z
assert!(close(c.asinh().sinh(), c)); // cosh(acosh(z)) = z
assert!(close(c.acosh().cosh(), c)); // tanh(atanh(z)) = z // 1 and -1 are branch points if c != _1_0i && c != _1_0i.scale(-1.0) {
assert!(close(c.atanh().tanh(), c));
}
// Test both a + b and a += b
macro_rules! test_a_op_b {
($a:ident + $b:expr, $answer:expr) => {
assert_eq!($a + $b, $answer);
assert_eq!(
{ letmut x = $a;
x += $b;
x
},
$answer
);
};
($a:ident - $b:expr, $answer:expr) => {
assert_eq!($a - $b, $answer);
assert_eq!(
{ letmut x = $a;
x -= $b;
x
},
$answer
);
};
($a:ident * $b:expr, $answer:expr) => {
assert_eq!($a * $b, $answer);
assert_eq!(
{ letmut x = $a;
x *= $b;
x
},
$answer
);
};
($a:ident / $b:expr, $answer:expr) => {
assert_eq!($a / $b, $answer);
assert_eq!(
{ letmut x = $a;
x /= $b;
x
},
$answer
);
};
($a:ident % $b:expr, $answer:expr) => {
assert_eq!($a % $b, $answer);
assert_eq!(
{ letmut x = $a;
x %= $b;
x
},
$answer
);
};
}
// Test both a + b and a + &b
macro_rules! test_op {
($a:ident $op:tt $b:expr, $answer:expr) => {
test_a_op_b!($a $op $b, $answer);
test_a_op_b!($a $op &$b, $answer);
};
}
mod complex_arithmetic { usesuper::{_05_05i, _0_0i, _0_1i, _1_0i, _1_1i, _4_2i, _neg1_1i, all_consts}; use num_traits::{MulAdd, MulAddAssign, Zero};
for &a in &all_consts { for &b in &all_consts { for &c in &all_consts { let abc = a * b + c;
assert_eq!(a.mul_add(b, c), abc); letmut x = a;
x.mul_add_assign(b, c);
assert_eq!(x, abc);
}
}
}
}
for &a in &all_consts { for &b in &all_consts { for &c in &all_consts { let abc = a * b + c;
assert_eq!(a.mul_add(b, c), abc); letmut x = a;
x.mul_add_assign(b, c);
assert_eq!(x, abc);
}
}
}
}
#[test] fn test_div() {
test_op!(_neg1_1i / _0_1i, _1_1i); for &c in all_consts.iter() { if c != Zero::zero() {
test_op!(c / c, _1_0i);
}
}
}
let c = Complex::new(-10, -10000);
assert_eq!(format!("{}", c), "-10-10000i"); #[cfg(feature = "std")]
assert_eq!(format!("{:16}", c), " -10-10000i");
}
#[test] fn test_hash() { let a = Complex::new(0i32, 0i32); let b = Complex::new(1i32, 0i32); let c = Complex::new(0i32, 1i32);
assert!(crate::hash(&a) != crate::hash(&b));
assert!(crate::hash(&b) != crate::hash(&c));
assert!(crate::hash(&c) != crate::hash(&a));
}
#[test] fn test_hashset() { use std::collections::HashSet; let a = Complex::new(0i32, 0i32); let b = Complex::new(1i32, 0i32); let c = Complex::new(0i32, 1i32);
let set: HashSet<_> = [a, b, c].iter().cloned().collect();
assert!(set.contains(&a));
assert!(set.contains(&b));
assert!(set.contains(&c));
assert!(!set.contains(&(a + b + c)));
}
#[test] fn test_is_nan() {
assert!(!_1_1i.is_nan()); let a = Complex::new(f64::NAN, f64::NAN);
assert!(a.is_nan());
}
#[test] fn test_is_nan_special_cases() { let a = Complex::new(0f64, f64::NAN); let b = Complex::new(f64::NAN, 0f64);
assert!(a.is_nan());
assert!(b.is_nan());
}
#[test] fn test_is_infinite() { let a = Complex::new(2f64, f64::INFINITY);
assert!(a.is_infinite());
}
#[test] fn test_is_normal() { let a = Complex::new(0f64, f64::NAN); let b = Complex::new(2f64, f64::INFINITY);
assert!(!a.is_normal());
assert!(!b.is_normal());
assert!(_1_1i.is_normal());
}
#[test] #[should_panic(expected = "radix is too high")] fn test_from_str_radix_fail() { // ensure we preserve the underlying panic on radix > 36 let _complex = Complex64::from_str_radix("1", 37);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.