Skip to content

Commit 54c8c23

Browse files
committed
auto merge of #8323 : kballard/rust/saturating, r=thestinger
Implement saturating math in `std::num::Saturating` and use it for `Iterator` impls
2 parents 4da1cfe + 3db3ce2 commit 54c8c23

File tree

2 files changed

+167
-85
lines changed

2 files changed

+167
-85
lines changed

src/libstd/iterator.rs

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ implementing the `Iterator` trait.
1818
*/
1919

2020
use cmp;
21-
use num::{Zero, One};
21+
use num::{Zero, One, Saturating};
2222
use option::{Option, Some, None};
2323
use ops::{Add, Mul};
2424
use cmp::Ord;
@@ -884,15 +884,10 @@ impl<A, T: Iterator<A>, U: Iterator<A>> Iterator<A> for Chain<T, U> {
884884
let (a_lower, a_upper) = self.a.size_hint();
885885
let (b_lower, b_upper) = self.b.size_hint();
886886

887-
let lower = if uint::max_value - a_lower < b_lower {
888-
uint::max_value
889-
} else {
890-
a_lower + b_lower
891-
};
887+
let lower = a_lower.saturating_add(b_lower);
892888

893889
let upper = match (a_upper, b_upper) {
894-
(Some(x), Some(y)) if uint::max_value - x < y => Some(uint::max_value),
895-
(Some(x), Some(y)) => Some(x + y),
890+
(Some(x), Some(y)) => Some(x.saturating_add(y)),
896891
_ => None
897892
};
898893

@@ -916,12 +911,7 @@ for Chain<T, U> {
916911
#[inline]
917912
fn indexable(&self) -> uint {
918913
let (a, b) = (self.a.indexable(), self.b.indexable());
919-
let total = a + b;
920-
if total < a || total < b {
921-
uint::max_value
922-
} else {
923-
total
924-
}
914+
a.saturating_add(b)
925915
}
926916

927917
#[inline]
@@ -1273,11 +1263,10 @@ impl<A, T: Iterator<A>> Iterator<A> for Skip<T> {
12731263
fn size_hint(&self) -> (uint, Option<uint>) {
12741264
let (lower, upper) = self.iter.size_hint();
12751265

1276-
let lower = if lower >= self.n { lower - self.n } else { 0 };
1266+
let lower = lower.saturating_sub(self.n);
12771267

12781268
let upper = match upper {
1279-
Some(x) if x >= self.n => Some(x - self.n),
1280-
Some(_) => Some(0),
1269+
Some(x) => Some(x.saturating_sub(self.n)),
12811270
None => None
12821271
};
12831272

@@ -1288,12 +1277,7 @@ impl<A, T: Iterator<A>> Iterator<A> for Skip<T> {
12881277
impl<A, T: RandomAccessIterator<A>> RandomAccessIterator<A> for Skip<T> {
12891278
#[inline]
12901279
fn indexable(&self) -> uint {
1291-
let N = self.iter.indexable();
1292-
if N < self.n {
1293-
0
1294-
} else {
1295-
N - self.n
1296-
}
1280+
self.iter.indexable().saturating_sub(self.n)
12971281
}
12981282

12991283
#[inline]
@@ -1410,9 +1394,10 @@ impl<'self, A, T: Iterator<A>, B, U: Iterator<B>> Iterator<B> for
14101394
fn size_hint(&self) -> (uint, Option<uint>) {
14111395
let (flo, fhi) = self.frontiter.map_default((0, Some(0)), |it| it.size_hint());
14121396
let (blo, bhi) = self.backiter.map_default((0, Some(0)), |it| it.size_hint());
1397+
let lo = flo.saturating_add(blo);
14131398
match (self.iter.size_hint(), fhi, bhi) {
1414-
((0, Some(0)), Some(a), Some(b)) => (flo + blo, Some(a + b)),
1415-
_ => (flo + blo, None)
1399+
((0, Some(0)), Some(a), Some(b)) => (lo, Some(a.saturating_add(b))),
1400+
_ => (lo, None)
14161401
}
14171402
}
14181403
}

src/libstd/num/num.rs

Lines changed: 157 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,56 @@ impl<T: Zero> Zero for ~T {
466466
fn is_zero(&self) -> bool { (**self).is_zero() }
467467
}
468468

469+
/// Saturating math operations
470+
pub trait Saturating: Int {
471+
/// Saturating addition operator.
472+
/// Returns a+b, saturating at the numeric bounds instead of overflowing.
473+
#[inline]
474+
fn saturating_add(self, v: Self) -> Self {
475+
let x = self + v;
476+
if v >= Zero::zero() {
477+
if x < self {
478+
// overflow
479+
Bounded::max_value::<Self>()
480+
} else { x }
481+
} else {
482+
if x > self {
483+
// underflow
484+
Bounded::min_value::<Self>()
485+
} else { x }
486+
}
487+
}
488+
489+
/// Saturating subtraction operator.
490+
/// Returns a-b, saturating at the numeric bounds instead of overflowing.
491+
#[inline]
492+
fn saturating_sub(self, v: Self) -> Self {
493+
let x = self - v;
494+
if v >= Zero::zero() {
495+
if x > self {
496+
// underflow
497+
Bounded::min_value::<Self>()
498+
} else { x }
499+
} else {
500+
if x < self {
501+
// overflow
502+
Bounded::max_value::<Self>()
503+
} else { x }
504+
}
505+
}
506+
}
507+
508+
impl Saturating for int {}
509+
impl Saturating for i8 {}
510+
impl Saturating for i16 {}
511+
impl Saturating for i32 {}
512+
impl Saturating for i64 {}
513+
impl Saturating for uint {}
514+
impl Saturating for u8 {}
515+
impl Saturating for u16 {}
516+
impl Saturating for u32 {}
517+
impl Saturating for u64 {}
518+
469519
/// Helper function for testing numeric operations
470520
#[cfg(test)]
471521
pub fn test_num<T:Num + NumCast>(ten: T, two: T) {
@@ -482,64 +532,111 @@ pub fn test_num<T:Num + NumCast>(ten: T, two: T) {
482532
assert_eq!(ten.rem(&two), ten % two);
483533
}
484534

485-
macro_rules! test_cast_20(
486-
($_20:expr) => ({
487-
let _20 = $_20;
488-
489-
assert_eq!(20u, _20.to_uint());
490-
assert_eq!(20u8, _20.to_u8());
491-
assert_eq!(20u16, _20.to_u16());
492-
assert_eq!(20u32, _20.to_u32());
493-
assert_eq!(20u64, _20.to_u64());
494-
assert_eq!(20i, _20.to_int());
495-
assert_eq!(20i8, _20.to_i8());
496-
assert_eq!(20i16, _20.to_i16());
497-
assert_eq!(20i32, _20.to_i32());
498-
assert_eq!(20i64, _20.to_i64());
499-
assert_eq!(20f, _20.to_float());
500-
assert_eq!(20f32, _20.to_f32());
501-
assert_eq!(20f64, _20.to_f64());
502-
503-
assert_eq!(_20, NumCast::from(20u));
504-
assert_eq!(_20, NumCast::from(20u8));
505-
assert_eq!(_20, NumCast::from(20u16));
506-
assert_eq!(_20, NumCast::from(20u32));
507-
assert_eq!(_20, NumCast::from(20u64));
508-
assert_eq!(_20, NumCast::from(20i));
509-
assert_eq!(_20, NumCast::from(20i8));
510-
assert_eq!(_20, NumCast::from(20i16));
511-
assert_eq!(_20, NumCast::from(20i32));
512-
assert_eq!(_20, NumCast::from(20i64));
513-
assert_eq!(_20, NumCast::from(20f));
514-
assert_eq!(_20, NumCast::from(20f32));
515-
assert_eq!(_20, NumCast::from(20f64));
516-
517-
assert_eq!(_20, cast(20u));
518-
assert_eq!(_20, cast(20u8));
519-
assert_eq!(_20, cast(20u16));
520-
assert_eq!(_20, cast(20u32));
521-
assert_eq!(_20, cast(20u64));
522-
assert_eq!(_20, cast(20i));
523-
assert_eq!(_20, cast(20i8));
524-
assert_eq!(_20, cast(20i16));
525-
assert_eq!(_20, cast(20i32));
526-
assert_eq!(_20, cast(20i64));
527-
assert_eq!(_20, cast(20f));
528-
assert_eq!(_20, cast(20f32));
529-
assert_eq!(_20, cast(20f64));
530-
})
531-
)
535+
#[cfg(test)]
536+
mod tests {
537+
use super::*;
538+
539+
macro_rules! test_cast_20(
540+
($_20:expr) => ({
541+
let _20 = $_20;
542+
543+
assert_eq!(20u, _20.to_uint());
544+
assert_eq!(20u8, _20.to_u8());
545+
assert_eq!(20u16, _20.to_u16());
546+
assert_eq!(20u32, _20.to_u32());
547+
assert_eq!(20u64, _20.to_u64());
548+
assert_eq!(20i, _20.to_int());
549+
assert_eq!(20i8, _20.to_i8());
550+
assert_eq!(20i16, _20.to_i16());
551+
assert_eq!(20i32, _20.to_i32());
552+
assert_eq!(20i64, _20.to_i64());
553+
assert_eq!(20f, _20.to_float());
554+
assert_eq!(20f32, _20.to_f32());
555+
assert_eq!(20f64, _20.to_f64());
556+
557+
assert_eq!(_20, NumCast::from(20u));
558+
assert_eq!(_20, NumCast::from(20u8));
559+
assert_eq!(_20, NumCast::from(20u16));
560+
assert_eq!(_20, NumCast::from(20u32));
561+
assert_eq!(_20, NumCast::from(20u64));
562+
assert_eq!(_20, NumCast::from(20i));
563+
assert_eq!(_20, NumCast::from(20i8));
564+
assert_eq!(_20, NumCast::from(20i16));
565+
assert_eq!(_20, NumCast::from(20i32));
566+
assert_eq!(_20, NumCast::from(20i64));
567+
assert_eq!(_20, NumCast::from(20f));
568+
assert_eq!(_20, NumCast::from(20f32));
569+
assert_eq!(_20, NumCast::from(20f64));
570+
571+
assert_eq!(_20, cast(20u));
572+
assert_eq!(_20, cast(20u8));
573+
assert_eq!(_20, cast(20u16));
574+
assert_eq!(_20, cast(20u32));
575+
assert_eq!(_20, cast(20u64));
576+
assert_eq!(_20, cast(20i));
577+
assert_eq!(_20, cast(20i8));
578+
assert_eq!(_20, cast(20i16));
579+
assert_eq!(_20, cast(20i32));
580+
assert_eq!(_20, cast(20i64));
581+
assert_eq!(_20, cast(20f));
582+
assert_eq!(_20, cast(20f32));
583+
assert_eq!(_20, cast(20f64));
584+
})
585+
)
586+
587+
#[test] fn test_u8_cast() { test_cast_20!(20u8) }
588+
#[test] fn test_u16_cast() { test_cast_20!(20u16) }
589+
#[test] fn test_u32_cast() { test_cast_20!(20u32) }
590+
#[test] fn test_u64_cast() { test_cast_20!(20u64) }
591+
#[test] fn test_uint_cast() { test_cast_20!(20u) }
592+
#[test] fn test_i8_cast() { test_cast_20!(20i8) }
593+
#[test] fn test_i16_cast() { test_cast_20!(20i16) }
594+
#[test] fn test_i32_cast() { test_cast_20!(20i32) }
595+
#[test] fn test_i64_cast() { test_cast_20!(20i64) }
596+
#[test] fn test_int_cast() { test_cast_20!(20i) }
597+
#[test] fn test_f32_cast() { test_cast_20!(20f32) }
598+
#[test] fn test_f64_cast() { test_cast_20!(20f64) }
599+
#[test] fn test_float_cast() { test_cast_20!(20f) }
600+
601+
#[test]
602+
fn test_saturating_add_uint() {
603+
use uint::max_value;
604+
assert_eq!(3u.saturating_add(5u), 8u);
605+
assert_eq!(3u.saturating_add(max_value-1), max_value);
606+
assert_eq!(max_value.saturating_add(max_value), max_value);
607+
assert_eq!((max_value-2).saturating_add(1), max_value-1);
608+
}
609+
610+
#[test]
611+
fn test_saturating_sub_uint() {
612+
use uint::max_value;
613+
assert_eq!(5u.saturating_sub(3u), 2u);
614+
assert_eq!(3u.saturating_sub(5u), 0u);
615+
assert_eq!(0u.saturating_sub(1u), 0u);
616+
assert_eq!((max_value-1).saturating_sub(max_value), 0);
617+
}
532618

533-
#[test] fn test_u8_cast() { test_cast_20!(20u8) }
534-
#[test] fn test_u16_cast() { test_cast_20!(20u16) }
535-
#[test] fn test_u32_cast() { test_cast_20!(20u32) }
536-
#[test] fn test_u64_cast() { test_cast_20!(20u64) }
537-
#[test] fn test_uint_cast() { test_cast_20!(20u) }
538-
#[test] fn test_i8_cast() { test_cast_20!(20i8) }
539-
#[test] fn test_i16_cast() { test_cast_20!(20i16) }
540-
#[test] fn test_i32_cast() { test_cast_20!(20i32) }
541-
#[test] fn test_i64_cast() { test_cast_20!(20i64) }
542-
#[test] fn test_int_cast() { test_cast_20!(20i) }
543-
#[test] fn test_f32_cast() { test_cast_20!(20f32) }
544-
#[test] fn test_f64_cast() { test_cast_20!(20f64) }
545-
#[test] fn test_float_cast() { test_cast_20!(20f) }
619+
#[test]
620+
fn test_saturating_add_int() {
621+
use int::{min_value,max_value};
622+
assert_eq!(3i.saturating_add(5i), 8i);
623+
assert_eq!(3i.saturating_add(max_value-1), max_value);
624+
assert_eq!(max_value.saturating_add(max_value), max_value);
625+
assert_eq!((max_value-2).saturating_add(1), max_value-1);
626+
assert_eq!(3i.saturating_add(-5i), -2i);
627+
assert_eq!(min_value.saturating_add(-1i), min_value);
628+
assert_eq!((-2i).saturating_add(-max_value), min_value);
629+
}
630+
631+
#[test]
632+
fn test_saturating_sub_int() {
633+
use int::{min_value,max_value};
634+
assert_eq!(3i.saturating_sub(5i), -2i);
635+
assert_eq!(min_value.saturating_sub(1i), min_value);
636+
assert_eq!((-2i).saturating_sub(max_value), min_value);
637+
assert_eq!(3i.saturating_sub(-5i), 8i);
638+
assert_eq!(3i.saturating_sub(-(max_value-1)), max_value);
639+
assert_eq!(max_value.saturating_sub(-max_value), max_value);
640+
assert_eq!((max_value-2).saturating_sub(-1), max_value-1);
641+
}
642+
}

0 commit comments

Comments
 (0)