Skip to content

Commit

Permalink
Implement Pow (#132)
Browse files Browse the repository at this point in the history
Added Pow implementation for OrderedFloat and NotNan. Closes #118
  • Loading branch information
cdchamness committed Apr 25, 2023
1 parent 8b54373 commit dd6956a
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 4 deletions.
202 changes: 200 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ use core::str::FromStr;

#[cfg(not(feature = "std"))]
use num_traits::float::FloatCore as Float;
#[cfg(feature = "std")]
pub use num_traits::Float;
use num_traits::{
AsPrimitive, Bounded, FloatConst, FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero,
};
#[cfg(feature = "std")]
pub use num_traits::{Float, Pow};

// masks for the parts of the IEEE 754 float
const SIGN_MASK: u64 = 0x8000000000000000u64;
Expand Down Expand Up @@ -345,6 +345,104 @@ impl_ordered_float_binop! {Mul, mul, MulAssign, mul_assign}
impl_ordered_float_binop! {Div, div, DivAssign, div_assign}
impl_ordered_float_binop! {Rem, rem, RemAssign, rem_assign}

macro_rules! impl_ordered_float_pow {
($inner:ty, $rhs:ty) => {
#[cfg(feature = "std")]
impl Pow<$rhs> for OrderedFloat<$inner> {
type Output = OrderedFloat<$inner>;
#[inline]
fn pow(self, rhs: $rhs) -> OrderedFloat<$inner> {
OrderedFloat(<$inner>::pow(self.0, rhs))
}
}

#[cfg(feature = "std")]
impl<'a> Pow<&'a $rhs> for OrderedFloat<$inner> {
type Output = OrderedFloat<$inner>;
#[inline]
fn pow(self, rhs: &'a $rhs) -> OrderedFloat<$inner> {
OrderedFloat(<$inner>::pow(self.0, *rhs))
}
}

#[cfg(feature = "std")]
impl<'a> Pow<$rhs> for &'a OrderedFloat<$inner> {
type Output = OrderedFloat<$inner>;
#[inline]
fn pow(self, rhs: $rhs) -> OrderedFloat<$inner> {
OrderedFloat(<$inner>::pow(self.0, rhs))
}
}

#[cfg(feature = "std")]
impl<'a, 'b> Pow<&'a $rhs> for &'b OrderedFloat<$inner> {
type Output = OrderedFloat<$inner>;
#[inline]
fn pow(self, rhs: &'a $rhs) -> OrderedFloat<$inner> {
OrderedFloat(<$inner>::pow(self.0, *rhs))
}
}
};
}

impl_ordered_float_pow! {f32, i8}
impl_ordered_float_pow! {f32, i16}
impl_ordered_float_pow! {f32, u8}
impl_ordered_float_pow! {f32, u16}
impl_ordered_float_pow! {f32, i32}
impl_ordered_float_pow! {f64, i8}
impl_ordered_float_pow! {f64, i16}
impl_ordered_float_pow! {f64, u8}
impl_ordered_float_pow! {f64, u16}
impl_ordered_float_pow! {f64, i32}
impl_ordered_float_pow! {f32, f32}
impl_ordered_float_pow! {f64, f32}
impl_ordered_float_pow! {f64, f64}

macro_rules! impl_ordered_float_self_pow {
($base:ty, $exp:ty) => {
#[cfg(feature = "std")]
impl Pow<OrderedFloat<$exp>> for OrderedFloat<$base> {
type Output = OrderedFloat<$base>;
#[inline]
fn pow(self, rhs: OrderedFloat<$exp>) -> OrderedFloat<$base> {
OrderedFloat(<$base>::pow(self.0, rhs.0))
}
}

#[cfg(feature = "std")]
impl<'a> Pow<&'a OrderedFloat<$exp>> for OrderedFloat<$base> {
type Output = OrderedFloat<$base>;
#[inline]
fn pow(self, rhs: &'a OrderedFloat<$exp>) -> OrderedFloat<$base> {
OrderedFloat(<$base>::pow(self.0, rhs.0))
}
}

#[cfg(feature = "std")]
impl<'a> Pow<OrderedFloat<$exp>> for &'a OrderedFloat<$base> {
type Output = OrderedFloat<$base>;
#[inline]
fn pow(self, rhs: OrderedFloat<$exp>) -> OrderedFloat<$base> {
OrderedFloat(<$base>::pow(self.0, rhs.0))
}
}

#[cfg(feature = "std")]
impl<'a, 'b> Pow<&'a OrderedFloat<$exp>> for &'b OrderedFloat<$base> {
type Output = OrderedFloat<$base>;
#[inline]
fn pow(self, rhs: &'a OrderedFloat<$exp>) -> OrderedFloat<$base> {
OrderedFloat(<$base>::pow(self.0, rhs.0))
}
}
};
}

impl_ordered_float_self_pow! {f32, f32}
impl_ordered_float_self_pow! {f64, f32}
impl_ordered_float_self_pow! {f64, f64}

/// Adds a float directly.
impl<T: Float + Sum> Sum for OrderedFloat<T> {
fn sum<I: Iterator<Item = OrderedFloat<T>>>(iter: I) -> Self {
Expand Down Expand Up @@ -1316,6 +1414,106 @@ impl_not_nan_binop! {Mul, mul, MulAssign, mul_assign}
impl_not_nan_binop! {Div, div, DivAssign, div_assign}
impl_not_nan_binop! {Rem, rem, RemAssign, rem_assign}

// Will panic if NaN value is return from the operation
macro_rules! impl_not_nan_pow {
($inner:ty, $rhs:ty) => {
#[cfg(feature = "std")]
impl Pow<$rhs> for NotNan<$inner> {
type Output = NotNan<$inner>;
#[inline]
fn pow(self, rhs: $rhs) -> NotNan<$inner> {
NotNan::new(<$inner>::pow(self.0, rhs)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a> Pow<&'a $rhs> for NotNan<$inner> {
type Output = NotNan<$inner>;
#[inline]
fn pow(self, rhs: &'a $rhs) -> NotNan<$inner> {
NotNan::new(<$inner>::pow(self.0, *rhs)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a> Pow<$rhs> for &'a NotNan<$inner> {
type Output = NotNan<$inner>;
#[inline]
fn pow(self, rhs: $rhs) -> NotNan<$inner> {
NotNan::new(<$inner>::pow(self.0, rhs)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a, 'b> Pow<&'a $rhs> for &'b NotNan<$inner> {
type Output = NotNan<$inner>;
#[inline]
fn pow(self, rhs: &'a $rhs) -> NotNan<$inner> {
NotNan::new(<$inner>::pow(self.0, *rhs)).expect("Pow resulted in NaN")
}
}
};
}

impl_not_nan_pow! {f32, i8}
impl_not_nan_pow! {f32, i16}
impl_not_nan_pow! {f32, u8}
impl_not_nan_pow! {f32, u16}
impl_not_nan_pow! {f32, i32}
impl_not_nan_pow! {f64, i8}
impl_not_nan_pow! {f64, i16}
impl_not_nan_pow! {f64, u8}
impl_not_nan_pow! {f64, u16}
impl_not_nan_pow! {f64, i32}
impl_not_nan_pow! {f32, f32}
impl_not_nan_pow! {f64, f32}
impl_not_nan_pow! {f64, f64}

// This also should panic on NaN
macro_rules! impl_not_nan_self_pow {
($base:ty, $exp:ty) => {
#[cfg(feature = "std")]
impl Pow<NotNan<$exp>> for NotNan<$base> {
type Output = NotNan<$base>;
#[inline]
fn pow(self, rhs: NotNan<$exp>) -> NotNan<$base> {
NotNan::new(self.0.pow(rhs.0)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a> Pow<&'a NotNan<$exp>> for NotNan<$base> {
type Output = NotNan<$base>;
#[inline]
fn pow(self, rhs: &'a NotNan<$exp>) -> NotNan<$base> {
NotNan::new(self.0.pow(rhs.0)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a> Pow<NotNan<$exp>> for &'a NotNan<$base> {
type Output = NotNan<$base>;
#[inline]
fn pow(self, rhs: NotNan<$exp>) -> NotNan<$base> {
NotNan::new(self.0.pow(rhs.0)).expect("Pow resulted in NaN")
}
}

#[cfg(feature = "std")]
impl<'a, 'b> Pow<&'a NotNan<$exp>> for &'b NotNan<$base> {
type Output = NotNan<$base>;
#[inline]
fn pow(self, rhs: &'a NotNan<$exp>) -> NotNan<$base> {
NotNan::new(self.0.pow(rhs.0)).expect("Pow resulted in NaN")
}
}
};
}

impl_not_nan_self_pow! {f32, f32}
impl_not_nan_self_pow! {f64, f32}
impl_not_nan_self_pow! {f64, f64}

impl<T: Float> Neg for NotNan<T> {
type Output = Self;

Expand Down
75 changes: 73 additions & 2 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ extern crate ordered_float;

#[cfg(not(feature = "std"))]
pub use num_traits::float::FloatCore as Float;
#[cfg(feature = "std")]
pub use num_traits::Float;
pub use num_traits::{Bounded, FloatConst, FromPrimitive, Num, One, Signed, ToPrimitive, Zero};
#[cfg(feature = "std")]
pub use num_traits::{Float, Pow};
pub use ordered_float::*;

pub use std::cmp::Ordering::*;
Expand Down Expand Up @@ -747,6 +747,77 @@ fn float_consts_equal_inner() {
test_float_const_methods!(NotNan<f32>);
}

#[cfg(feature = "std")]
macro_rules! test_pow_ord {
($type:ident < $inner:ident >) => {
assert_eq!($type::<$inner>::from(3.0).pow(2i8), OrderedFloat(9.0));
assert_eq!($type::<$inner>::from(3.0).pow(2i16), OrderedFloat(9.0));
assert_eq!($type::<$inner>::from(3.0).pow(2i32), OrderedFloat(9.0));
assert_eq!($type::<$inner>::from(3.0).pow(2u8), OrderedFloat(9.0));
assert_eq!($type::<$inner>::from(3.0).pow(2u16), OrderedFloat(9.0));
assert_eq!($type::<$inner>::from(3.0).pow(2f32), OrderedFloat(9.0));
};
}

#[cfg(feature = "std")]
macro_rules! test_pow_nn {
($type:ident < $inner:ident >) => {
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2i8),
NotNan::new(9.0).unwrap()
);
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2u8),
NotNan::new(9.0).unwrap()
);
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2i16),
NotNan::new(9.0).unwrap()
);
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2u16),
NotNan::new(9.0).unwrap()
);
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2i32),
NotNan::new(9.0).unwrap()
);
assert_eq!(
$type::<$inner>::new(3.0).unwrap().pow(2f32),
NotNan::new(9.0).unwrap()
);
};
}

#[cfg(feature = "std")]
#[test]
fn test_pow_works() {
assert_eq!(OrderedFloat(3.0).pow(OrderedFloat(2.0)), OrderedFloat(9.0));
test_pow_ord!(OrderedFloat<f32>);
test_pow_ord!(OrderedFloat<f64>);
assert_eq!(
NotNan::new(3.0).unwrap().pow(NotNan::new(2.0).unwrap()),
NotNan::new(9.0).unwrap()
);
test_pow_nn!(NotNan<f32>);
test_pow_nn!(NotNan<f64>);
// Only f64 have Pow<f64> impl by default, so checking those seperate from macro
assert_eq!(OrderedFloat::<f64>::from(3.0).pow(2f64), OrderedFloat(9.0));
assert_eq!(
NotNan::<f64>::new(3.0).unwrap().pow(2f64),
NotNan::new(9.0).unwrap()
);
}

#[cfg(feature = "std")]
#[test]
#[should_panic]
fn test_pow_fails_on_nan() {
let a = not_nan(-1.0);
let b = f32::NAN;
a.pow(b);
}

#[cfg(feature = "arbitrary")]
mod arbitrary_test {
use super::{NotNan, OrderedFloat};
Expand Down

0 comments on commit dd6956a

Please sign in to comment.