-
Notifications
You must be signed in to change notification settings - Fork 137
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
use crate::identities::One; | ||
use crate::CheckedMul; | ||
|
||
/// This trait represents types of which an iterator can be multiplied up with overflow checking. | ||
/// This trait should rarely be called directly. | ||
pub trait CheckedProduct<Result = Self> { | ||
/// Multiplies up the elements of an iterator, returning `None` if an overflow would occur. | ||
/// | ||
/// Multiplies up an empty iterator returns a value representing One. | ||
/// | ||
/// If the iterator contains Zero, the order of elements may effect whether the result is `None`. | ||
fn checked_product<I: Iterator<Item = Self>>(iter: I) -> Option<Result>; | ||
} | ||
|
||
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<T> for &'a T | ||
where | ||
T: CheckedMul + Sized + One, | ||
{ | ||
fn checked_product<I: Iterator<Item = Self>>(mut iter: I) -> Option<T> { | ||
iter.try_fold(T::one(), |acc, x| acc.checked_mul(x)) | ||
} | ||
} | ||
|
||
///This trait is for iterators that can be multiplied up with overflow checking. | ||
trait CheckedProductIter<T, Result>: Iterator<Item = T> { | ||
/// Multiplies up the elements of an iterator, returning `None` if an overflow would occur. | ||
/// | ||
/// Multiplies up an empty iterator returns a value representing One. | ||
/// | ||
/// If the iterator contains Zero, the order of elements may effect whether the result is `None`. | ||
fn checked_product(self) -> Option<Result>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, for consistency with I think then we could also combine your two iterator extensions into one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another great idea, thankyou. I have done this and put both into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I wasn't sure. I'm very happy to close this pull request and move the code changes over there if that's best. |
||
} | ||
|
||
impl<Result, T: CheckedProduct<Result>, I: Iterator<Item = T>> CheckedProductIter<T, Result> for I { | ||
fn checked_product(self) -> Option<Result> { | ||
T::checked_product(self) | ||
} | ||
} | ||
|
||
#[test] | ||
fn checked_product_returns_none_instead_of_overflowing() { | ||
macro_rules! test_checked_product { | ||
($($t:ty)+) => { | ||
$( | ||
assert_eq!(None, [<$t>::MAX, 2 ].iter().checked_product() ); | ||
assert_eq!(None,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() { | ||
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() { | ||
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() { | ||
assert_eq!(None, [100u8, 3u8, 0u8].iter().checked_product()); | ||
assert_eq!(Some(0), [0u8, 100u8, 3u8].iter().checked_product()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use crate::identities::Zero; | ||
use crate::CheckedAdd; | ||
|
||
/// This trait represents types of which an iterator can be summed up with overflow checking. | ||
/// This trait should rarely be called directly. | ||
pub trait CheckedSum<Result = Self> { | ||
/// Adds the elements of an iterator, returning `None` if an overflow would occur. | ||
/// | ||
/// Summing an empty iterator returns a value representing Zero. | ||
/// | ||
/// For signed numbers, the order of elements may effect whether the result is `None`. | ||
fn checked_sum<I: Iterator<Item = Self>>(iter: I) -> Option<Result>; | ||
} | ||
|
||
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<T> for &'a T | ||
where | ||
T: CheckedAdd + Sized + Zero, | ||
{ | ||
fn checked_sum<I: Iterator<Item = Self>>(mut iter: I) -> Option<T> { | ||
iter.try_fold(T::zero(), |acc, x| acc.checked_add(x)) | ||
} | ||
} | ||
|
||
///This trait is for iterators that can be summed up with overflow checking. | ||
trait CheckedSumIter<T, Result>: Iterator<Item = T> { | ||
/// Adds the elements of an iterator, returning `None` if an overflow would occur. | ||
/// | ||
/// Summing an empty iterator returns a value representing Zero. | ||
/// | ||
/// For signed numbers, the order of elements may effect whether the result is `None`. | ||
fn checked_sum(self) -> Option<Result>; | ||
} | ||
|
||
impl<Result, T: CheckedSum<Result>, I: Iterator<Item = T>> CheckedSumIter<T, Result> for I { | ||
fn checked_sum(self) -> Option<Result> { | ||
T::checked_sum(self) | ||
} | ||
} | ||
|
||
#[test] | ||
fn checked_sum_returns_none_instead_of_overflowing() { | ||
use crate::identities::One; | ||
|
||
macro_rules! test_checked_sum { | ||
($($t:ty)+) => { | ||
$( | ||
assert_eq!(None, [<$t>::MAX, <$t>::one()].iter().checked_sum() ); | ||
assert_eq!(None,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() { | ||
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() { | ||
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() { | ||
assert_eq!(None, [120i8, 8i8, -1i8].iter().checked_sum()); | ||
assert_eq!(Some(127), [-1i8, 120i8, 8i8].iter().checked_sum()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod checked_product; | ||
pub mod checked_sum; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is opposite of how
Product
is defined, where the type parameter is the input (Item
) and it always outputsSelf
. I would rather match that for consistency, and same forSum
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree. I feel very silly for having done it the other way around. I have changed it now.