Skip to content

Commit

Permalink
Merge #56
Browse files Browse the repository at this point in the history
56: Add Complex::powi and assorted Pow impls r=cuviper a=cuviper

Closes #18.

Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
bors[bot] and cuviper committed Jun 6, 2019
2 parents 61a7f5b + c3173f7 commit 2e11d2a
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 6 deletions.
51 changes: 45 additions & 6 deletions src/lib.rs
Expand Up @@ -38,13 +38,15 @@ use core::str::FromStr;
#[cfg(feature = "std")]
use std::error::Error;

use traits::{Inv, MulAdd, Num, One, Signed, Zero};
use traits::{Inv, MulAdd, Num, One, Pow, Signed, Zero};

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

mod cast;
mod pow;

#[cfg(feature = "rand")]
mod crand;
#[cfg(feature = "rand")]
Expand Down Expand Up @@ -121,6 +123,12 @@ impl<T: Clone + Num> Complex<T> {
pub fn unscale(&self, t: T) -> Self {
Self::new(self.re.clone() / t.clone(), self.im.clone() / t)
}

/// Raises `self` to an unsigned integer power.
#[inline]
pub fn powu(&self, exp: u32) -> Self {
Pow::pow(self, exp)
}
}

impl<T: Clone + Num + Neg<Output = T>> Complex<T> {
Expand All @@ -139,6 +147,12 @@ impl<T: Clone + Num + Neg<Output = T>> Complex<T> {
-self.im.clone() / norm_sqr,
)
}

/// Raises `self` to a signed integer power.
#[inline]
pub fn powi(&self, exp: i32) -> Self {
Pow::pow(self, exp)
}
}

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

#[test]
fn test_pow() {
for c in all_consts.iter() {
assert_eq!(c.powi(0), _1_0i);
let mut pos = _1_0i;
let mut neg = _1_0i;
for i in 1i32..20 {
pos *= c;
assert_eq!(pos, c.powi(i));
if c.is_zero() {
assert!(c.powi(-i).is_nan());
} else {
neg /= c;
assert_eq!(neg, c.powi(-i));
}
}
}
}

#[cfg(feature = "std")]
mod float {
use super::*;
use traits::Float;
use traits::{Float, Pow};

#[test]
#[cfg_attr(target_arch = "x86", ignore)]
Expand Down Expand Up @@ -1547,7 +1580,11 @@ mod test {

fn close_to_tol(a: Complex64, b: Complex64, tol: f64) -> bool {
// returns true if a and b are reasonably close
(a == b) || (a - b).norm() < tol
let close = (a == b) || (a - b).norm() < tol;
if !close {
println!("{:?} != {:?}", a, b);
}
close
}

#[test]
Expand Down Expand Up @@ -1604,9 +1641,11 @@ mod test {

#[test]
fn test_powf() {
let c = Complex::new(2.0, -1.0);
let r = c.powf(3.5);
assert!(close_to_tol(r, Complex::new(-0.8684746, -16.695934), 1e-5));
let c = Complex64::new(2.0, -1.0);
let expected = Complex64::new(-0.8684746, -16.695934);
assert!(close_to_tol(c.powf(3.5), expected, 1e-5));
assert!(close_to_tol(Pow::pow(c, 3.5_f64), expected, 1e-5));
assert!(close_to_tol(Pow::pow(c, 3.5_f32), expected, 1e-5));
}

#[test]
Expand Down
187 changes: 187 additions & 0 deletions src/pow.rs
@@ -0,0 +1,187 @@
use super::Complex;

use core::ops::Neg;
#[cfg(feature = "std")]
use traits::Float;
use traits::{Num, One, Pow};

macro_rules! pow_impl {
($U:ty, $S:ty) => {
impl<'a, T: Clone + Num> Pow<$U> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, mut exp: $U) -> Self::Output {
if exp == 0 {
return Complex::one();
}
let mut base = self.clone();

while exp & 1 == 0 {
base = base.clone() * base;
exp >>= 1;
}

if exp == 1 {
return base;
}

let mut acc = base.clone();
while exp > 1 {
exp >>= 1;
base = base.clone() * base;
if exp & 1 == 1 {
acc = acc * base.clone();
}
}
acc
}
}

impl<'a, 'b, T: Clone + Num> Pow<&'b $U> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, exp: &$U) -> Self::Output {
self.pow(*exp)
}
}

impl<'a, T: Clone + Num + Neg<Output = T>> Pow<$S> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, exp: $S) -> Self::Output {
if exp < 0 {
Pow::pow(&self.inv(), exp.wrapping_neg() as $U)
} else {
Pow::pow(self, exp as $U)
}
}
}

impl<'a, 'b, T: Clone + Num + Neg<Output = T>> Pow<&'b $S> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, exp: &$S) -> Self::Output {
self.pow(*exp)
}
}
};
}

pow_impl!(u8, i8);
pow_impl!(u16, i16);
pow_impl!(u32, i32);
pow_impl!(u64, i64);
pow_impl!(usize, isize);
#[cfg(has_i128)]
pow_impl!(u128, i128);

// Note: we can't add `impl<T: Float> Pow<T> for Complex<T>` because new blanket impls are a
// breaking change. Someone could already have their own `F` and `impl Pow<F> for Complex<F>`
// which would conflict. We can't even do this in a new semantic version, because we have to
// gate it on the "std" feature, and features can't add breaking changes either.

macro_rules! powf_impl {
($F:ty) => {
#[cfg(feature = "std")]
impl<'a, T: Float> Pow<$F> for &'a Complex<T>
where
$F: Into<T>,
{
type Output = Complex<T>;

#[inline]
fn pow(self, exp: $F) -> Self::Output {
self.powf(exp.into())
}
}

#[cfg(feature = "std")]
impl<'a, 'b, T: Float> Pow<&'b $F> for &'a Complex<T>
where
$F: Into<T>,
{
type Output = Complex<T>;

#[inline]
fn pow(self, &exp: &$F) -> Self::Output {
self.powf(exp.into())
}
}

#[cfg(feature = "std")]
impl<T: Float> Pow<$F> for Complex<T>
where
$F: Into<T>,
{
type Output = Complex<T>;

#[inline]
fn pow(self, exp: $F) -> Self::Output {
self.powf(exp.into())
}
}

#[cfg(feature = "std")]
impl<'b, T: Float> Pow<&'b $F> for Complex<T>
where
$F: Into<T>,
{
type Output = Complex<T>;

#[inline]
fn pow(self, &exp: &$F) -> Self::Output {
self.powf(exp.into())
}
}
};
}

powf_impl!(f32);
powf_impl!(f64);

// These blanket impls are OK, because both the target type and the trait parameter would be
// foreign to anyone else trying to implement something that would overlap, raising E0117.

#[cfg(feature = "std")]
impl<'a, T: Float> Pow<Complex<T>> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, exp: Complex<T>) -> Self::Output {
self.powc(exp)
}
}

#[cfg(feature = "std")]
impl<'a, 'b, T: Float> Pow<&'b Complex<T>> for &'a Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, &exp: &'b Complex<T>) -> Self::Output {
self.powc(exp)
}
}

#[cfg(feature = "std")]
impl<T: Float> Pow<Complex<T>> for Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, exp: Complex<T>) -> Self::Output {
self.powc(exp)
}
}

#[cfg(feature = "std")]
impl<'b, T: Float> Pow<&'b Complex<T>> for Complex<T> {
type Output = Complex<T>;

#[inline]
fn pow(self, &exp: &'b Complex<T>) -> Self::Output {
self.powc(exp)
}
}

0 comments on commit 2e11d2a

Please sign in to comment.