|
@@ -22,10 +22,12 @@ extern crate rustc_serialize;
|
|
|
#[cfg(feature = "serde")]
|
|
|
extern crate serde;
|
|
|
|
|
|
+use std::error::Error;
|
|
|
use std::fmt;
|
|
|
#[cfg(test)]
|
|
|
use std::hash;
|
|
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
|
|
+use std::str::FromStr;
|
|
|
|
|
|
use traits::{Zero, One, Num, Float};
|
|
|
|
|
@@ -178,7 +180,7 @@ impl<T: Clone + Float> Complex<T> {
|
|
|
let (r, theta) = self.to_polar();
|
|
|
Complex::from_polar(&(r.sqrt()), &(theta/two))
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// Raises `self` to a floating point power.
|
|
|
#[inline]
|
|
|
pub fn powf(&self, exp: T) -> Complex<T> {
|
|
@@ -187,25 +189,25 @@ impl<T: Clone + Float> Complex<T> {
|
|
|
let (r, theta) = self.to_polar();
|
|
|
Complex::from_polar(&r.powf(exp), &(theta*exp))
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// Returns the logarithm of `self` with respect to an arbitrary base.
|
|
|
#[inline]
|
|
|
pub fn log(&self, base: T) -> Complex<T> {
|
|
|
- // 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)
|
|
|
+ // 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();
|
|
|
Complex::new(r.log(base), theta / base.ln())
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// Raises `self` to a complex power.
|
|
|
#[inline]
|
|
|
pub fn powc(&self, exp: Complex<T>) -> Complex<T> {
|
|
|
// formula: x^y = (a + i b)^(c + i d)
|
|
|
- // = (ρ e^(i θ))^c (ρ e^(i θ))^(i d)
|
|
|
+ // = (ρ e^(i θ))^c (ρ e^(i θ))^(i d)
|
|
|
// where ρ=|x| and θ=arg(x)
|
|
|
// = ρ^c e^(−d θ) e^(i c θ) ρ^(i d)
|
|
|
- // = p^c e^(−d θ) (cos(c θ)
|
|
|
+ // = p^c e^(−d θ) (cos(c θ)
|
|
|
// + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ)))
|
|
|
// = p^c e^(−d θ) (
|
|
|
// cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ))
|
|
@@ -214,14 +216,14 @@ impl<T: Clone + Float> Complex<T> {
|
|
|
// = from_polar(p^c e^(−d θ), c θ + d ln(ρ))
|
|
|
let (r, theta) = self.to_polar();
|
|
|
Complex::from_polar(
|
|
|
- &(r.powf(exp.re) * (-exp.im * theta).exp()),
|
|
|
+ &(r.powf(exp.re) * (-exp.im * theta).exp()),
|
|
|
&(exp.re * theta + exp.im * r.ln()))
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// Raises a floating point number to the complex power `self`.
|
|
|
#[inline]
|
|
|
pub fn expf(&self, base: T) -> Complex<T> {
|
|
|
- // formula: x^(a+bi) = x^a x^bi = x^a e^(b ln(x) i)
|
|
|
+ // formula: x^(a+bi) = x^a x^bi = x^a e^(b ln(x) i)
|
|
|
// = from_polar(x^a, b ln(x))
|
|
|
Complex::from_polar(&base.powf(self.re), &(self.im * base.ln()))
|
|
|
}
|
|
@@ -740,6 +742,77 @@ impl<T> fmt::Binary for Complex<T> where
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl<T> FromStr for Complex<T> where
|
|
|
+ T: FromStr + Num + PartialOrd + Clone
|
|
|
+{
|
|
|
+ type Err = ParseComplexError;
|
|
|
+
|
|
|
+ /// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T`
|
|
|
+ fn from_str(s: &str) -> Result<Complex<T>, ParseComplexError>
|
|
|
+ {
|
|
|
+ // first try to split on " + "
|
|
|
+ let mut split_p = s.splitn(2, " + ");
|
|
|
+
|
|
|
+ let mut a = match split_p.next() {
|
|
|
+ None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }),
|
|
|
+ Some(s) => s.to_string()
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut b = match split_p.next() {
|
|
|
+ // no second item could mean we need to split on " - " instead
|
|
|
+ None => {
|
|
|
+ let mut split_m = s.splitn(2, " - ");
|
|
|
+
|
|
|
+ a = match split_m.next() {
|
|
|
+ None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }),
|
|
|
+ Some(s) => s.to_string()
|
|
|
+ };
|
|
|
+
|
|
|
+ let c = match split_m.next() {
|
|
|
+ None => {
|
|
|
+ // if `a` is imaginary, let `b` be real (and vice versa)
|
|
|
+ match a.rfind('i') {
|
|
|
+ None => "0i".to_string(),
|
|
|
+ Some(u) => "0".to_string()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Some(s) => {
|
|
|
+ "-".to_string() + s
|
|
|
+ }
|
|
|
+ };
|
|
|
+ c
|
|
|
+ },
|
|
|
+ Some(s) => s.to_string()
|
|
|
+ };
|
|
|
+
|
|
|
+ let re = match a.rfind('i') {
|
|
|
+ None => {
|
|
|
+ try!(T::from_str(&a)
|
|
|
+ .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError }))
|
|
|
+ },
|
|
|
+ Some(u) => {
|
|
|
+ try!(T::from_str(&b)
|
|
|
+ .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError }))
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let im = match a.rfind('i') {
|
|
|
+ None => {
|
|
|
+ b.pop();
|
|
|
+ try!(T::from_str(&b)
|
|
|
+ .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError }))
|
|
|
+ },
|
|
|
+ Some(u) => {
|
|
|
+ a.pop();
|
|
|
+ try!(T::from_str(&a)
|
|
|
+ .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError }))
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ Ok(Complex::new(re, im))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[cfg(feature = "serde")]
|
|
|
impl<T> serde::Serialize for Complex<T>
|
|
|
where T: serde::Serialize
|
|
@@ -763,6 +836,36 @@ impl<T> serde::Deserialize for Complex<T> where
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#[derive(Copy, Clone, Debug, PartialEq)]
|
|
|
+pub struct ParseComplexError {
|
|
|
+ kind: ComplexErrorKind,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Copy, Clone, Debug, PartialEq)]
|
|
|
+enum ComplexErrorKind {
|
|
|
+ ParseError,
|
|
|
+}
|
|
|
+
|
|
|
+impl Error for ParseComplexError {
|
|
|
+ fn description(&self) -> &str {
|
|
|
+ self.kind.description()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl fmt::Display for ParseComplexError {
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
+ self.description().fmt(f)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl ComplexErrorKind {
|
|
|
+ fn description(&self) -> &'static str {
|
|
|
+ match *self {
|
|
|
+ ComplexErrorKind::ParseError => "failed to parse complex number",
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[cfg(test)]
|
|
|
fn hash<T: hash::Hash>(x: &T) -> u64 {
|
|
|
use std::hash::{BuildHasher, Hasher};
|
|
@@ -880,7 +983,7 @@ mod test {
|
|
|
fn close(a: Complex64, b: Complex64) -> bool {
|
|
|
close_to_tol(a, b, 1e-10)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool {
|
|
|
// returns true if a and b are reasonably close
|
|
|
(a == b) || (a-b).norm() < tol
|
|
@@ -914,7 +1017,7 @@ mod test {
|
|
|
assert!(-f64::consts::PI <= c.ln().arg() && c.ln().arg() <= f64::consts::PI);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
#[test]
|
|
|
fn test_powc()
|
|
|
{
|
|
@@ -925,7 +1028,7 @@ mod test {
|
|
|
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));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
#[test]
|
|
|
fn test_powf()
|
|
|
{
|
|
@@ -933,7 +1036,7 @@ mod test {
|
|
|
let r = c.powf(3.5);
|
|
|
assert!(close_to_tol(r, Complex::new(-0.8684746, -16.695934), 1e-5));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
#[test]
|
|
|
fn test_log()
|
|
|
{
|
|
@@ -941,18 +1044,18 @@ mod test {
|
|
|
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));
|