From 98b9df622fc6b5894fc3f2cdeb658119632013d9 Mon Sep 17 00:00:00 2001 From: William Rieger Date: Fri, 4 Sep 2015 02:08:31 -0400 Subject: [PATCH] Add mathematical functions for complex numbers. For Complex, the following functions along with corresponding tests were added: - exp - sin, cos, tan - sinh, cosh, tanh --- src/complex.rs | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/src/complex.rs b/src/complex.rs index ab5e86f..79b1cd9 100644 --- a/src/complex.rs +++ b/src/complex.rs @@ -81,6 +81,76 @@ impl Complex { pub fn norm(&self) -> T { self.re.clone().hypot(self.im.clone()) } + + /// Computes e^(self), where e is the base of the natural logarithm. + #[inline] + pub fn exp(&self) -> Complex { + // formula: e^(a + bi) = e^a * (cos(b) + isin(b)) + let exp = self.re.exp(); + Complex::new(exp * self.im.cos(), exp * self.im.sin()) + } + + /// Computes the sine of self. + #[inline] + pub fn sin(&self) -> Complex { + // formula: sin(z) = (e^(iz) - e^(-iz)) / 2i + //let one = One::one(); + let i = Complex::new(Zero::zero(), One::one()); + let two_i = i + i; + let e_iz = (self*i).exp(); + let e_neg_iz = e_iz.inv(); + (e_iz - e_neg_iz) / two_i + } + + /// Computes the cosine of self. + #[inline] + pub fn cos(&self) -> Complex { + // formula: cos(z) = (e^(iz) + e^(-iz)) / 2 + let i = Complex::new(Zero::zero(), One::one()); + let two = Complex::one() + Complex::one(); + let e_iz = (self*i).exp(); + let e_neg_iz = e_iz.inv(); + (e_iz + e_neg_iz) / two + } + + /// Computes the tangent of self. + #[inline] + pub fn tan(&self) -> Complex { + // formula: tan(z) = i (e^(-iz) - e^(iz)) / (e^(-iz) + e^(iz)) + let i = Complex::new(Zero::zero(), One::one()); + let e_iz = (self*i).exp(); + let e_neg_iz = e_iz.inv(); + i * (e_neg_iz - e_iz) / (e_neg_iz + e_iz) + } + + /// Computes the hyperbolic sine of self. + #[inline] + pub fn sinh(&self) -> Complex { + // formula: sinh(z) = (e^(z) - e^(-z)) / 2 + let two = Complex::one() + Complex::one(); + let e_z = self.exp(); + let e_neg_z = e_z.inv(); + (e_z - e_neg_z) / two + } + + /// Computes the hyperbolic cosine of self. + #[inline] + pub fn cosh(&self) -> Complex { + // formula: sinh(z) = (e^(z) + e^(-z)) / 2 + let two = Complex::one() + Complex::one(); + let e_z = self.exp(); + let e_neg_z = e_z.inv(); + (e_z + e_neg_z) / two + } + + /// Computes the hyperbolic tangent of self. + #[inline] + pub fn tanh(&self) -> Complex { + // formula: tanh(z) = (e^(z) - e^(-z)) / (e^(z) + e^(-z)) + let e_z = self.exp(); + let e_neg_z = e_z.inv(); + (e_z - e_neg_z) / (e_z + e_neg_z) + } } impl Complex { @@ -360,6 +430,90 @@ mod test { for &c in all_consts.iter() { test(c); } } + fn very_close(a: Complex64, b: Complex64) -> bool { + // returns true if a and b are reasonably close + (a-b).norm() < 1e-10 + } + + #[test] + fn test_exp() { + assert_eq!(_1_0i.exp(), Complex::new(f64::consts::E, 0.0)); + assert_eq!(_0_0i.exp(), _1_0i); + assert_eq!(_0_1i.exp(), Complex::new(1.0.cos(), 1.0.sin())); + assert!(very_close(_05_05i.exp()*_05_05i.exp(), _1_1i.exp())); + assert!(very_close(Complex::new(0.0, -f64::consts::PI).exp(), _1_0i.scale(-1.0))); + for &c in all_consts.iter() { + assert!(very_close(c.exp(), (c + Complex::new(0.0, f64::consts::PI*2.0)).exp())); + } + } + + #[test] + fn test_sin() { + assert_eq!(_0_0i.sin(), _0_0i); + assert!(very_close(_1_0i.scale(f64::consts::PI*2.0).sin(), _0_0i)); + assert_eq!(_0_1i.sin(), _0_1i.scale(1.0.sinh())); + for &c in all_consts.iter() { + assert!(very_close(c.conj().sin(), c.sin().conj())); + assert!(very_close(c.scale(-1.0).sin(), c.sin().scale(-1.0))); + } + } + + #[test] + fn test_cos() { + assert_eq!(_0_0i.cos(), _1_0i); + assert!(very_close(_1_0i.scale(f64::consts::PI*2.0).cos(), _1_0i)); + assert_eq!(_0_1i.cos(), _1_0i.scale(1.0.cosh())); + for &c in all_consts.iter() { + assert!(very_close(c.conj().cos(), c.cos().conj())); + assert!(very_close(c.scale(-1.0).cos(), c.cos())); + } + } + + #[test] + fn test_tan() { + assert_eq!(_0_0i.tan(), _0_0i); + assert!(very_close(_1_0i.scale(f64::consts::PI).tan(), _0_0i)); + for &c in all_consts.iter() { + assert!(very_close(c.conj().tan(), c.tan().conj())); + assert!(very_close(c.scale(-1.0).tan(), c.tan().scale(-1.0))); + assert!(very_close(c.tan(), c.sin()/c.cos())); + } + } + + #[test] + fn test_sinh() { + assert_eq!(_0_0i.sinh(), _0_0i); + assert_eq!(_1_0i.sinh(), _1_0i.scale((f64::consts::E - 1.0/f64::consts::E)/2.0)); + assert_eq!(_0_1i.sinh(), _0_1i.scale(1.0.sin())); + for &c in all_consts.iter() { + assert!(very_close(c.conj().sinh(), c.sinh().conj())); + assert!(very_close(c.scale(-1.0).sinh(), c.sinh().scale(-1.0))); + } + } + + #[test] + fn test_cosh() { + assert_eq!(_0_0i.cosh(), _1_0i); + assert_eq!(_1_0i.cosh(), _1_0i.scale((f64::consts::E + 1.0/f64::consts::E)/2.0)); + assert_eq!(_0_1i.cosh(), _1_0i.scale(1.0.cos())); + for &c in all_consts.iter() { + assert!(very_close(c.conj().cosh(), c.cosh().conj())); + assert!(very_close(c.scale(-1.0).cosh(), c.cosh())); + } + } + + #[test] + fn test_tanh() { + assert_eq!(_0_0i.tanh(), _0_0i); + assert!(very_close(_1_0i.tanh(), _1_0i.scale((f64::consts::E.powi(2) - 1.0)/(f64::consts::E.powi(2) + 1.0)))); + assert!(very_close(_0_1i.tanh(), _0_1i.scale(1.0.tan()))); + for &c in all_consts.iter() { + assert!(very_close(c.conj().tanh(), c.conj().tanh())); + assert!(very_close(c.scale(-1.0).tanh(), c.tanh().scale(-1.0))); + assert!(very_close(c.tanh(), c.sinh()/c.cosh())); + } + } + mod arith { use super::{_0_0i, _1_0i, _1_1i, _0_1i, _neg1_1i, _05_05i, all_consts}; use Zero;