Skip to content

Commit 2e11d2a

Browse files
bors[bot]cuviper
andcommitted
Merge #56
56: Add Complex::powi and assorted Pow impls r=cuviper a=cuviper Closes #18. Co-authored-by: Josh Stone <[email protected]>
2 parents 61a7f5b + c3173f7 commit 2e11d2a

File tree

2 files changed

+232
-6
lines changed

2 files changed

+232
-6
lines changed

src/lib.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ use core::str::FromStr;
3838
#[cfg(feature = "std")]
3939
use std::error::Error;
4040

41-
use traits::{Inv, MulAdd, Num, One, Signed, Zero};
41+
use traits::{Inv, MulAdd, Num, One, Pow, Signed, Zero};
4242

4343
#[cfg(feature = "std")]
4444
use traits::float::Float;
4545
use traits::float::FloatCore;
4646

4747
mod cast;
48+
mod pow;
49+
4850
#[cfg(feature = "rand")]
4951
mod crand;
5052
#[cfg(feature = "rand")]
@@ -121,6 +123,12 @@ impl<T: Clone + Num> Complex<T> {
121123
pub fn unscale(&self, t: T) -> Self {
122124
Self::new(self.re.clone() / t.clone(), self.im.clone() / t)
123125
}
126+
127+
/// Raises `self` to an unsigned integer power.
128+
#[inline]
129+
pub fn powu(&self, exp: u32) -> Self {
130+
Pow::pow(self, exp)
131+
}
124132
}
125133

126134
impl<T: Clone + Num + Neg<Output = T>> Complex<T> {
@@ -139,6 +147,12 @@ impl<T: Clone + Num + Neg<Output = T>> Complex<T> {
139147
-self.im.clone() / norm_sqr,
140148
)
141149
}
150+
151+
/// Raises `self` to a signed integer power.
152+
#[inline]
153+
pub fn powi(&self, exp: i32) -> Self {
154+
Pow::pow(self, exp)
155+
}
142156
}
143157

