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

Add CheckedSum and CheckedProduct traits #251

Open
wants to merge 2 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
87 changes: 87 additions & 0 deletions src/iter/checked_product.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use crate::identities::One;
use crate::CheckedMul;

/// Trait to represent types that can be created by multiplying elements of an iterator with overflow checking.
/// This trait should rarely be called directly.
pub trait CheckedProduct<A = Self> : Sized {
/// Method which takes an iterator and generates Self from the elements by multiplying the items with overflow checking, returning `None` if the multiplication would overflow.
///
/// An empty iterator returns the one value of the type.
///
/// For iterators containing zero, the order of elements may effect whether the result is `None`.
fn checked_product<I: Iterator<Item = A>>(iter: I) -> Option<Self>;
}

impl<T> CheckedProduct<T> for T
where
T: CheckedMul + One,
{
fn checked_product<I: Iterator<Item = Self>>(mut iter: I) -> Option<Self> {
iter.try_fold(Self::one(), |acc, x| acc.checked_mul(&x))
}
}

impl<'a, T> CheckedProduct<&'a T> for T
where
T: CheckedMul + One,
{
fn checked_product<I: Iterator<Item = &'a Self>>(mut iter: I) -> Option<Self> {
iter.try_fold(Self::one(), |acc, x| acc.checked_mul(&x))
}
}

#[test]
fn checked_product_returns_none_instead_of_overflowing() {
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_product {
($($t:ty)+) => {
$(
assert_eq!(None::<$t>, [<$t>::MAX, 2 ].iter().checked_product() );
assert_eq!(None::<$t>,IntoIterator::into_iter([<$t>::MAX, 2]).checked_product() );
)+
};
}

test_checked_product!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_product_returns_one_if_empty() {
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_product {
($($t:ty)+) => {
$(
assert_eq!(Some(<$t>::one()), ([] as [$t; 0]).iter().checked_product() );
assert_eq!(Some(<$t>::one()),IntoIterator::into_iter(([] as [$t; 0])).checked_product() );
)+
};
}

test_checked_product!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_product_returns_correct_product() {
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_product {
($($t:ty)+) => {
$(
assert_eq!(Some(42), ([3,7,2] as [$t; 3]).iter().checked_product() );
assert_eq!(Some(42),IntoIterator::into_iter(([3,7,2] as [$t; 3])).checked_product() );
)+
};
}

test_checked_product!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_product_multiplies_left_to_right() {
use crate::iter::num_iter::NumIter;

assert_eq!(None::<u8>, [100u8, 3u8, 0u8].iter().checked_product());
assert_eq!(Some(0), [0u8, 100u8, 3u8].iter().checked_product());
}
89 changes: 89 additions & 0 deletions src/iter/checked_sum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::identities::Zero;
use crate::CheckedAdd;

/// Trait to represent types that can be created by summing up an iterator with overflow checking.
/// This trait should rarely be called directly.
pub trait CheckedSum<A = Self>: Sized {
/// Method which takes an iterator and generates Self from the elements by “summing up” the items with overflow checking, returning `None` if the addition would overflow.
///
/// An empty iterator returns the one value of the type.
///
/// For signed numbers, the order of elements may effect whether the result is `None`.
fn checked_sum<I: Iterator<Item = A>>(iter: I) -> Option<Self>;
}

impl<T> CheckedSum<T> for T
where
T: CheckedAdd + Zero,
{
fn checked_sum<I: Iterator<Item = Self>>(mut iter: I) -> Option<Self> {
iter.try_fold(Self::zero(), |acc, x| acc.checked_add(&x))
}
}

impl<'a, T> CheckedSum<&'a T> for T
where
T: CheckedAdd + Zero,
{
fn checked_sum<I: Iterator<Item = &'a Self>>(mut iter: I) -> Option<Self> {
iter.try_fold(Self::zero(), |acc, x| acc.checked_add(&x))
}
}


#[test]
fn checked_sum_returns_none_instead_of_overflowing() {
use crate::identities::One;
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_sum {
($($t:ty)+) => {
$(
assert_eq!(None::<$t>, [<$t>::MAX, <$t>::one()].iter().checked_sum() );
assert_eq!(None::<$t>,IntoIterator::into_iter([<$t>::MAX, <$t>::one()]).checked_sum() );
)+
};
}

test_checked_sum!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_sum_returns_zero_if_empty() {
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_sum {
($($t:ty)+) => {
$(
assert_eq!(Some(<$t>::zero()), ([] as [$t; 0]).iter().checked_sum() );
assert_eq!(Some(<$t>::zero()),IntoIterator::into_iter(([] as [$t; 0])).checked_sum() );
)+
};
}

test_checked_sum!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_sum_returns_correct_sum() {
use crate::iter::num_iter::NumIter;

macro_rules! test_checked_sum {
($($t:ty)+) => {
$(
assert_eq!(Some(42), ([40,2] as [$t; 2]).iter().checked_sum() );
assert_eq!(Some(42),IntoIterator::into_iter(([40,2] as [$t; 2])).checked_sum() );
)+
};
}

test_checked_sum!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}

#[test]
fn checked_sum_adds_left_to_right() {
use crate::iter::num_iter::NumIter;

assert_eq!(None::<i8>, [120i8, 8i8, -1i8].iter().checked_sum());
assert_eq!(Some(127), [-1i8, 120i8, 8i8].iter().checked_sum());
}
3 changes: 3 additions & 0 deletions src/iter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod checked_product;
pub mod checked_sum;
pub mod num_iter;
50 changes: 50 additions & 0 deletions src/iter/num_iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::CheckedProduct;
use crate::CheckedSum;

/// An [`Iterator`] blanket implementation that provides extra adaptors and
/// methods.
///
/// This traits defines methods for summing and multiplying together iterators with overflow checking.
pub trait NumIter: Iterator {
/// Sums the elements of an iterator with overflow checking.
///
/// Takes each element, adds them together, and returns the result or None if the addition would overflow.
///
/// An empty iterator returns the zero value of the type.
///
/// For signed numbers, the order of elements may effect whether the result is `None`.
fn checked_sum<A, T: CheckedSum<A>>(self) -> Option<T>
where
Self: Iterator<Item = A>,
Self: Sized;

/// Iterates over the entire iterator, multiplying all the elements with overflow checking.
///
/// Takes each element, multiplies them together, and returns the result or None if the multiplication would overflow.
///
/// An empty iterator returns the one value of the type.
///
/// For iterators containing zero, the order of elements may effect whether the result is `None`.
fn checked_product<A, T: CheckedProduct<A>>(self) -> Option<T>
where
Self: Iterator<Item = A>,
Self: Sized;
}

impl<I: Iterator> NumIter for I {
fn checked_sum<A, T: CheckedSum<A>>(self) -> Option<T>
where
Self: Iterator<Item = A>,
Self: Sized,
{
CheckedSum::checked_sum(self)
}

fn checked_product<A, T: CheckedProduct<A>>(self) -> Option<T>
where
Self: Iterator<Item = A>,
Self: Sized,
{
CheckedProduct::checked_product(self)
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub use crate::float::FloatConst;
pub use crate::cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive};
pub use crate::identities::{one, zero, One, Zero};
pub use crate::int::PrimInt;
pub use crate::iter::{checked_product::CheckedProduct, checked_sum::CheckedSum, num_iter::NumIter};
pub use crate::ops::checked::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
};
Expand All @@ -56,6 +57,7 @@ pub mod cast;
pub mod float;
pub mod identities;
pub mod int;
pub mod iter;
pub mod ops;
pub mod pow;
pub mod real;
Expand Down