diff --git a/complex/src/lib.rs b/complex/src/lib.rs index 334f8e0..110febb 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -26,7 +26,7 @@ use std::error::Error; use std::fmt; #[cfg(test)] use std::hash; -use std::ops::{Add, Div, Mul, Neg, Sub}; +use std::ops::{Add, Div, Mul, Neg, Sub, Rem}; use std::str::FromStr; use traits::{Zero, One, Num, Float}; @@ -261,8 +261,8 @@ impl Complex { #[inline] pub fn asin(&self) -> Complex { // formula: arcsin(z) = -i ln(sqrt(1-z^2) + iz) - let i = Complex::i(); - -i*((Complex::one() - self*self).sqrt() + i*self).ln() + let i = Complex::::i(); + -i*((Complex::::one() - self*self).sqrt() + i*self).ln() } /// Computes the principal value of the inverse cosine of `self`. @@ -276,8 +276,8 @@ impl Complex { #[inline] pub fn acos(&self) -> Complex { // formula: arccos(z) = -i ln(i sqrt(1-z^2) + z) - let i = Complex::i(); - -i*(i*(Complex::one() - self*self).sqrt() + self).ln() + let i = Complex::::i(); + -i*(i*(Complex::::one() - self*self).sqrt() + self).ln() } /// Computes the principal value of the inverse tangent of `self`. @@ -291,8 +291,8 @@ impl Complex { #[inline] pub fn atan(&self) -> Complex { // formula: arctan(z) = (ln(1+iz) - ln(1-iz))/(2i) - let i = Complex::i(); - let one = Complex::one(); + let i = Complex::::i(); + let one = Complex::::one(); let two = one + one; if *self == i { return Complex::new(T::zero(), T::infinity()); @@ -336,7 +336,7 @@ impl Complex { #[inline] pub fn asinh(&self) -> Complex { // formula: arcsinh(z) = ln(z + sqrt(1+z^2)) - let one = Complex::one(); + let one = Complex::::one(); (self + (one + self * self).sqrt()).ln() } @@ -417,8 +417,8 @@ impl<'a, T: Clone + Num> From<&'a T> for Complex { } macro_rules! forward_ref_ref_binop { - (impl $imp:ident, $method:ident) => { - impl<'a, 'b, T: Clone + Num> $imp<&'b Complex> for &'a Complex { + (impl $imp:ident, $method:ident, $($dep:ident),*) => { + impl<'a, 'b, T: Clone + Num $(+ $dep)*> $imp<&'b Complex> for &'a Complex { type Output = Complex; #[inline] @@ -430,8 +430,8 @@ macro_rules! forward_ref_ref_binop { } macro_rules! forward_ref_val_binop { - (impl $imp:ident, $method:ident) => { - impl<'a, T: Clone + Num> $imp> for &'a Complex { + (impl $imp:ident, $method:ident, $($dep:ident),*) => { + impl<'a, T: Clone + Num $(+ $dep)*> $imp> for &'a Complex { type Output = Complex; #[inline] @@ -443,8 +443,8 @@ macro_rules! forward_ref_val_binop { } macro_rules! forward_val_ref_binop { - (impl $imp:ident, $method:ident) => { - impl<'a, T: Clone + Num> $imp<&'a Complex> for Complex { + (impl $imp:ident, $method:ident, $($dep:ident),*) => { + impl<'a, T: Clone + Num $(+ $dep)*> $imp<&'a Complex> for Complex { type Output = Complex; #[inline] @@ -456,15 +456,15 @@ macro_rules! forward_val_ref_binop { } macro_rules! forward_all_binop { - (impl $imp:ident, $method:ident) => { - forward_ref_ref_binop!(impl $imp, $method); - forward_ref_val_binop!(impl $imp, $method); - forward_val_ref_binop!(impl $imp, $method); + (impl $imp:ident, $method:ident, $($dep:ident),*) => { + forward_ref_ref_binop!(impl $imp, $method, $($dep),*); + forward_ref_val_binop!(impl $imp, $method, $($dep),*); + forward_val_ref_binop!(impl $imp, $method, $($dep),*); }; } /* arithmetic */ -forward_all_binop!(impl Add, add); +forward_all_binop!(impl Add, add, ); // (a + i b) + (c + i d) == (a + c) + i (b + d) impl Add> for Complex { @@ -476,7 +476,7 @@ impl Add> for Complex { } } -forward_all_binop!(impl Sub, sub); +forward_all_binop!(impl Sub, sub, ); // (a + i b) - (c + i d) == (a - c) + i (b - d) impl Sub> for Complex { @@ -488,7 +488,7 @@ impl Sub> for Complex { } } -forward_all_binop!(impl Mul, mul); +forward_all_binop!(impl Mul, mul, ); // (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c) impl Mul> for Complex { @@ -502,7 +502,7 @@ impl Mul> for Complex { } } -forward_all_binop!(impl Div, div); +forward_all_binop!(impl Div, div, ); // (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d) // == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)] @@ -518,10 +518,50 @@ impl Div> for Complex { } } +forward_all_binop!(impl Rem, rem, PartialOrd); + +// Attempts to identify the gaussian integer whose product with `modulus` +// is closest to `self`. +impl Rem> for Complex where { + type Output = Complex; + + #[inline] + fn rem(self, modulus: Complex) -> Self { + let Complex { re, im } = self.clone() / modulus.clone(); + // This is the gaussian integer corresponding to the true ratio + // rounded towards zero. + let (re0, im0) = (re.clone() - re % T::one(), im.clone() - im % T::one()); + + let zero = T::zero(); + let one = T::one(); + let neg = zero.clone() - one.clone(); + // Traverse the 3x3 square of gaussian integers surrounding our + // current approximation, and select the one whose product with + // `modulus` is closest to `self`. + let mut bestrem = self.clone() - modulus.clone() * + Complex::new(re0.clone(), im0.clone()); + let mut bestnorm = bestrem.norm_sqr(); + for &(dr, di) in + vec![(&one, &zero), (&one, &one), (&zero, &one), (&neg, &one), + (&neg, &zero), (&neg, &neg), (&zero, &neg), (&one, &neg)] + .iter() { + let newrem = self.clone() - modulus.clone() * + Complex::new(re0.clone() + dr.clone(), + im0.clone() + di.clone()); + let newnorm = newrem.norm_sqr(); + if newnorm < bestnorm { + bestrem = newrem; + bestnorm = newnorm; + } + } + bestrem + } +} + // Op Assign mod opassign { - use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign}; + use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; use traits::NumAssign; @@ -553,6 +593,12 @@ mod opassign { } } + impl RemAssign for Complex { + fn rem_assign(&mut self, other: Complex) { + *self = self.clone() % other; + } + } + impl AddAssign for Complex { fn add_assign(&mut self, other: T) { self.re += other; @@ -579,6 +625,12 @@ mod opassign { } } + impl RemAssign for Complex { + fn rem_assign(&mut self, other: T) { + *self = self.clone() % other; + } + } + macro_rules! forward_op_assign { (impl $imp:ident, $method:ident) => { impl<'a, T: Clone + NumAssign> $imp<&'a Complex> for Complex { @@ -600,6 +652,19 @@ mod opassign { forward_op_assign!(impl SubAssign, sub_assign); forward_op_assign!(impl MulAssign, mul_assign); forward_op_assign!(impl DivAssign, div_assign); + + impl<'a, T: Clone + NumAssign + PartialOrd> RemAssign<&'a Complex> for Complex { + #[inline] + fn rem_assign(&mut self, other: &Complex) { + self.rem_assign(other.clone()) + } + } + impl<'a, T: Clone + NumAssign + PartialOrd> RemAssign<&'a T> for Complex { + #[inline] + fn rem_assign(&mut self, other: &T) { + self.rem_assign(other.clone()) + } + } } impl> Neg for Complex { @@ -621,8 +686,8 @@ impl<'a, T: Clone + Num + Neg> Neg for &'a Complex { } macro_rules! real_arithmetic { - (@forward $imp:ident::$method:ident for $($real:ident),*) => ( - impl<'a, T: Clone + Num> $imp<&'a T> for Complex { + (@forward $imp:ident::$method:ident for $($real:ident),*: $($dep:ident),*) => ( + impl<'a, T: Clone + Num $(+ $dep)*> $imp<&'a T> for Complex { type Output = Complex; #[inline] @@ -630,7 +695,7 @@ macro_rules! real_arithmetic { self.$method(other.clone()) } } - impl<'a, T: Clone + Num> $imp for &'a Complex { + impl<'a, T: Clone + Num $(+ $dep)*> $imp for &'a Complex { type Output = Complex; #[inline] @@ -638,7 +703,7 @@ macro_rules! real_arithmetic { self.clone().$method(other) } } - impl<'a, 'b, T: Clone + Num> $imp<&'a T> for &'b Complex { + impl<'a, 'b, T: Clone + Num $(+ $dep)*> $imp<&'a T> for &'b Complex { type Output = Complex; #[inline] @@ -674,10 +739,11 @@ macro_rules! real_arithmetic { )* ); ($($real:ident),*) => ( - real_arithmetic!(@forward Add::add for $($real),*); - real_arithmetic!(@forward Sub::sub for $($real),*); - real_arithmetic!(@forward Mul::mul for $($real),*); - real_arithmetic!(@forward Div::div for $($real),*); + real_arithmetic!(@forward Add::add for $($real),*: ); + real_arithmetic!(@forward Sub::sub for $($real),*: ); + real_arithmetic!(@forward Mul::mul for $($real),*: ); + real_arithmetic!(@forward Div::div for $($real),*: ); + real_arithmetic!(@forward Rem::rem for $($real),*: PartialOrd); $( impl Add> for $real { @@ -718,6 +784,15 @@ macro_rules! real_arithmetic { $real::zero() - self * other.im / norm_sqr) } } + + impl Rem> for $real { + type Output = Complex<$real>; + + #[inline] + fn rem(self, other: Complex<$real>) -> Complex<$real> { + Complex::new(self, Self::zero()) % other + } + } )* ); } @@ -758,6 +833,15 @@ impl Div for Complex { } } +impl Rem for Complex { + type Output = Complex; + + #[inline] + fn rem(self, other: T) -> Complex { + self % Complex::new(other, T::zero()) + } +} + real_arithmetic!(usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64); /* constants */ @@ -970,6 +1054,96 @@ impl FromStr for Complex where } } +impl Num for Complex where +{ + type FromStrRadixErr = ParseComplexError; + + /// Parses `a +/- bi`; `ai +/- b`; `a`; or `bi` where `a` and `b` are of type `T` + fn from_str_radix(s: &str, radix: u32) -> Result + { + let imag = match s.rfind('j') { + None => 'i', + _ => 'j' + }; + + let mut b = String::with_capacity(s.len()); + let mut first = true; + + let char_indices = s.char_indices(); + let mut pc = ' '; + let mut split_index = s.len(); + + 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 + 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.ends_with(imag) { + false => "0i", + true => "0" + }; + } + + 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::new()); + } + + // parse re + let re = try!(T::from_str_radix(re, radix).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() || im == "+" { + im = "1"; + } else if im == "-" { + im = "-1"; + } + + // parse im + let im = try!(T::from_str_radix(im, radix).map_err(ParseComplexError::from_error)); + + Ok(Complex::new(re, im)) + } +} + #[cfg(feature = "serde")] impl serde::Serialize for Complex where T: serde::Serialize @@ -1512,6 +1686,10 @@ mod test { assert_eq!($a / $b, $answer); assert_eq!({ let mut x = $a; x /= $b; x}, $answer); }; + ($a:ident % $b:expr, $answer:expr) => { + assert_eq!($a % $b, $answer); + assert_eq!({ let mut x = $a; x %= $b; x}, $answer); + } } // Test both a + b and a + &b @@ -1523,7 +1701,7 @@ mod test { } mod complex_arithmetic { - use super::{_0_0i, _1_0i, _1_1i, _0_1i, _neg1_1i, _05_05i, all_consts}; + use super::{_0_0i, _1_0i, _1_1i, _0_1i, _neg1_1i, _05_05i, _4_2i, all_consts}; use traits::Zero; #[test] @@ -1575,6 +1753,16 @@ mod test { } } + #[test] + fn test_rem() { + test_op!(_neg1_1i % _0_1i, _0_0i); + test_op!(_4_2i % _0_1i, _0_0i); + test_op!(_05_05i % _0_1i, _05_05i); + test_op!(_05_05i % _1_1i, _05_05i); + assert_eq!((_4_2i + _05_05i) % _0_1i, _05_05i); + assert_eq!((_4_2i + _05_05i) % _1_1i, _05_05i); + } + #[test] fn test_neg() { assert_eq!(-_1_0i + _0_1i, _neg1_1i); @@ -1612,6 +1800,13 @@ mod test { assert_eq!(_4_2i / 0.5, Complex::new(8.0, 4.0)); assert_eq!(0.5 / _4_2i, Complex::new(0.1, -0.05)); } + + #[test] + fn test_rem() { + assert_eq!(_4_2i % 2.0, Complex::new(0.0, 0.0)); + assert_eq!(_4_2i % 3.0, Complex::new(1.0, -1.0)); + assert_eq!(3.0 % _4_2i, Complex::new(-1.0, -2.0)); + } } #[test]