Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic exp for pow and checked_pow #300

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,10 +700,10 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
exp = exp.wrapping_neg();
self = self.recip();
}
// It should always be possible to convert a positive `i32` to a `usize`.
// It should always be possible to convert a positive `i32` to a `u32`.
// Note, `i32::MIN` will wrap and still be negative, so we need to convert
// to `u32` without sign-extension before growing to `usize`.
super::pow(self, (exp as u32).to_usize().unwrap())
// to `u32` without sign-extension.
super::pow(self, exp as u32)
}

/// Converts to degrees, assuming the number is in radians.
Expand Down
152 changes: 58 additions & 94 deletions src/pow.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{CheckedMul, One};
use crate::{CheckedMul, One, PrimInt, Unsigned};
use core::num::Wrapping;
use core::ops::Mul;

/// Binary operator for raising a value to a power.
pub trait Pow<RHS> {
Expand All @@ -19,17 +18,24 @@ pub trait Pow<RHS> {
}

macro_rules! pow_impl {
(prim_int $t:ty) => {
pow_impl!($t, u8);
pow_impl!($t, u16);
pow_impl!($t, u32, u32, <$t>::pow);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utilizes the default implementation of pow for exp with u32

pow_impl!($t, u64);
pow_impl!($t, u128);
pow_impl!($t, usize);
};
($t:ty) => {
pow_impl!($t, u8);
pow_impl!($t, u16);
pow_impl!($t, u32);
pow_impl!($t, u64);
pow_impl!($t, u128);
pow_impl!($t, usize);

// FIXME: these should be possible
// pow_impl!($t, u16);
// pow_impl!($t, u32);
// pow_impl!($t, u64);
};
($t:ty, $rhs:ty) => {
pow_impl!($t, $rhs, usize, pow);
pow_impl!($t, $rhs, $rhs, pow);
};
($t:ty, $rhs:ty, $desired_rhs:ty, $method:expr) => {
impl Pow<$rhs> for $t {
Expand Down Expand Up @@ -66,57 +72,19 @@ macro_rules! pow_impl {
};
}

pow_impl!(u8, u8, u32, u8::pow);
pow_impl!(u8, u16, u32, u8::pow);
pow_impl!(u8, u32, u32, u8::pow);
pow_impl!(u8, usize);
pow_impl!(i8, u8, u32, i8::pow);
pow_impl!(i8, u16, u32, i8::pow);
pow_impl!(i8, u32, u32, i8::pow);
pow_impl!(i8, usize);
pow_impl!(u16, u8, u32, u16::pow);
pow_impl!(u16, u16, u32, u16::pow);
pow_impl!(u16, u32, u32, u16::pow);
pow_impl!(u16, usize);
pow_impl!(i16, u8, u32, i16::pow);
pow_impl!(i16, u16, u32, i16::pow);
pow_impl!(i16, u32, u32, i16::pow);
pow_impl!(i16, usize);
pow_impl!(u32, u8, u32, u32::pow);
pow_impl!(u32, u16, u32, u32::pow);
pow_impl!(u32, u32, u32, u32::pow);
pow_impl!(u32, usize);
pow_impl!(i32, u8, u32, i32::pow);
pow_impl!(i32, u16, u32, i32::pow);
pow_impl!(i32, u32, u32, i32::pow);
pow_impl!(i32, usize);
pow_impl!(u64, u8, u32, u64::pow);
pow_impl!(u64, u16, u32, u64::pow);
pow_impl!(u64, u32, u32, u64::pow);
pow_impl!(u64, usize);
pow_impl!(i64, u8, u32, i64::pow);
pow_impl!(i64, u16, u32, i64::pow);
pow_impl!(i64, u32, u32, i64::pow);
pow_impl!(i64, usize);

pow_impl!(u128, u8, u32, u128::pow);
pow_impl!(u128, u16, u32, u128::pow);
pow_impl!(u128, u32, u32, u128::pow);
pow_impl!(u128, usize);

pow_impl!(i128, u8, u32, i128::pow);
pow_impl!(i128, u16, u32, i128::pow);
pow_impl!(i128, u32, u32, i128::pow);
pow_impl!(i128, usize);

pow_impl!(usize, u8, u32, usize::pow);
pow_impl!(usize, u16, u32, usize::pow);
pow_impl!(usize, u32, u32, usize::pow);
pow_impl!(usize, usize);
pow_impl!(isize, u8, u32, isize::pow);
pow_impl!(isize, u16, u32, isize::pow);
pow_impl!(isize, u32, u32, isize::pow);
pow_impl!(isize, usize);
pow_impl!(prim_int u8);
pow_impl!(prim_int u16);
pow_impl!(prim_int u32);
pow_impl!(prim_int u64);
pow_impl!(prim_int u128);
pow_impl!(prim_int i8);
pow_impl!(prim_int i16);
pow_impl!(prim_int i32);
pow_impl!(prim_int i64);
pow_impl!(prim_int i128);
pow_impl!(prim_int usize);
pow_impl!(prim_int isize);

pow_impl!(Wrapping<u8>);
pow_impl!(Wrapping<i8>);
pow_impl!(Wrapping<u16>);
Expand All @@ -130,18 +98,6 @@ pow_impl!(Wrapping<i128>);
pow_impl!(Wrapping<usize>);
pow_impl!(Wrapping<isize>);

// FIXME: these should be possible
// pow_impl!(u8, u64);
// pow_impl!(i16, u64);
// pow_impl!(i8, u64);
// pow_impl!(u16, u64);
// pow_impl!(u32, u64);
// pow_impl!(i32, u64);
// pow_impl!(u64, u64);
// pow_impl!(i64, u64);
// pow_impl!(usize, u64);
// pow_impl!(isize, u64);

#[cfg(any(feature = "std", feature = "libm"))]
mod float_impls {
use super::Pow;
Expand Down Expand Up @@ -171,29 +127,33 @@ mod float_impls {
/// ```rust
/// use num_traits::pow;
///
/// assert_eq!(pow(2i8, 4), 16);
/// assert_eq!(pow(6u8, 3), 216);
/// assert_eq!(pow(0u8, 0), 1); // Be aware if this case affects you
/// assert_eq!(pow(2i8, 4u32), 16);
/// assert_eq!(pow(6u8, 3u32), 216);
/// assert_eq!(pow(0u8, 0u32), 1); // Be aware if this case affects you
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
if exp == 0 {
pub fn pow<T, U>(mut base: T, mut exp: U) -> T
where
T: Clone + One,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed T: Mul<T, Output = T> because One needs it.

U: PrimInt + Unsigned,
{
if exp == U::zero() {
return T::one();
}

while exp & 1 == 0 {
while exp & U::one() == U::zero() {
base = base.clone() * base;
exp >>= 1;
exp = exp >> 1;
}
if exp == 1 {
if exp == U::one() {
return base;
}

let mut acc = base.clone();
while exp > 1 {
exp >>= 1;
while exp > U::one() {
exp = exp >> 1;
base = base.clone() * base;
if exp & 1 == 1 {
if exp & U::one() == U::one() {
acc = acc * base.clone();
}
}
Expand All @@ -211,30 +171,34 @@ pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) ->
/// ```rust
/// use num_traits::checked_pow;
///
/// assert_eq!(checked_pow(2i8, 4), Some(16));
/// assert_eq!(checked_pow(7i8, 8), None);
/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
/// assert_eq!(checked_pow(0u32, 0), Some(1)); // Be aware if this case affect you
/// assert_eq!(checked_pow(2i8, 4u32), Some(16));
/// assert_eq!(checked_pow(7i8, 8u32), None);
/// assert_eq!(checked_pow(7u32, 8u32), Some(5_764_801));
/// assert_eq!(checked_pow(0u32, 0u32), Some(1)); // Be aware if this case affect you
/// ```
#[inline]
pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
if exp == 0 {
pub fn checked_pow<T, U>(mut base: T, mut exp: U) -> Option<T>
where
T: Clone + One + CheckedMul,
U: PrimInt + Unsigned,
{
if exp == U::zero() {
return Some(T::one());
}

while exp & 1 == 0 {
while exp & U::one() == U::zero() {
base = base.checked_mul(&base)?;
exp >>= 1;
exp = exp >> 1;
}
if exp == 1 {
if exp == U::one() {
return Some(base);
}

let mut acc = base.clone();
while exp > 1 {
exp >>= 1;
while exp > U::one() {
exp = exp >> 1;
base = base.checked_mul(&base)?;
if exp & 1 == 1 {
if exp & U::one() == U::one() {
acc = acc.checked_mul(&base)?;
}
}
Expand Down