From 3c490cdee4639e533ec485dabda9a2a5bb4317d3 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Mon, 10 Jul 2017 05:57:38 -0400 Subject: [PATCH 01/12] 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)); From 506fbaf4e7748964a203542373e1bfd6a572ab64 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Mon, 10 Jul 2017 08:09:49 -0400 Subject: [PATCH 02/12] swap 'Some(u)' out for '_' to suppress unused variable warnings --- complex/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index d42a886..5743468 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -773,7 +773,7 @@ impl FromStr for Complex where // if `a` is imaginary, let `b` be real (and vice versa) match a.rfind('i') { None => "0i".to_string(), - Some(u) => "0".to_string() + _ => "0".to_string() } } Some(s) => { @@ -790,7 +790,7 @@ impl FromStr for Complex where try!(T::from_str(&a) .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) }, - Some(u) => { + _ => { try!(T::from_str(&b) .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) } @@ -802,7 +802,7 @@ impl FromStr for Complex where 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 })) From e5e82660099a22c8cbf2d2b2b4952766f326c6de Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Mon, 10 Jul 2017 09:51:46 -0400 Subject: [PATCH 03/12] handles case where imaginary part is just 'i' or '-i', adds tests --- complex/src/lib.rs | 84 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index 5743468..e90fd25 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -755,7 +755,7 @@ impl FromStr for Complex where let mut a = match split_p.next() { None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(s) => s.to_string() + Some(s) => s.trim_right().to_string() }; let mut b = match split_p.next() { @@ -765,7 +765,7 @@ impl FromStr for Complex where a = match split_m.next() { None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(s) => s.to_string() + Some(s) => s.trim_right().to_string() }; let c = match split_m.next() { @@ -777,12 +777,12 @@ impl FromStr for Complex where } } Some(s) => { - "-".to_string() + s + "-".to_string() + s.trim_left() } }; c }, - Some(s) => s.to_string() + Some(s) => s.trim_left().to_string() }; let re = match a.rfind('i') { @@ -798,12 +798,51 @@ impl FromStr for Complex where let im = match a.rfind('i') { None => { - b.pop(); + // a is real + match b.pop() { + // b was empty + None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), + Some(c) => { + if c == 'i' { + // b is imaginary + if b.is_empty() { + // b was just 'i' + b = "1".to_string(); + } else if b == "-" { + // b was just '-i' + b = "-1".to_string(); + } + } else { + // given a sum or difference of two reals + return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); + } + } + } + try!(T::from_str(&b) .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) }, _ => { - a.pop(); + // a contains an 'i' + match a.pop() { + None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), + Some(c) => { + if c == 'i' { + // a is imaginary + if a.is_empty() { + // a was just 'i' + a = "1".to_string(); + } else if a == "-" { + // a was just '-i' + a = "-1".to_string(); + } + } else { + // given a sum or difference of two reals + return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); + } + } + } + try!(T::from_str(&a) .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) } @@ -881,6 +920,7 @@ mod test { use super::{Complex64, Complex}; use std::f64; + use std::str::FromStr; use traits::{Zero, One, Float}; @@ -1502,4 +1542,36 @@ mod test { assert!(!b.is_normal()); assert!(_1_1i.is_normal()); } + + #[test] + fn test_from_string() { + fn test(z: Complex64, s: String) { + assert_eq!(FromStr::from_str(&s), Ok(z)); + } + test(_0_0i, "0".to_string()); + test(_0_0i, "0i".to_string()); + test(_0_0i, "-0".to_string()); + test(_0_0i, "-0i".to_string()); + test(_0_0i, "0 + 0i".to_string()); + test(_0_0i, "0 - 0i".to_string()); + + test(_1_0i, "1".to_string()); + test(_1_0i, "1 + 0i".to_string()); + test(_1_0i, "1 - 0i".to_string()); + + test(_1_1i, "1 + i".to_string()); + test(_1_1i, "1 + 1i".to_string()); + + test(_0_1i, "i".to_string()); + test(_0_1i, "1i".to_string()); + test(_0_1i, "0 + i".to_string()); + test(_0_1i, "-0 + i".to_string()); + test(_0_1i, "0 + 1i".to_string()); + test(_0_1i, "-0 + 1i".to_string()); + + test(_neg1_1i, "-1 + i".to_string()); + test(_neg1_1i, "-1 + 1i".to_string()); + + test(_05_05i, "0.5 + 0.5i".to_string()); + } } From ac746c2a5dfad2cee874991a072f6a0efd225a63 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Thu, 13 Jul 2017 14:05:44 -0400 Subject: [PATCH 04/12] Allows for arbitrary spacing between operands when parsing Complex --- complex/src/lib.rs | 111 +++++++++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 38 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index e90fd25..348f838 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -750,42 +750,64 @@ impl FromStr for Complex where /// 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 imag = 'i'; - let mut a = match split_p.next() { - None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(s) => s.trim_right().to_string() - }; + let mut a = String::with_capacity(s.len()); + let mut b = String::with_capacity(s.len()); - 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, " - "); + let mut first = true; + let mut char_indices = s.char_indices(); + let mut pc = ' '; - a = match split_m.next() { - None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(s) => s.trim_right().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(), - _ => "0".to_string() + loop { + match char_indices.next() { + Some(t) => { + let i = t.0; + let cc = t.1; + if cc == '+' { + // handle exponents + if pc != 'e' && pc != 'E' { + first = false; + // don't carry '+' over into b + pc = ' '; + continue; + } + } else if cc == '-' { + // handle exponents and negative numbers + if i > 0 && pc != 'e' && pc != 'E' { + first = false; + // DO carry '-' over into b } } - Some(s) => { - "-".to_string() + s.trim_left() - } - }; - c - }, - Some(s) => s.trim_left().to_string() - }; - let re = match a.rfind('i') { + if !first && cc == ' ' && pc == '-' { + // ignore whitespace between minus sign and next number + continue; + } + + if first { + a.push(cc); + } else { + b.push(cc); + } + + pc = cc; + }, + None => break, + } + } + + a = a.trim_right().to_string(); + b = b.trim_left().to_string(); + + if b.is_empty() { + b = match a.rfind(imag) { + None => "0i".to_string(), + _ => "0".to_string() + }; + } + + let re = match a.rfind(imag) { None => { try!(T::from_str(&a) .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) @@ -796,20 +818,20 @@ impl FromStr for Complex where } }; - let im = match a.rfind('i') { + let im = match a.rfind(imag) { None => { // a is real match b.pop() { // b was empty None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), Some(c) => { - if c == 'i' { + if c == imag { // b is imaginary if b.is_empty() { - // b was just 'i' + // b was just 'imag' b = "1".to_string(); } else if b == "-" { - // b was just '-i' + // b was just '-imag' b = "-1".to_string(); } } else { @@ -823,17 +845,17 @@ impl FromStr for Complex where .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) }, _ => { - // a contains an 'i' + // a contains imag match a.pop() { None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), Some(c) => { - if c == 'i' { + if c == imag { // a is imaginary if a.is_empty() { - // a was just 'i' + // a was just 'imag' a = "1".to_string(); } else if a == "-" { - // a was just '-i' + // a was just '-imag' a = "-1".to_string(); } } else { @@ -1553,25 +1575,38 @@ mod test { test(_0_0i, "-0".to_string()); test(_0_0i, "-0i".to_string()); test(_0_0i, "0 + 0i".to_string()); + test(_0_0i, "0+0i".to_string()); test(_0_0i, "0 - 0i".to_string()); + test(_0_0i, "0-0i".to_string()); test(_1_0i, "1".to_string()); test(_1_0i, "1 + 0i".to_string()); + test(_1_0i, "1+0i".to_string()); test(_1_0i, "1 - 0i".to_string()); + test(_1_0i, "1-0i".to_string()); test(_1_1i, "1 + i".to_string()); + test(_1_1i, "1+i".to_string()); test(_1_1i, "1 + 1i".to_string()); + test(_1_1i, "1+1i".to_string()); test(_0_1i, "i".to_string()); test(_0_1i, "1i".to_string()); test(_0_1i, "0 + i".to_string()); + test(_0_1i, "0+i".to_string()); test(_0_1i, "-0 + i".to_string()); + test(_0_1i, "-0+i".to_string()); test(_0_1i, "0 + 1i".to_string()); + test(_0_1i, "0+1i".to_string()); test(_0_1i, "-0 + 1i".to_string()); + test(_0_1i, "-0+1i".to_string()); test(_neg1_1i, "-1 + i".to_string()); + test(_neg1_1i, "-1+i".to_string()); test(_neg1_1i, "-1 + 1i".to_string()); + test(_neg1_1i, "-1+1i".to_string()); test(_05_05i, "0.5 + 0.5i".to_string()); + test(_05_05i, "0.5+0.5i".to_string()); } } From 14d323f5a37364a423fbbc361892e2d8194b8c96 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Thu, 13 Jul 2017 14:13:04 -0400 Subject: [PATCH 05/12] Allows 'j' for imaginary unit --- complex/src/lib.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index 348f838..e71cf5b 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -750,7 +750,10 @@ impl FromStr for Complex where /// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T` fn from_str(s: &str) -> Result, ParseComplexError> { - let imag = 'i'; + let imag = match s.rfind('j') { + None => 'i', + _ => 'j' + }; let mut a = String::with_capacity(s.len()); let mut b = String::with_capacity(s.len()); @@ -1572,41 +1575,71 @@ mod test { } test(_0_0i, "0".to_string()); test(_0_0i, "0i".to_string()); + test(_0_0i, "0j".to_string()); test(_0_0i, "-0".to_string()); test(_0_0i, "-0i".to_string()); + test(_0_0i, "-0j".to_string()); test(_0_0i, "0 + 0i".to_string()); + test(_0_0i, "0 + 0j".to_string()); test(_0_0i, "0+0i".to_string()); + test(_0_0i, "0+0j".to_string()); test(_0_0i, "0 - 0i".to_string()); + test(_0_0i, "0 - 0j".to_string()); test(_0_0i, "0-0i".to_string()); + test(_0_0i, "0-0j".to_string()); test(_1_0i, "1".to_string()); test(_1_0i, "1 + 0i".to_string()); + test(_1_0i, "1 + 0j".to_string()); test(_1_0i, "1+0i".to_string()); + test(_1_0i, "1+0j".to_string()); test(_1_0i, "1 - 0i".to_string()); + test(_1_0i, "1 - 0j".to_string()); test(_1_0i, "1-0i".to_string()); + test(_1_0i, "1-0j".to_string()); test(_1_1i, "1 + i".to_string()); + test(_1_1i, "1 + j".to_string()); test(_1_1i, "1+i".to_string()); + test(_1_1i, "1+j".to_string()); test(_1_1i, "1 + 1i".to_string()); + test(_1_1i, "1 + 1j".to_string()); test(_1_1i, "1+1i".to_string()); + test(_1_1i, "1+1j".to_string()); test(_0_1i, "i".to_string()); + test(_0_1i, "j".to_string()); test(_0_1i, "1i".to_string()); + test(_0_1i, "1j".to_string()); test(_0_1i, "0 + i".to_string()); + test(_0_1i, "0 + j".to_string()); test(_0_1i, "0+i".to_string()); + test(_0_1i, "0+j".to_string()); test(_0_1i, "-0 + i".to_string()); + test(_0_1i, "-0 + j".to_string()); test(_0_1i, "-0+i".to_string()); + test(_0_1i, "-0+j".to_string()); test(_0_1i, "0 + 1i".to_string()); + test(_0_1i, "0 + 1j".to_string()); test(_0_1i, "0+1i".to_string()); + test(_0_1i, "0+1j".to_string()); test(_0_1i, "-0 + 1i".to_string()); + test(_0_1i, "-0 + 1j".to_string()); test(_0_1i, "-0+1i".to_string()); + test(_0_1i, "-0+1j".to_string()); test(_neg1_1i, "-1 + i".to_string()); + test(_neg1_1i, "-1 + j".to_string()); test(_neg1_1i, "-1+i".to_string()); + test(_neg1_1i, "-1+j".to_string()); test(_neg1_1i, "-1 + 1i".to_string()); + test(_neg1_1i, "-1 + 1j".to_string()); test(_neg1_1i, "-1+1i".to_string()); + test(_neg1_1i, "-1+1j".to_string()); test(_05_05i, "0.5 + 0.5i".to_string()); + test(_05_05i, "0.5 + 0.5j".to_string()); test(_05_05i, "0.5+0.5i".to_string()); + test(_05_05i, "0.5+0.5j".to_string()); } } From 97e6d45d036d8ba9118b2dd0661a73eaa23f4d05 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Thu, 13 Jul 2017 14:50:27 -0400 Subject: [PATCH 06/12] Adds more tests, including failure cases --- complex/src/lib.rs | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index e71cf5b..bbf046a 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -1569,77 +1569,76 @@ mod test { } #[test] - fn test_from_string() { + fn test_from_str() { fn test(z: Complex64, s: String) { assert_eq!(FromStr::from_str(&s), Ok(z)); } - test(_0_0i, "0".to_string()); - test(_0_0i, "0i".to_string()); - test(_0_0i, "0j".to_string()); - test(_0_0i, "-0".to_string()); - test(_0_0i, "-0i".to_string()); - test(_0_0i, "-0j".to_string()); test(_0_0i, "0 + 0i".to_string()); - test(_0_0i, "0 + 0j".to_string()); - test(_0_0i, "0+0i".to_string()); test(_0_0i, "0+0j".to_string()); - test(_0_0i, "0 - 0i".to_string()); test(_0_0i, "0 - 0j".to_string()); test(_0_0i, "0-0i".to_string()); - test(_0_0i, "0-0j".to_string()); + test(_0_0i, "0i + 0".to_string()); + test(_0_0i, "0".to_string()); + test(_0_0i, "-0".to_string()); + test(_0_0i, "0i".to_string()); + test(_0_0i, "0j".to_string()); + test(_0_0i, "-0i".to_string()); - test(_1_0i, "1".to_string()); test(_1_0i, "1 + 0i".to_string()); - test(_1_0i, "1 + 0j".to_string()); - test(_1_0i, "1+0i".to_string()); test(_1_0i, "1+0j".to_string()); - test(_1_0i, "1 - 0i".to_string()); test(_1_0i, "1 - 0j".to_string()); test(_1_0i, "1-0i".to_string()); - test(_1_0i, "1-0j".to_string()); + test(_1_0i, "-0j+1".to_string()); + test(_1_0i, "1".to_string()); test(_1_1i, "1 + i".to_string()); - test(_1_1i, "1 + j".to_string()); - test(_1_1i, "1+i".to_string()); test(_1_1i, "1+j".to_string()); - test(_1_1i, "1 + 1i".to_string()); test(_1_1i, "1 + 1j".to_string()); test(_1_1i, "1+1i".to_string()); - test(_1_1i, "1+1j".to_string()); + test(_1_1i, "i + 1".to_string()); + test(_1_1i, "1i+1".to_string()); + test(_1_1i, "j+1".to_string()); - test(_0_1i, "i".to_string()); - test(_0_1i, "j".to_string()); - test(_0_1i, "1i".to_string()); - test(_0_1i, "1j".to_string()); test(_0_1i, "0 + i".to_string()); - test(_0_1i, "0 + j".to_string()); - test(_0_1i, "0+i".to_string()); test(_0_1i, "0+j".to_string()); - test(_0_1i, "-0 + i".to_string()); test(_0_1i, "-0 + j".to_string()); test(_0_1i, "-0+i".to_string()); - test(_0_1i, "-0+j".to_string()); test(_0_1i, "0 + 1i".to_string()); - test(_0_1i, "0 + 1j".to_string()); - test(_0_1i, "0+1i".to_string()); test(_0_1i, "0+1j".to_string()); - test(_0_1i, "-0 + 1i".to_string()); test(_0_1i, "-0 + 1j".to_string()); test(_0_1i, "-0+1i".to_string()); - test(_0_1i, "-0+1j".to_string()); + test(_0_1i, "j + 0".to_string()); + test(_0_1i, "i".to_string()); + test(_0_1i, "j".to_string()); + test(_0_1i, "1j".to_string()); test(_neg1_1i, "-1 + i".to_string()); - test(_neg1_1i, "-1 + j".to_string()); - test(_neg1_1i, "-1+i".to_string()); test(_neg1_1i, "-1+j".to_string()); - test(_neg1_1i, "-1 + 1i".to_string()); test(_neg1_1i, "-1 + 1j".to_string()); test(_neg1_1i, "-1+1i".to_string()); - test(_neg1_1i, "-1+1j".to_string()); + test(_neg1_1i, "1i-1".to_string()); + test(_neg1_1i, "j + -1".to_string()); test(_05_05i, "0.5 + 0.5i".to_string()); - test(_05_05i, "0.5 + 0.5j".to_string()); - test(_05_05i, "0.5+0.5i".to_string()); test(_05_05i, "0.5+0.5j".to_string()); + test(_05_05i, "5e-1+0.5j".to_string()); + test(_05_05i, "5E-1 + 0.5j".to_string()); + test(_05_05i, "5E-1i + 0.5".to_string()); + test(_05_05i, "0.05e+1j + 50E-2".to_string()); + } + + #[test] + fn test_from_str_fail() { + fn test(s: &str) { + let complex: Result = FromStr::from_str(s); + assert!(complex.is_err()); + } + test("foo"); + test("6E"); + test("0 + 2.718"); + test("1 - -2i"); + test("314e-2ij"); + test("4.3j - i"); + test("1i - 2i"); } } From 38ee1304bb1e92be75cfa8a5d6159bb349b72eac Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Sun, 16 Jul 2017 09:51:55 -0400 Subject: [PATCH 07/12] Cleaned out some allocations --- complex/src/lib.rs | 228 +++++++++++++++++++-------------------------- 1 file changed, 97 insertions(+), 131 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index bbf046a..c34ccf3 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -755,123 +755,89 @@ impl FromStr for Complex where _ => 'j' }; - let mut a = String::with_capacity(s.len()); let mut b = String::with_capacity(s.len()); - let mut first = true; + let mut char_indices = s.char_indices(); let mut pc = ' '; + let mut split_index = s.len(); loop { match char_indices.next() { Some(t) => { let i = t.0; let cc = t.1; - if cc == '+' { - // handle exponents - if pc != 'e' && pc != 'E' { + + if cc == '+' && pc != 'e' && pc != 'E' { + // ignore '+' if part of an exponent + if first { + split_index = i; first = false; - // don't carry '+' over into b - pc = ' '; - continue; } - } else if cc == '-' { - // handle exponents and negative numbers - if i > 0 && pc != 'e' && pc != 'E' { + // don't carry '+' over into b + pc = ' '; + continue; + } else if cc == '-' && pc != 'e' && pc != 'E' && i > 0 { + // ignore '-' if part of an exponent or begins the string + if first { + split_index = i; first = false; - // DO carry '-' over into b } + // DO carry '-' over into b } - if !first && cc == ' ' && pc == '-' { + if pc == '-' && cc == ' ' && !first { // ignore whitespace between minus sign and next number continue; } - if first { - a.push(cc); - } else { + if !first { b.push(cc); } - pc = cc; }, None => break, } } - a = a.trim_right().to_string(); - b = b.trim_left().to_string(); - + // split off real and imaginary parts, trim whitespace + let (a, _) = s.split_at(split_index); + let a = a.trim_right(); + let mut b = b.trim_left(); + // input was either pure real or pure imaginary if b.is_empty() { - b = match a.rfind(imag) { - None => "0i".to_string(), - _ => "0".to_string() + b = match a.ends_with(imag) { + false => "0i", + true => "0" }; } - let re = match a.rfind(imag) { - None => { - try!(T::from_str(&a) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) - }, - _ => { - try!(T::from_str(&b) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) - } - }; + let re; + let im; + if a.ends_with(imag) { + im = a; re = b; + } else if b.ends_with(imag) { + re = a; im = b; + } else { + return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); + } - let im = match a.rfind(imag) { - None => { - // a is real - match b.pop() { - // b was empty - None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(c) => { - if c == imag { - // b is imaginary - if b.is_empty() { - // b was just 'imag' - b = "1".to_string(); - } else if b == "-" { - // b was just '-imag' - b = "-1".to_string(); - } - } else { - // given a sum or difference of two reals - return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); - } - } - } + // parse re + let re = try!(T::from_str(re) + .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })); - try!(T::from_str(&b) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) - }, - _ => { - // a contains imag - match a.pop() { - None => return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }), - Some(c) => { - if c == imag { - // a is imaginary - if a.is_empty() { - // a was just 'imag' - a = "1".to_string(); - } else if a == "-" { - // a was just '-imag' - a = "-1".to_string(); - } - } else { - // given a sum or difference of two reals - return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); - } - } - } + // pop imaginary unit off + let mut im = &im[..im.len()-1]; + // handle im == "i" or im == "-i" + if im.is_empty() { + im = "1"; + } else if im == "-" { + im = "-1"; + } - try!(T::from_str(&a) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })) - } - }; + // parse im + let im = try!(T::from_str(im) + .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })); Ok(Complex::new(re, im)) } @@ -1570,61 +1536,61 @@ mod test { #[test] fn test_from_str() { - fn test(z: Complex64, s: String) { - assert_eq!(FromStr::from_str(&s), Ok(z)); + fn test(z: Complex64, s: &str) { + assert_eq!(FromStr::from_str(s), Ok(z)); } - test(_0_0i, "0 + 0i".to_string()); - test(_0_0i, "0+0j".to_string()); - test(_0_0i, "0 - 0j".to_string()); - test(_0_0i, "0-0i".to_string()); - test(_0_0i, "0i + 0".to_string()); - test(_0_0i, "0".to_string()); - test(_0_0i, "-0".to_string()); - test(_0_0i, "0i".to_string()); - test(_0_0i, "0j".to_string()); - test(_0_0i, "-0i".to_string()); + test(_0_0i, "0 + 0i"); + test(_0_0i, "0+0j"); + test(_0_0i, "0 - 0j"); + test(_0_0i, "0-0i"); + test(_0_0i, "0i + 0"); + test(_0_0i, "0"); + test(_0_0i, "-0"); + test(_0_0i, "0i"); + test(_0_0i, "0j"); + test(_0_0i, "-0i"); - test(_1_0i, "1 + 0i".to_string()); - test(_1_0i, "1+0j".to_string()); - test(_1_0i, "1 - 0j".to_string()); - test(_1_0i, "1-0i".to_string()); - test(_1_0i, "-0j+1".to_string()); - test(_1_0i, "1".to_string()); + test(_1_0i, "1 + 0i"); + test(_1_0i, "1+0j"); + test(_1_0i, "1 - 0j"); + test(_1_0i, "1-0i"); + test(_1_0i, "-0j+1"); + test(_1_0i, "1"); - test(_1_1i, "1 + i".to_string()); - test(_1_1i, "1+j".to_string()); - test(_1_1i, "1 + 1j".to_string()); - test(_1_1i, "1+1i".to_string()); - test(_1_1i, "i + 1".to_string()); - test(_1_1i, "1i+1".to_string()); - test(_1_1i, "j+1".to_string()); + test(_1_1i, "1 + i"); + test(_1_1i, "1+j"); + test(_1_1i, "1 + 1j"); + test(_1_1i, "1+1i"); + test(_1_1i, "i + 1"); + test(_1_1i, "1i+1"); + test(_1_1i, "j+1"); - test(_0_1i, "0 + i".to_string()); - test(_0_1i, "0+j".to_string()); - test(_0_1i, "-0 + j".to_string()); - test(_0_1i, "-0+i".to_string()); - test(_0_1i, "0 + 1i".to_string()); - test(_0_1i, "0+1j".to_string()); - test(_0_1i, "-0 + 1j".to_string()); - test(_0_1i, "-0+1i".to_string()); - test(_0_1i, "j + 0".to_string()); - test(_0_1i, "i".to_string()); - test(_0_1i, "j".to_string()); - test(_0_1i, "1j".to_string()); + test(_0_1i, "0 + i"); + test(_0_1i, "0+j"); + test(_0_1i, "-0 + j"); + test(_0_1i, "-0+i"); + test(_0_1i, "0 + 1i"); + test(_0_1i, "0+1j"); + test(_0_1i, "-0 + 1j"); + test(_0_1i, "-0+1i"); + test(_0_1i, "j + 0"); + test(_0_1i, "i"); + test(_0_1i, "j"); + test(_0_1i, "1j"); - test(_neg1_1i, "-1 + i".to_string()); - test(_neg1_1i, "-1+j".to_string()); - test(_neg1_1i, "-1 + 1j".to_string()); - test(_neg1_1i, "-1+1i".to_string()); - test(_neg1_1i, "1i-1".to_string()); - test(_neg1_1i, "j + -1".to_string()); + test(_neg1_1i, "-1 + i"); + test(_neg1_1i, "-1+j"); + test(_neg1_1i, "-1 + 1j"); + test(_neg1_1i, "-1+1i"); + test(_neg1_1i, "1i-1"); + test(_neg1_1i, "j + -1"); - test(_05_05i, "0.5 + 0.5i".to_string()); - test(_05_05i, "0.5+0.5j".to_string()); - test(_05_05i, "5e-1+0.5j".to_string()); - test(_05_05i, "5E-1 + 0.5j".to_string()); - test(_05_05i, "5E-1i + 0.5".to_string()); - test(_05_05i, "0.05e+1j + 50E-2".to_string()); + test(_05_05i, "0.5 + 0.5i"); + test(_05_05i, "0.5+0.5j"); + test(_05_05i, "5e-1+0.5j"); + test(_05_05i, "5E-1 + 0.5j"); + test(_05_05i, "5E-1i + 0.5"); + test(_05_05i, "0.05e+1j + 50E-2"); } #[test] From add0fbf0f0c2a37a881739a03b9529cc6128458c Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Sun, 16 Jul 2017 15:20:50 -0400 Subject: [PATCH 08/12] More informative error messages for complex::from_str --- complex/src/lib.rs | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index c34ccf3..8281b68 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -743,12 +743,12 @@ impl fmt::Binary for Complex where } impl FromStr for Complex where - T: FromStr + Num + PartialOrd + Clone + T: FromStr + Num + PartialOrd + Clone + std::fmt::Debug, T::Err: Error + PartialEq { - type Err = ParseComplexError; + 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> + fn from_str(s: &str) -> Result, ParseComplexError> { let imag = match s.rfind('j') { None => 'i', @@ -819,12 +819,12 @@ impl FromStr for Complex where } else if b.ends_with(imag) { re = a; im = b; } else { - return Err(ParseComplexError { kind: ComplexErrorKind::ParseError }); + return Err(ParseComplexError { kind: ComplexErrorKind::ExprError }); } // parse re let re = try!(T::from_str(re) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })); + .map_err(|e| ParseComplexError { kind: ComplexErrorKind::ParseError(e) })); // pop imaginary unit off let mut im = &im[..im.len()-1]; @@ -837,7 +837,7 @@ impl FromStr for Complex where // parse im let im = try!(T::from_str(im) - .map_err(|_| ParseComplexError { kind: ComplexErrorKind::ParseError })); + .map_err(|e| ParseComplexError { kind: ComplexErrorKind::ParseError(e) })); Ok(Complex::new(re, im)) } @@ -866,32 +866,44 @@ impl serde::Deserialize for Complex where } } -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct ParseComplexError { - kind: ComplexErrorKind, +#[derive(Debug, PartialEq)] +pub struct ParseComplexError where + T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +{ + kind: ComplexErrorKind, } -#[derive(Copy, Clone, Debug, PartialEq)] -enum ComplexErrorKind { - ParseError, +#[derive(Debug, PartialEq)] +enum ComplexErrorKind where + T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +{ + ParseError(T::Err), + ExprError } -impl Error for ParseComplexError { +impl Error for ParseComplexError where + T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +{ fn description(&self) -> &str { self.kind.description() } } -impl fmt::Display for ParseComplexError { +impl fmt::Display for ParseComplexError where + T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.description().fmt(f) } } -impl ComplexErrorKind { - fn description(&self) -> &'static str { +impl ComplexErrorKind where + T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +{ + fn description(&self) -> &str { match *self { - ComplexErrorKind::ParseError => "failed to parse complex number", + ComplexErrorKind::ParseError(ref e) => e.description(), + ComplexErrorKind::ExprError => "invalid or unsupported complex expression" } } } From 16a180f132b8d3854b4601c33e7e64f9945cc850 Mon Sep 17 00:00:00 2001 From: Alan Liddell Date: Wed, 19 Jul 2017 06:03:05 -0400 Subject: [PATCH 09/12] simplifies parse loop, allows leading '+', parametrizes Error on T::Err --- complex/src/lib.rs | 102 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index 8281b68..62ff99a 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -743,12 +743,12 @@ impl fmt::Binary for Complex where } impl FromStr for Complex where - T: FromStr + Num + PartialOrd + Clone + std::fmt::Debug, T::Err: Error + PartialEq + T: FromStr + Num + PartialOrd + Clone, T::Err: Error { - type Err = ParseComplexError; + 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> + fn from_str(s: &str) -> Result, ParseComplexError> { let imag = match s.rfind('j') { None => 'i', @@ -758,46 +758,38 @@ impl FromStr for Complex where let mut b = String::with_capacity(s.len()); let mut first = true; - let mut char_indices = s.char_indices(); + let char_indices = s.char_indices(); let mut pc = ' '; let mut split_index = s.len(); - loop { - match char_indices.next() { - Some(t) => { - let i = t.0; - let cc = t.1; - - if cc == '+' && pc != 'e' && pc != 'E' { - // ignore '+' if part of an exponent - if first { - split_index = i; - first = false; - } - // don't carry '+' over into b - pc = ' '; - continue; - } else if cc == '-' && pc != 'e' && pc != 'E' && i > 0 { - // ignore '-' if part of an exponent or begins the string - if first { - split_index = i; - first = false; - } - // DO carry '-' over into b - } - - if pc == '-' && cc == ' ' && !first { - // ignore whitespace between minus sign and next number - continue; - } - - if !first { - b.push(cc); - } - pc = cc; - }, - None => break, + for (i, cc) in char_indices { + if cc == '+' && pc != 'e' && pc != 'E' && i > 0 { + // ignore '+' if part of an exponent + if first { + split_index = i; + first = false; + } + // don't carry '+' over into b + pc = ' '; + continue; + } else if cc == '-' && pc != 'e' && pc != 'E' && i > 0 { + // ignore '-' if part of an exponent or begins the string + if first { + split_index = i; + first = false; + } + // DO carry '-' over into b } + + if pc == '-' && cc == ' ' && !first { + // ignore whitespace between minus sign and next number + continue; + } + + if !first { + b.push(cc); + } + pc = cc; } // split off real and imaginary parts, trim whitespace @@ -833,6 +825,8 @@ impl FromStr for Complex where im = "1"; } else if im == "-" { im = "-1"; + } else if im == "+" { + im = "1"; } // parse im @@ -867,38 +861,38 @@ impl serde::Deserialize for Complex where } #[derive(Debug, PartialEq)] -pub struct ParseComplexError where - T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +pub struct ParseComplexError where + E: Error { - kind: ComplexErrorKind, + kind: ComplexErrorKind, } #[derive(Debug, PartialEq)] -enum ComplexErrorKind where - T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +enum ComplexErrorKind where + E: Error { - ParseError(T::Err), + ParseError(E), ExprError } -impl Error for ParseComplexError where - T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +impl Error for ParseComplexError where + E: Error { fn description(&self) -> &str { self.kind.description() } } -impl fmt::Display for ParseComplexError where - T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +impl fmt::Display for ParseComplexError where + E: Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.description().fmt(f) } } -impl ComplexErrorKind where - T: FromStr + std::fmt::Debug + PartialEq, T::Err: Error + PartialEq +impl ComplexErrorKind where + E: Error { fn description(&self) -> &str { match *self { @@ -1560,12 +1554,13 @@ mod test { test(_0_0i, "-0"); test(_0_0i, "0i"); test(_0_0i, "0j"); + test(_0_0i, "+0j"); test(_0_0i, "-0i"); test(_1_0i, "1 + 0i"); test(_1_0i, "1+0j"); test(_1_0i, "1 - 0j"); - test(_1_0i, "1-0i"); + test(_1_0i, "+1-0i"); test(_1_0i, "-0j+1"); test(_1_0i, "1"); @@ -1575,7 +1570,7 @@ mod test { test(_1_1i, "1+1i"); test(_1_1i, "i + 1"); test(_1_1i, "1i+1"); - test(_1_1i, "j+1"); + test(_1_1i, "+j+1"); test(_0_1i, "0 + i"); test(_0_1i, "0+j"); @@ -1618,5 +1613,6 @@ mod test { test("314e-2ij"); test("4.3j - i"); test("1i - 2i"); + test("+ 1 - 3.0i"); } } From 79c7a4be5f774ea013451f36e8244f943f3194a3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Jul 2017 12:47:07 -0700 Subject: [PATCH 10/12] complex: relax the FromStr trait requirements even more --- complex/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index 62ff99a..a4e2887 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -743,7 +743,7 @@ impl fmt::Binary for Complex where } impl FromStr for Complex where - T: FromStr + Num + PartialOrd + Clone, T::Err: Error + T: FromStr + Num + Clone { type Err = ParseComplexError; @@ -861,15 +861,13 @@ impl serde::Deserialize for Complex where } #[derive(Debug, PartialEq)] -pub struct ParseComplexError where - E: Error +pub struct ParseComplexError { kind: ComplexErrorKind, } #[derive(Debug, PartialEq)] -enum ComplexErrorKind where - E: Error +enum ComplexErrorKind { ParseError(E), ExprError From bd22a89a32edef116d2026c2830c827ec6c7c08d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Jul 2017 12:49:03 -0700 Subject: [PATCH 11/12] complex: Simplify the from_str signature --- complex/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index a4e2887..bb30c8c 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -748,7 +748,7 @@ impl FromStr for Complex where 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> + fn from_str(s: &str) -> Result { let imag = match s.rfind('j') { None => 'i', From ee6bbdb2f64199df55dd3bf3aa9fdf965000ccaa Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Jul 2017 15:16:34 -0700 Subject: [PATCH 12/12] complex: refactor ParseComplexError a little --- complex/src/lib.rs | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/complex/src/lib.rs b/complex/src/lib.rs index bb30c8c..692311d 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -811,27 +811,23 @@ impl FromStr for Complex where } else if b.ends_with(imag) { re = a; im = b; } else { - return Err(ParseComplexError { kind: ComplexErrorKind::ExprError }); + return Err(ParseComplexError::new()); } // parse re - let re = try!(T::from_str(re) - .map_err(|e| ParseComplexError { kind: ComplexErrorKind::ParseError(e) })); + let re = try!(T::from_str(re).map_err(ParseComplexError::from_error)); // pop imaginary unit off let mut im = &im[..im.len()-1]; // handle im == "i" or im == "-i" - if im.is_empty() { + if im.is_empty() || im == "+" { im = "1"; } else if im == "-" { im = "-1"; - } else if im == "+" { - im = "1"; } // parse im - let im = try!(T::from_str(im) - .map_err(|e| ParseComplexError { kind: ComplexErrorKind::ParseError(e) })); + let im = try!(T::from_str(im).map_err(ParseComplexError::from_error)); Ok(Complex::new(re, im)) } @@ -873,33 +869,38 @@ enum ComplexErrorKind ExprError } -impl Error for ParseComplexError where - E: Error +impl ParseComplexError { - fn description(&self) -> &str { - self.kind.description() - } + fn new() -> Self { + ParseComplexError { + kind: ComplexErrorKind::ExprError, + } + } + + fn from_error(error: E) -> Self { + ParseComplexError { + kind: ComplexErrorKind::ParseError(error), + } + } } -impl fmt::Display for ParseComplexError where - E: Error -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.description().fmt(f) - } -} - -impl ComplexErrorKind where - E: Error +impl Error for ParseComplexError { fn description(&self) -> &str { - match *self { + match self.kind { ComplexErrorKind::ParseError(ref e) => e.description(), ComplexErrorKind::ExprError => "invalid or unsupported complex expression" } } } +impl fmt::Display for ParseComplexError +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + #[cfg(test)] fn hash(x: &T) -> u64 { use std::hash::{BuildHasher, Hasher};