diff --git a/src/libstd/iterator.rs b/src/libstd/iterator.rs index 0769aa6a76464..d71bc3156a568 100644 --- a/src/libstd/iterator.rs +++ b/src/libstd/iterator.rs @@ -18,7 +18,7 @@ implementing the `Iterator` trait. */ use cmp; -use num::{Zero, One}; +use num::{Zero, One, Saturating}; use option::{Option, Some, None}; use ops::{Add, Mul}; use cmp::Ord; @@ -863,15 +863,10 @@ impl, U: Iterator> Iterator for Chain { let (a_lower, a_upper) = self.a.size_hint(); let (b_lower, b_upper) = self.b.size_hint(); - let lower = if uint::max_value - a_lower < b_lower { - uint::max_value - } else { - a_lower + b_lower - }; + let lower = a_lower.saturating_add(b_lower); let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) if uint::max_value - x < y => Some(uint::max_value), - (Some(x), Some(y)) => Some(x + y), + (Some(x), Some(y)) => Some(x.saturating_add(y)), _ => None }; @@ -895,12 +890,7 @@ for Chain { #[inline] fn indexable(&self) -> uint { let (a, b) = (self.a.indexable(), self.b.indexable()); - let total = a + b; - if total < a || total < b { - uint::max_value - } else { - total - } + a.saturating_add(b) } #[inline] @@ -1252,11 +1242,10 @@ impl> Iterator for Skip { fn size_hint(&self) -> (uint, Option) { let (lower, upper) = self.iter.size_hint(); - let lower = if lower >= self.n { lower - self.n } else { 0 }; + let lower = lower.saturating_sub(self.n); let upper = match upper { - Some(x) if x >= self.n => Some(x - self.n), - Some(_) => Some(0), + Some(x) => Some(x.saturating_sub(self.n)), None => None }; @@ -1267,12 +1256,7 @@ impl> Iterator for Skip { impl> RandomAccessIterator for Skip { #[inline] fn indexable(&self) -> uint { - let N = self.iter.indexable(); - if N < self.n { - 0 - } else { - N - self.n - } + self.iter.indexable().saturating_sub(self.n) } #[inline] @@ -1389,9 +1373,10 @@ impl<'self, A, T: Iterator, B, U: Iterator> Iterator for fn size_hint(&self) -> (uint, Option) { let (flo, fhi) = self.frontiter.map_default((0, Some(0)), |it| it.size_hint()); let (blo, bhi) = self.backiter.map_default((0, Some(0)), |it| it.size_hint()); + let lo = flo.saturating_add(blo); match (self.iter.size_hint(), fhi, bhi) { - ((0, Some(0)), Some(a), Some(b)) => (flo + blo, Some(a + b)), - _ => (flo + blo, None) + ((0, Some(0)), Some(a), Some(b)) => (lo, Some(a.saturating_add(b))), + _ => (lo, None) } } } diff --git a/src/libstd/num/num.rs b/src/libstd/num/num.rs index 8b61a8a8734b9..bbadf1caca241 100644 --- a/src/libstd/num/num.rs +++ b/src/libstd/num/num.rs @@ -466,6 +466,56 @@ impl Zero for ~T { fn is_zero(&self) -> bool { (**self).is_zero() } } +/// Saturating math operations +pub trait Saturating: Int { + /// Saturating addition operator. + /// Returns a+b, saturating at the numeric bounds instead of overflowing. + #[inline] + fn saturating_add(self, v: Self) -> Self { + let x = self + v; + if v >= Zero::zero() { + if x < self { + // overflow + Bounded::max_value::() + } else { x } + } else { + if x > self { + // underflow + Bounded::min_value::() + } else { x } + } + } + + /// Saturating subtraction operator. + /// Returns a-b, saturating at the numeric bounds instead of overflowing. + #[inline] + fn saturating_sub(self, v: Self) -> Self { + let x = self - v; + if v >= Zero::zero() { + if x > self { + // underflow + Bounded::min_value::() + } else { x } + } else { + if x < self { + // overflow + Bounded::max_value::() + } else { x } + } + } +} + +impl Saturating for int {} +impl Saturating for i8 {} +impl Saturating for i16 {} +impl Saturating for i32 {} +impl Saturating for i64 {} +impl Saturating for uint {} +impl Saturating for u8 {} +impl Saturating for u16 {} +impl Saturating for u32 {} +impl Saturating for u64 {} + /// Helper function for testing numeric operations #[cfg(test)] pub fn test_num(ten: T, two: T) { @@ -482,64 +532,111 @@ pub fn test_num(ten: T, two: T) { assert_eq!(ten.rem(&two), ten % two); } -macro_rules! test_cast_20( - ($_20:expr) => ({ - let _20 = $_20; - - assert_eq!(20u, _20.to_uint()); - assert_eq!(20u8, _20.to_u8()); - assert_eq!(20u16, _20.to_u16()); - assert_eq!(20u32, _20.to_u32()); - assert_eq!(20u64, _20.to_u64()); - assert_eq!(20i, _20.to_int()); - assert_eq!(20i8, _20.to_i8()); - assert_eq!(20i16, _20.to_i16()); - assert_eq!(20i32, _20.to_i32()); - assert_eq!(20i64, _20.to_i64()); - assert_eq!(20f, _20.to_float()); - assert_eq!(20f32, _20.to_f32()); - assert_eq!(20f64, _20.to_f64()); - - assert_eq!(_20, NumCast::from(20u)); - assert_eq!(_20, NumCast::from(20u8)); - assert_eq!(_20, NumCast::from(20u16)); - assert_eq!(_20, NumCast::from(20u32)); - assert_eq!(_20, NumCast::from(20u64)); - assert_eq!(_20, NumCast::from(20i)); - assert_eq!(_20, NumCast::from(20i8)); - assert_eq!(_20, NumCast::from(20i16)); - assert_eq!(_20, NumCast::from(20i32)); - assert_eq!(_20, NumCast::from(20i64)); - assert_eq!(_20, NumCast::from(20f)); - assert_eq!(_20, NumCast::from(20f32)); - assert_eq!(_20, NumCast::from(20f64)); - - assert_eq!(_20, cast(20u)); - assert_eq!(_20, cast(20u8)); - assert_eq!(_20, cast(20u16)); - assert_eq!(_20, cast(20u32)); - assert_eq!(_20, cast(20u64)); - assert_eq!(_20, cast(20i)); - assert_eq!(_20, cast(20i8)); - assert_eq!(_20, cast(20i16)); - assert_eq!(_20, cast(20i32)); - assert_eq!(_20, cast(20i64)); - assert_eq!(_20, cast(20f)); - assert_eq!(_20, cast(20f32)); - assert_eq!(_20, cast(20f64)); - }) -) +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! test_cast_20( + ($_20:expr) => ({ + let _20 = $_20; + + assert_eq!(20u, _20.to_uint()); + assert_eq!(20u8, _20.to_u8()); + assert_eq!(20u16, _20.to_u16()); + assert_eq!(20u32, _20.to_u32()); + assert_eq!(20u64, _20.to_u64()); + assert_eq!(20i, _20.to_int()); + assert_eq!(20i8, _20.to_i8()); + assert_eq!(20i16, _20.to_i16()); + assert_eq!(20i32, _20.to_i32()); + assert_eq!(20i64, _20.to_i64()); + assert_eq!(20f, _20.to_float()); + assert_eq!(20f32, _20.to_f32()); + assert_eq!(20f64, _20.to_f64()); + + assert_eq!(_20, NumCast::from(20u)); + assert_eq!(_20, NumCast::from(20u8)); + assert_eq!(_20, NumCast::from(20u16)); + assert_eq!(_20, NumCast::from(20u32)); + assert_eq!(_20, NumCast::from(20u64)); + assert_eq!(_20, NumCast::from(20i)); + assert_eq!(_20, NumCast::from(20i8)); + assert_eq!(_20, NumCast::from(20i16)); + assert_eq!(_20, NumCast::from(20i32)); + assert_eq!(_20, NumCast::from(20i64)); + assert_eq!(_20, NumCast::from(20f)); + assert_eq!(_20, NumCast::from(20f32)); + assert_eq!(_20, NumCast::from(20f64)); + + assert_eq!(_20, cast(20u)); + assert_eq!(_20, cast(20u8)); + assert_eq!(_20, cast(20u16)); + assert_eq!(_20, cast(20u32)); + assert_eq!(_20, cast(20u64)); + assert_eq!(_20, cast(20i)); + assert_eq!(_20, cast(20i8)); + assert_eq!(_20, cast(20i16)); + assert_eq!(_20, cast(20i32)); + assert_eq!(_20, cast(20i64)); + assert_eq!(_20, cast(20f)); + assert_eq!(_20, cast(20f32)); + assert_eq!(_20, cast(20f64)); + }) + ) + + #[test] fn test_u8_cast() { test_cast_20!(20u8) } + #[test] fn test_u16_cast() { test_cast_20!(20u16) } + #[test] fn test_u32_cast() { test_cast_20!(20u32) } + #[test] fn test_u64_cast() { test_cast_20!(20u64) } + #[test] fn test_uint_cast() { test_cast_20!(20u) } + #[test] fn test_i8_cast() { test_cast_20!(20i8) } + #[test] fn test_i16_cast() { test_cast_20!(20i16) } + #[test] fn test_i32_cast() { test_cast_20!(20i32) } + #[test] fn test_i64_cast() { test_cast_20!(20i64) } + #[test] fn test_int_cast() { test_cast_20!(20i) } + #[test] fn test_f32_cast() { test_cast_20!(20f32) } + #[test] fn test_f64_cast() { test_cast_20!(20f64) } + #[test] fn test_float_cast() { test_cast_20!(20f) } + + #[test] + fn test_saturating_add_uint() { + use uint::max_value; + assert_eq!(3u.saturating_add(5u), 8u); + assert_eq!(3u.saturating_add(max_value-1), max_value); + assert_eq!(max_value.saturating_add(max_value), max_value); + assert_eq!((max_value-2).saturating_add(1), max_value-1); + } + + #[test] + fn test_saturating_sub_uint() { + use uint::max_value; + assert_eq!(5u.saturating_sub(3u), 2u); + assert_eq!(3u.saturating_sub(5u), 0u); + assert_eq!(0u.saturating_sub(1u), 0u); + assert_eq!((max_value-1).saturating_sub(max_value), 0); + } -#[test] fn test_u8_cast() { test_cast_20!(20u8) } -#[test] fn test_u16_cast() { test_cast_20!(20u16) } -#[test] fn test_u32_cast() { test_cast_20!(20u32) } -#[test] fn test_u64_cast() { test_cast_20!(20u64) } -#[test] fn test_uint_cast() { test_cast_20!(20u) } -#[test] fn test_i8_cast() { test_cast_20!(20i8) } -#[test] fn test_i16_cast() { test_cast_20!(20i16) } -#[test] fn test_i32_cast() { test_cast_20!(20i32) } -#[test] fn test_i64_cast() { test_cast_20!(20i64) } -#[test] fn test_int_cast() { test_cast_20!(20i) } -#[test] fn test_f32_cast() { test_cast_20!(20f32) } -#[test] fn test_f64_cast() { test_cast_20!(20f64) } -#[test] fn test_float_cast() { test_cast_20!(20f) } + #[test] + fn test_saturating_add_int() { + use int::{min_value,max_value}; + assert_eq!(3i.saturating_add(5i), 8i); + assert_eq!(3i.saturating_add(max_value-1), max_value); + assert_eq!(max_value.saturating_add(max_value), max_value); + assert_eq!((max_value-2).saturating_add(1), max_value-1); + assert_eq!(3i.saturating_add(-5i), -2i); + assert_eq!(min_value.saturating_add(-1i), min_value); + assert_eq!((-2i).saturating_add(-max_value), min_value); + } + + #[test] + fn test_saturating_sub_int() { + use int::{min_value,max_value}; + assert_eq!(3i.saturating_sub(5i), -2i); + assert_eq!(min_value.saturating_sub(1i), min_value); + assert_eq!((-2i).saturating_sub(max_value), min_value); + assert_eq!(3i.saturating_sub(-5i), 8i); + assert_eq!(3i.saturating_sub(-(max_value-1)), max_value); + assert_eq!(max_value.saturating_sub(-max_value), max_value); + assert_eq!((max_value-2).saturating_sub(-1), max_value-1); + } +}