144158
impl<T: Clone + Signed> Complex<T> {
@@ -1499,10 +1513,29 @@ mod test {
14991513
assert_eq!(_4_2i.l1_norm(), 6.0);
15001514
}
15011515

1516+
#[test]
1517+
fn test_pow() {
1518+
for c in all_consts.iter() {
1519+
assert_eq!(c.powi(0), _1_0i);
1520+
let mut pos = _1_0i;
1521+
let mut neg = _1_0i;
1522+
for i in 1i32..20 {
1523+
pos *= c;
1524+
assert_eq!(pos, c.powi(i));
1525+
if c.is_zero() {
1526+
assert!(c.powi(-i).is_nan());
1527+
} else {
1528+
neg /= c;
1529+
assert_eq!(neg, c.powi(-i));
1530+
}
1531+
}
1532+
}
1533+
}
1534+
15021535
#[cfg(feature = "std")]
15031536
mod float {
15041537
use super::*;
1505-
use traits::Float;
1538+
use traits::{Float, Pow};
15061539

15071540
#[test]
15081541
#[cfg_attr(target_arch = "x86", ignore)]
@@ -1547,7 +1580,11 @@ mod test {
15471580

15481581
fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool {
15491582
// returns true if a and b are reasonably close
1550-
(a == b) || (a - b).norm() < tol
1583+
let close = (a == b) || (a - b).norm() < tol;
1584+
if !close {
1585+
println!("{:?} != {:?}", a, b);
1586+
}
1587+
close
15511588
}
15521589

15531590
#[test]
@@ -1604,9 +1641,11 @@ mod test {
16041641

16051642
#[test]
16061643
fn test_powf() {
1607-
let c = Complex::new(2.0, -1.0);
1608-
let r = c.powf(3.5);
1609-
assert!(close_to_tol(r, Complex::new(-0.8684746, -16.695934), 1e-5));
1644+
let c = Complex64::new(2.0, -1.0);
1645+
let expected = Complex64::new(-0.8684746, -16.695934);
1646+
assert!(close_to_tol(c.powf(3.5), expected, 1e-5));
1647+
assert!(close_to_tol(Pow::pow(c, 3.5_f64), expected, 1e-5));
1648+
assert!(close_to_tol(Pow::pow(c, 3.5_f32), expected, 1e-5));
16101649
}
16111650

16121651
#[test]

src/pow.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use super::Complex;
2+
3+
use core::ops::Neg;
4+
#[cfg(feature = "std")]
5+
use traits::Float;
6+
use traits::{Num, One, Pow};
7+
8+
macro_rules! pow_impl {
9+
($U:ty, $S:ty) => {
10+
impl<'a, T: Clone + Num> Pow<$U> for &'a Complex<T> {
11+
type Output = Complex<T>;
12+
13+
#[inline]
14+
fn pow(self, mut exp: $U) -> Self::Output {
15+
if exp == 0 {
16+
return Complex::one();
17+
}
18+
let mut base = self.clone();
19+
20+
while exp & 1 == 0 {
21+
base = base.clone() * base;
22+
exp >>= 1;
23+
}
24+
25+
if exp == 1 {
26+
return base;
27+
}
28+
29+
let mut acc = base.clone();
30+
while exp > 1 {
31+
exp >>= 1;
32+
base = base.clone() * base;
33+
if exp & 1 == 1 {
34+
acc = acc * base.clone();
35+
}
36+
}
37+
acc
38+
}
39+
}
40+
41+
impl<'a, 'b, T: Clone + Num> Pow<&'b $U> for &'a Complex<T> {
42+
type Output = Complex<T>;
43+
44+
#[inline]
45+
fn pow(self, exp: &$U) -> Self::Output {
46+
self.pow(*exp)
47+
}
48+
}
49+
50+
impl<'a, T: Clone + Num + Neg<Output = T>> Pow<$S> for &'a Complex<T> {
51+
type Output = Complex<T>;
52+
53+
#[inline]
54+
fn pow(self, exp: $S) -> Self::Output {
55+
if exp < 0 {
56+
Pow::pow(&self.inv(), exp.wrapping_neg() as $U)
57+
} else {
58+
Pow::pow(self, exp as $U)
59+
}
60+
}
61+
}
62+
63+
impl<'a, 'b, T: Clone + Num + Neg<Output = T>> Pow<&'b $S> for &'a Complex<T> {
64+
type Output = Complex<T>;
65+
66+
#[inline]
67+
fn pow(self, exp: &$S) -> Self::Output {
68+
self.pow(*exp)
69+
}
70+
}
71+
};
72+
}
73+
74+
pow_impl!(u8, i8);
75+
pow_impl!(u16, i16);
76+
pow_impl!(u32, i32);
77+
pow_impl!(u64, i64);
78+
pow_impl!(usize, isize);
79+
#[cfg(has_i128)]
80+
pow_impl!(u128, i128);
81+
82+
// Note: we can't add `impl<T: Float> Pow<T> for Complex<T>` because new blanket impls are a
83+
// breaking change. Someone could already have their own `F` and `impl Pow<F> for Complex<F>`
84+
// which would conflict. We can't even do this in a new semantic version, because we have to
85+
// gate it on the "std" feature, and features can't add breaking changes either.
86+
87+
macro_rules! powf_impl {
88+
($F:ty) => {
89+
#[cfg(feature = "std")]
90+
impl<'a, T: Float> Pow<$F> for &'a Complex<T>
91+
where
92+
$F: Into<T>,
93+
{
94+
type Output = Complex<T>;
95+
96+
#[inline]
97+
fn pow(self, exp: $F) -> Self::Output {
98+
self.powf(exp.into())
99+
}
100+
}
101+
102+
#[cfg(feature = "std")]
103+
impl<'a, 'b, T: Float> Pow<&'b $F> for &'a Complex<T>
104+
where
105+
$F: Into<T>,
106+
{
107+
type Output = Complex<T>;
108+
109+
#[inline]
110+
fn pow(self, &exp: &$F) -> Self::Output {
111+
self.powf(exp.into())
112+
}
113+
}
114+
115+
#[cfg(feature = "std")]
116+
impl<T: Float> Pow<$F> for Complex<T>
117+
where
118+
$F: Into<T>,
119+
{
120+
type Output = Complex<T>;
121+
122+
#[inline]
123+
fn pow(self, exp: $F) -> Self::Output {
124+
self.powf(exp.into())
125+
}
126+
}
127+
128+
#[cfg(feature = "std")]
129+
impl<'b, T: Float> Pow<&'b $F> for Complex<T>
130+
where
131+
$F: Into<T>,
132+
{
133+
type Output = Complex<T>;
134+
135+
#[inline]
136+
fn pow(self, &exp: &$F) -> Self::Output {
137+
self.powf(exp.into())
138+
}
139+
}
140+
};
141+
}
142+
143+
powf_impl!(f32);
144+
powf_impl!(f64);
145+
146+
// These blanket impls are OK, because both the target type and the trait parameter would be
147+
// foreign to anyone else trying to implement something that would overlap, raising E0117.
148+
149+
#[cfg(feature = "std")]
150+
impl<'a, T: Float> Pow<Complex<T>> for &'a Complex<T> {
151+
type Output = Complex<T>;
152+
153+
#[inline]
154+
fn pow(self, exp: Complex<T>) -> Self::Output {
155+
self.powc(exp)
156+
}
157+
}
158+
159+
#[cfg(feature = "std")]
160+
impl<'a, 'b, T: Float> Pow<&'b Complex<T>> for &'a Complex<T> {
161+
type Output = Complex<T>;
162+
163+
#[inline]
164+
fn pow(self, &exp: &'b Complex<T>) -> Self::Output {
165+
self.powc(exp)
166+
}
167+
}
168+
169+
#[cfg(feature = "std")]
170+
impl<T: Float> Pow<Complex<T>> for Complex<T> {
171+
type Output = Complex<T>;
172+
173+
#[inline]
174+
fn pow(self, exp: Complex<T>) -> Self::Output {
175+
self.powc(exp)
176+
}
177+
}
178+
179+
#[cfg(feature = "std")]
180+
impl<'b, T: Float> Pow<&'b Complex<T>> for Complex<T> {
181+
type Output = Complex<T>;
182+
183+
#[inline]
184+
fn pow(self, &exp: &'b Complex<T>) -> Self::Output {
185+
self.powc(exp)
186+
}
187+
}

0 commit comments

Comments
 (0)