From d6fd99e886e0fbed7a7acc127c59c46ded23f641 Mon Sep 17 00:00:00 2001 From: Mark Wainwright Date: Wed, 2 Nov 2022 16:55:33 +0000 Subject: [PATCH 1/2] Added checked_product and checked_sum --- src/iter/checked_product.rs | 95 ++++++++++++++++++++++++++++++++++++ src/iter/checked_sum.rs | 97 +++++++++++++++++++++++++++++++++++++ src/iter/mod.rs | 2 + src/lib.rs | 2 + 4 files changed, 196 insertions(+) create mode 100644 src/iter/checked_product.rs create mode 100644 src/iter/checked_sum.rs create mode 100644 src/iter/mod.rs diff --git a/src/iter/checked_product.rs b/src/iter/checked_product.rs new file mode 100644 index 00000000..e7263a43 --- /dev/null +++ b/src/iter/checked_product.rs @@ -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 { + /// 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>(iter: I) -> Option; +} + +impl CheckedProduct for T +where + T: CheckedMul + One, +{ + fn checked_product>(mut iter: I) -> Option { + iter.try_fold(Self::one(), |acc, x| acc.checked_mul(&x)) + } +} + +impl<'a, T> CheckedProduct for &'a T +where + T: CheckedMul + Sized + One, +{ + fn checked_product>(mut iter: I) -> Option { + 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: Iterator { + /// 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; +} + +impl, I: Iterator> CheckedProductIter for I { + fn checked_product(self) -> Option { + 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()); +} diff --git a/src/iter/checked_sum.rs b/src/iter/checked_sum.rs new file mode 100644 index 00000000..f239020c --- /dev/null +++ b/src/iter/checked_sum.rs @@ -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 { + /// 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>(iter: I) -> Option; +} + +impl CheckedSum for T +where + T: CheckedAdd + Zero, +{ + fn checked_sum>(mut iter: I) -> Option { + iter.try_fold(Self::zero(), |acc, x| acc.checked_add(&x)) + } +} + +impl<'a, T> CheckedSum for &'a T +where + T: CheckedAdd + Sized + Zero, +{ + fn checked_sum>(mut iter: I) -> Option { + 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: Iterator { + /// 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; +} + +impl, I: Iterator> CheckedSumIter for I { + fn checked_sum(self) -> Option { + 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()); +} diff --git a/src/iter/mod.rs b/src/iter/mod.rs new file mode 100644 index 00000000..6bed24ec --- /dev/null +++ b/src/iter/mod.rs @@ -0,0 +1,2 @@ +pub mod checked_product; +pub mod checked_sum; diff --git a/src/lib.rs b/src/lib.rs index f6562d2e..088c25e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; pub use crate::ops::checked::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, }; @@ -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; From 12432be1d93251623194853b6ddbfbd50313e30a Mon Sep 17 00:00:00 2001 From: Mark Wainwright Date: Thu, 3 Nov 2022 18:09:49 +0000 Subject: [PATCH 2/2] Made CheckedProduct and CheckedSum more consistent with Product and Sum traits. Added NumIter and a blanket implementation --- src/iter/checked_product.rs | 50 ++++++++++++++++--------------------- src/iter/checked_sum.rs | 48 +++++++++++++++-------------------- src/iter/mod.rs | 1 + src/iter/num_iter.rs | 50 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 5 files changed, 93 insertions(+), 58 deletions(-) create mode 100644 src/iter/num_iter.rs diff --git a/src/iter/checked_product.rs b/src/iter/checked_product.rs index e7263a43..1fc46db1 100644 --- a/src/iter/checked_product.rs +++ b/src/iter/checked_product.rs @@ -1,15 +1,15 @@ use crate::identities::One; use crate::CheckedMul; -/// This trait represents types of which an iterator can be multiplied up with overflow checking. +/// 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 { - /// Multiplies up the elements of an iterator, returning `None` if an overflow would occur. +pub trait CheckedProduct : 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. /// - /// Multiplies up an empty iterator returns a value representing One. + /// An empty iterator returns the one value of the type. /// - /// If the iterator contains Zero, the order of elements may effect whether the result is `None`. - fn checked_product>(iter: I) -> Option; + /// For iterators containing zero, the order of elements may effect whether the result is `None`. + fn checked_product>(iter: I) -> Option; } impl CheckedProduct for T @@ -21,38 +21,24 @@ where } } -impl<'a, T> CheckedProduct for &'a T +impl<'a, T> CheckedProduct<&'a T> for T where - T: CheckedMul + Sized + One, + T: CheckedMul + One, { - fn checked_product>(mut iter: I) -> Option { - 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: Iterator { - /// 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; -} - -impl, I: Iterator> CheckedProductIter for I { - fn checked_product(self) -> Option { - T::checked_product(self) + fn checked_product>(mut iter: I) -> Option { + 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>::MAX, 2 ].iter().checked_product() ); - assert_eq!(None,IntoIterator::into_iter([<$t>::MAX, 2]).checked_product() ); + assert_eq!(None::<$t>, [<$t>::MAX, 2 ].iter().checked_product() ); + assert_eq!(None::<$t>,IntoIterator::into_iter([<$t>::MAX, 2]).checked_product() ); )+ }; } @@ -62,6 +48,8 @@ fn checked_product_returns_none_instead_of_overflowing() { #[test] fn checked_product_returns_one_if_empty() { + use crate::iter::num_iter::NumIter; + macro_rules! test_checked_product { ($($t:ty)+) => { $( @@ -76,6 +64,8 @@ fn checked_product_returns_one_if_empty() { #[test] fn checked_product_returns_correct_product() { + use crate::iter::num_iter::NumIter; + macro_rules! test_checked_product { ($($t:ty)+) => { $( @@ -90,6 +80,8 @@ fn checked_product_returns_correct_product() { #[test] fn checked_product_multiplies_left_to_right() { - assert_eq!(None, [100u8, 3u8, 0u8].iter().checked_product()); + use crate::iter::num_iter::NumIter; + + assert_eq!(None::, [100u8, 3u8, 0u8].iter().checked_product()); assert_eq!(Some(0), [0u8, 100u8, 3u8].iter().checked_product()); } diff --git a/src/iter/checked_sum.rs b/src/iter/checked_sum.rs index f239020c..7d084090 100644 --- a/src/iter/checked_sum.rs +++ b/src/iter/checked_sum.rs @@ -1,15 +1,15 @@ use crate::identities::Zero; use crate::CheckedAdd; -/// This trait represents types of which an iterator can be summed up with overflow checking. +/// 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 { - /// Adds the elements of an iterator, returning `None` if an overflow would occur. +pub trait CheckedSum: 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. /// - /// Summing an empty iterator returns a value representing Zero. + /// 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>(iter: I) -> Option; + /// For signed numbers, the order of elements may effect whether the result is `None`. + fn checked_sum>(iter: I) -> Option; } impl CheckedSum for T @@ -21,40 +21,26 @@ where } } -impl<'a, T> CheckedSum for &'a T +impl<'a, T> CheckedSum<&'a T> for T where - T: CheckedAdd + Sized + Zero, + T: CheckedAdd + Zero, { - fn checked_sum>(mut iter: I) -> Option { - iter.try_fold(T::zero(), |acc, x| acc.checked_add(x)) + fn checked_sum>(mut iter: I) -> Option { + iter.try_fold(Self::zero(), |acc, x| acc.checked_add(&x)) } } -///This trait is for iterators that can be summed up with overflow checking. -trait CheckedSumIter: Iterator { - /// 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; -} - -impl, I: Iterator> CheckedSumIter for I { - fn checked_sum(self) -> Option { - T::checked_sum(self) - } -} #[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>::MAX, <$t>::one()].iter().checked_sum() ); - assert_eq!(None,IntoIterator::into_iter([<$t>::MAX, <$t>::one()]).checked_sum() ); + assert_eq!(None::<$t>, [<$t>::MAX, <$t>::one()].iter().checked_sum() ); + assert_eq!(None::<$t>,IntoIterator::into_iter([<$t>::MAX, <$t>::one()]).checked_sum() ); )+ }; } @@ -64,6 +50,8 @@ fn checked_sum_returns_none_instead_of_overflowing() { #[test] fn checked_sum_returns_zero_if_empty() { + use crate::iter::num_iter::NumIter; + macro_rules! test_checked_sum { ($($t:ty)+) => { $( @@ -78,6 +66,8 @@ fn checked_sum_returns_zero_if_empty() { #[test] fn checked_sum_returns_correct_sum() { + use crate::iter::num_iter::NumIter; + macro_rules! test_checked_sum { ($($t:ty)+) => { $( @@ -92,6 +82,8 @@ fn checked_sum_returns_correct_sum() { #[test] fn checked_sum_adds_left_to_right() { - assert_eq!(None, [120i8, 8i8, -1i8].iter().checked_sum()); + use crate::iter::num_iter::NumIter; + + assert_eq!(None::, [120i8, 8i8, -1i8].iter().checked_sum()); assert_eq!(Some(127), [-1i8, 120i8, 8i8].iter().checked_sum()); } diff --git a/src/iter/mod.rs b/src/iter/mod.rs index 6bed24ec..a1d7ed05 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -1,2 +1,3 @@ pub mod checked_product; pub mod checked_sum; +pub mod num_iter; \ No newline at end of file diff --git a/src/iter/num_iter.rs b/src/iter/num_iter.rs new file mode 100644 index 00000000..6c831b46 --- /dev/null +++ b/src/iter/num_iter.rs @@ -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>(self) -> Option + where + Self: Iterator, + 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>(self) -> Option + where + Self: Iterator, + Self: Sized; +} + +impl NumIter for I { + fn checked_sum>(self) -> Option + where + Self: Iterator, + Self: Sized, + { + CheckedSum::checked_sum(self) + } + + fn checked_product>(self) -> Option + where + Self: Iterator, + Self: Sized, + { + CheckedProduct::checked_product(self) + } +} diff --git a/src/lib.rs b/src/lib.rs index 088c25e2..318ad6d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +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}; +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, };