From 3c490cdee4639e533ec485dabda9a2a5bb4317d3 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Mon, 10 Jul 2017 05:57:38 -0400 Subject: [PATCH] adds basic parser for complex numbers in Cartesian form --- complex/src/lib.rs | 139 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 121 insertions(+), 18 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index cb605a2..d42a886 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -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 Complex { 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 { @@ -187,25 +189,25 @@ impl Complex { 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 { - // 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) -> Complex { // 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 Complex { // = 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 { - // 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 fmt::Binary for Complex where } } +impl FromStr for Complex 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, 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 serde::Serialize for Complex where T: serde::Serialize @@ -763,6 +836,36 @@ impl serde::Deserialize for Complex 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(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));