From 94adcd072c9ae0db1d0fc3a13bb122ee969a178f Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 26 Oct 2025 22:51:59 +0100 Subject: [PATCH 1/2] add `.cis()` methods for Float and Complex --- src/cis.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 2 files changed, 61 insertions(+) create mode 100644 src/cis.rs diff --git a/src/cis.rs b/src/cis.rs new file mode 100644 index 0000000..d4ab05b --- /dev/null +++ b/src/cis.rs @@ -0,0 +1,59 @@ +//! 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() { + let theta = 1.2345; + let expected = Complex::new(0.3299931576785677, 0.9439833239445111); + assert!((expected - theta.cis()).norm_sqr() < 1e-10); + } + + #[test] + fn cis_complex() { + 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"))] From 75272752bee10986f5af0e94782f31207a375f21 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 26 Oct 2025 23:02:11 +0100 Subject: [PATCH 2/2] tests: credit wolframalpha --- src/cis.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cis.rs b/src/cis.rs index d4ab05b..108d75d 100644 --- a/src/cis.rs +++ b/src/cis.rs @@ -44,6 +44,9 @@ mod imp { #[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); @@ -51,6 +54,9 @@ mod imp { #[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);