Răsfoiți Sursa

adds basic parser for complex numbers in Cartesian form

Alan Liddell 7 ani în urmă
părinte
comite
3c490cdee4
1 a modificat fișierele cu 121 adăugiri și 18 ștergeri
  1. 121 18
      complex/src/lib.rs

+ 121 - 18
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<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));