adds basic parser for complex numbers in Cartesian form
This commit is contained in:
parent
31fa9f626a
commit
3c490cdee4
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue