Skip to content

Commit 39c1de8

Browse files
committed
Auto merge of #137 - liebharc:master, r=cuviper
Added powc, powf, log and expf methods for complex numbers I would like to have a few functions added for complex numbers (powf, log and exp function with arbitrary base). I've provided an implementation with this commit. However it requires more work and discussion and I've added comments to point out the parts which I'm especially unhappy with. Would be nice to get some feedback so that we can improve this pull request first.
2 parents 0861fb4 + 24fcc15 commit 39c1de8

File tree

1 file changed

+99
-3
lines changed

1 file changed

+99
-3
lines changed

complex/src/lib.rs

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ impl<T: Clone + Float> Complex<T> {
120120
#[inline]
121121
pub fn exp(&self) -> Complex<T> {
122122
// formula: e^(a + bi) = e^a (cos(b) + i*sin(b))
123-
Complex::new(self.im.cos(), self.im.sin()).scale(self.re.exp())
123+
// = from_polar(e^a, b)
124+
Complex::from_polar(&self.re.exp(), &self.im)
124125
}
125126

126127
/// Computes the principal value of natural logarithm of `self`.
@@ -133,7 +134,8 @@ impl<T: Clone + Float> Complex<T> {
133134
#[inline]
134135
pub fn ln(&self) -> Complex<T> {
135136
// formula: ln(z) = ln|z| + i*arg(z)
136-
Complex::new(self.norm().ln(), self.arg())
137+
let (r, theta) = self.to_polar();
138+
Complex::new(r.ln(), theta)
137139
}
138140

139141
/// Computes the principal value of the square root of `self`.
@@ -150,6 +152,53 @@ impl<T: Clone + Float> Complex<T> {
150152
let (r, theta) = self.to_polar();
151153
Complex::from_polar(&(r.sqrt()), &(theta/two))
152154
}
155+
156+
/// Raises `self` to a floating point power.
157+
#[inline]
158+
pub fn powf(&self, exp: T) -> Complex<T> {
159+
// formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y)
160+
// = from_polar(ρ^y, θ y)
161+
let (r, theta) = self.to_polar();
162+
Complex::from_polar(&r.powf(exp), &(theta*exp))
163+
}
164+
165+
/// Returns the logarithm of `self` with respect to an arbitrary base.
166+
#[inline]
167+
pub fn log(&self, base: T) -> Complex<T> {
168+
// formula: log_y(x) = log_y(ρ e^(i θ))
169+
// = log_y(ρ) + log_y(e^(i θ)) = log_y(ρ) + ln(e^(i θ)) / ln(y)
170+
// = log_y(ρ) + i θ / ln(y)
171+
let (r, theta) = self.to_polar();
172+
Complex::new(r.log(base), theta / base.ln())
173+
}
174+
175+
/// Raises `self` to a complex power.
176+
#[inline]
177+
pub fn powc(&self, exp: Complex<T>) -> Complex<T> {
178+
// formula: x^y = (a + i b)^(c + i d)
179+
// = (ρ e^(i θ))^c (ρ e^(i θ))^(i d)
180+
// where ρ=|x| and θ=arg(x)
181+
// = ρ^c e^(−d θ) e^(i c θ) ρ^(i d)
182+
// = p^c e^(−d θ) (cos(c θ)
183+
// + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ)))
184+
// = p^c e^(−d θ) (
185+
// cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ))
186+
// + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ))))
187+
// = p^c e^(−d θ) (cos(c θ + d ln(ρ)) + i sin(c θ + d ln(ρ)))
188+
// = from_polar(p^c e^(−d θ), c θ + d ln(ρ))
189+
let (r, theta) = self.to_polar();
190+
Complex::from_polar(
191+
&(r.powf(exp.re) * (-exp.im * theta).exp()),
192+
&(exp.re * theta + exp.im * r.ln()))
193+
}
194+
195+
/// Raises a floating point number to the complex power `self`.
196+
#[inline]
197+
pub fn expf(&self, base: T) -> Complex<T> {
198+
// formula: x^(a+bi) = x^a x^bi = x^a e^(b ln(x) i)
199+
// = from_polar(x^a, b ln(x))
200+
Complex::from_polar(&base.powf(self.re), &(self.im * base.ln()))
201+
}
153202

154203
/// Computes the sine of `self`.
155204
#[inline]
@@ -716,8 +765,12 @@ mod test {
716765
}
717766

718767
fn close(a: Complex64, b: Complex64) -> bool {
768+
close_to_tol(a, b, 1e-10)
769+
}
770+
771+
fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool {
719772
// returns true if a and b are reasonably close
720-
(a == b) || (a-b).norm() < 1e-10
773+
(a == b) || (a-b).norm() < tol
721774
}
722775

723776
#[test]
@@ -748,6 +801,49 @@ mod test {
748801
assert!(-f64::consts::PI <= c.ln().arg() && c.ln().arg() <= f64::consts::PI);
749802
}
750803
}
804+
805+
#[test]
806+
fn test_powc()
807+
{
808+
let a = Complex::new(2.0, -3.0);
809+
let b = Complex::new(3.0, 0.0);
810+
assert!(close(a.powc(b), a.powf(b.re)));
811+
assert!(close(b.powc(a), a.expf(b.re)));
812+
let c = Complex::new(1.0 / 3.0, 0.1);
813+
assert!(close_to_tol(a.powc(c), Complex::new(1.65826, -0.33502), 1e-5));
814+
}
815+
816+
#[test]
817+
fn test_powf()
818+
{
819+
let c = Complex::new(2.0, -1.0);
820+
let r = c.powf(3.5);
821+
assert!(close_to_tol(r, Complex::new(-0.8684746, -16.695934), 1e-5));
822+
}
823+
824+
#[test]
825+
fn test_log()
826+
{
827+
let c = Complex::new(2.0, -1.0);
828+
let r = c.log(10.0);
829+
assert!(close_to_tol(r, Complex::new(0.349485, -0.20135958), 1e-5));
830+
}
831+
832+
#[test]
833+
fn test_some_expf_cases()
834+
{
835+
let c = Complex::new(2.0, -1.0);
836+
let r = c.expf(10.0);
837+
assert!(close_to_tol(r, Complex::new(-66.82015, -74.39803), 1e-5));
838+
839+
let c = Complex::new(5.0, -2.0);
840+
let r = c.expf(3.4);
841+
assert!(close_to_tol(r, Complex::new(-349.25, -290.63), 1e-2));
842+
843+
let c = Complex::new(-1.5, 2.0 / 3.0);
844+
let r = c.expf(1.0 / 3.0);
845+
assert!(close_to_tol(r, Complex::new(3.8637, -3.4745), 1e-2));
846+
}
751847

752848
#[test]
753849
fn test_sqrt() {

0 commit comments

Comments
 (0)