diff --git a/src/cis.rs b/src/cis.rs new file mode 100644 index 0000000..108d75d --- /dev/null +++ b/src/cis.rs @@ -0,0 +1,65 @@ +//! Trait-based `cis` for both real and complex inputs. +//! +//! This provides a `cis()` method for both real floats and `Complex` so callers can +//! uniformly write `x.cis()` whether `x` is a `T` (float) or a `Complex`. + +/// Compute cis(x) = exp(i*x). Implemented for both real floats and Complex. +/// +/// - For real `T`, `x.cis()` returns `Complex::new(x.cos(), x.sin())`. +/// - For `Complex`, `z.cis()` returns `exp(i*z) = exp(-Im(z)) * cis(Re(z))`. +pub trait Cis { + type Output; + fn cis(self) -> Self::Output; +} + +#[cfg(any(feature = "std", feature = "libm"))] +mod imp { + use super::Cis; + use crate::Complex; + use num_traits::Float; + + impl Cis for T { + type Output = Complex; + + fn cis(self) -> Complex { + // cis(x) = cos(x) + i*sin(x) + Complex::new(self.cos(), self.sin()) + } + } + + impl Cis for Complex { + type Output = Complex; + + fn cis(self) -> Complex { + // cis(a+ib) = exp(-b) * cis(a) + let scale = (-self.im).exp(); + self.re.cis() * scale + } + } + + #[cfg(test)] + mod tests { + use crate::Cis; + use crate::Complex; + + #[test] + fn cis_real() { + // Compare a hard-coded example to a reference result via WolframAlpha: + // https://www.wolframalpha.com/input?i=cis%281.2345%29 + + let theta = 1.2345; + let expected = Complex::new(0.3299931576785677, 0.9439833239445111); + assert!((expected - theta.cis()).norm_sqr() < 1e-10); + } + + #[test] + fn cis_complex() { + // Compare a hard-coded example to a reference result via WolframAlpha: + // https://www.wolframalpha.com/input?i=cis%28-6.789%2Bi1.2345%29 + + let z = Complex::new(-6.7890, 1.2345); + let expected = Complex::new(0.254543682056692, -0.1409858152159941); + assert!((expected - z.cis()).norm_sqr() < 1e-10); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 661b67b..21966a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ use num_traits::float::FloatCore; use num_traits::float::{Float, FloatConst}; mod cast; +mod cis; +pub use cis::Cis; mod pow; #[cfg(any(feature = "std", feature = "libm"))]