From 3c19f1bca83be3f4abef378d0a4cd852c8615164 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 27 Jun 2013 14:20:42 +0200 Subject: [PATCH 1/3] Refactored int/uint range code in preparation for change to range_rev semantics. Also added unit tests of range code to test refactoring. The num-range-rev.rs test will need to be updated when the range_rev semantics change. --- src/libstd/num/int_macros.rs | 94 ++++++++++++++++++----- src/libstd/num/uint_macros.rs | 93 +++++++++++++++++----- src/test/run-pass/num-range-rev.rs | 114 +++++++++++++++++++++++++++ src/test/run-pass/num-range.rs | 119 +++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+), 36 deletions(-) create mode 100644 src/test/run-pass/num-range-rev.rs create mode 100644 src/test/run-pass/num-range.rs diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index 75e0bbcb71b..4fd30be80e6 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -29,28 +29,38 @@ pub static bytes : uint = ($bits / 8); pub static min_value: $T = (-1 as $T) << (bits - 1); pub static max_value: $T = min_value - 1 as $T; -/// -/// Iterate over the range [`lo`..`hi`) -/// -/// # Arguments -/// -/// * `lo` - lower bound, inclusive -/// * `hi` - higher bound, exclusive -/// -/// # Examples -/// ~~~ -/// let mut sum = 0; -/// for int::range(1, 5) |i| { -/// sum += i; -/// } -/// assert!(sum == 10); -/// ~~~ -/// +enum Range { Closed, HalfOpen } + #[inline] -pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool { +/// +/// Iterate through a range with a given step value. +/// +/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed; +/// otherwise `term` denotes the half-open interval `[stop-step,stop)`. +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// `x_j == start + step*j`, and `x_n` lies in the interval `term`. +/// +/// If no such nonnegative integer `n` exists, then the iteration range +/// is empty. +/// +fn range_step_core(start: $T, stop: $T, step: $T, r: Range, it: &fn($T) -> bool) -> bool { let mut i = start; if step == 0 { fail!(~"range_step called with step == 0"); + } else if step == (1 as $T) { // elide bounds check to tighten loop + while i < stop { + if !it(i) { return false; } + // no need for overflow check; + // cannot have i + 1 > max_value because i < stop <= max_value + i += (1 as $T); + } + } else if step == (-1 as $T) { // elide bounds check to tighten loop + while i > stop { + if !it(i) { return false; } + // no need for underflow check; + // cannot have i - 1 < min_value because i > stop >= min_value + i -= (1 as $T); + } } else if step > 0 { // ascending while i < stop { if !it(i) { return false; } @@ -66,9 +76,55 @@ pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool { i += step; } } - return true; + match r { + HalfOpen => return true, + Closed => return (i != stop || it(i)) + } } +#[inline] +/// +/// Iterate through the range [`start`..`stop`) with a given step value. +/// +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// * `x_i == start + step*i`, and +/// * `n` is the greatest nonnegative integer such that `x_n < stop` +/// +/// (If no such `n` exists, then the iteration range is empty.) +/// +/// # Arguments +/// +/// * `start` - lower bound, inclusive +/// * `stop` - higher bound, exclusive +/// +/// # Examples +/// ~~~ +/// let mut sum = 0; +/// for int::range(1, 5) |i| { +/// sum += i; +/// } +/// assert!(sum == 10); +/// ~~~ +/// +pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool { + range_step_core(start, stop, step, HalfOpen, it) +} + +#[inline] +/// +/// Iterate through a range with a given step value. +/// +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// `x_i == start + step*i` and `x_n <= last < step + x_n`. +/// +/// (If no such nonnegative integer `n` exists, then the iteration +/// range is empty.) +/// +pub fn range_step_inclusive(start: $T, last: $T, step: $T, it: &fn($T) -> bool) -> bool { + range_step_core(start, last, step, Closed, it) +} + + #[inline] /// Iterate over the range [`lo`..`hi`) pub fn range(lo: $T, hi: $T, it: &fn($T) -> bool) -> bool { diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index de1b997b14b..09397ecfd77 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -30,32 +30,46 @@ pub static bytes : uint = ($bits / 8); pub static min_value: $T = 0 as $T; pub static max_value: $T = 0 as $T - 1 as $T; +enum Range { Closed, HalfOpen } + #[inline] -/** - * Iterate through a range with a given step value. - * - * # Examples - * ~~~ {.rust} - * let nums = [1,2,3,4,5,6,7]; - * - * for uint::range_step(0, nums.len() - 1, 2) |i| { - * println(fmt!("%d & %d", nums[i], nums[i+1])); - * } - * ~~~ - */ -pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool { +/// +/// Iterate through a range with a given step value. +/// +/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed; +/// otherwise `term` denotes the half-open interval `[stop-step,stop)`. +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// `x_j == start + step*j`, and `x_n` lies in the interval `term`. +/// +/// If no such nonnegative integer `n` exists, then the iteration range +/// is empty. +/// +fn range_step_core(start: $T, stop: $T, step: $T_SIGNED, r: Range, it: &fn($T) -> bool) -> bool { let mut i = start; if step == 0 { fail!("range_step called with step == 0"); - } - if step >= 0 { + } else if step == (1 as $T_SIGNED) { // elide bounds check to tighten loop + while i < stop { + if !it(i) { return false; } + // no need for overflow check; + // cannot have i + 1 > max_value because i < stop <= max_value + i += (1 as $T); + } + } else if step == (-1 as $T_SIGNED) { // elide bounds check to tighten loop + while i > stop { + if !it(i) { return false; } + // no need for underflow check; + // cannot have i - 1 < min_value because i > stop >= min_value + i -= (1 as $T); + } + } else if step > 0 { // ascending while i < stop { if !it(i) { return false; } // avoiding overflow. break if i + step > max_value if i > max_value - (step as $T) { return true; } i += step as $T; } - } else { + } else { // descending while i > stop { if !it(i) { return false; } // avoiding underflow. break if i + step < min_value @@ -63,7 +77,52 @@ pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> i -= -step as $T; } } - return true; + match r { + HalfOpen => return true, + Closed => return (i != stop || it(i)) + } +} + +#[inline] +/// +/// Iterate through the range [`start`..`stop`) with a given step value. +/// +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// - `x_i == start + step*i`, and +/// - `n` is the greatest nonnegative integer such that `x_n < stop` +/// +/// (If no such `n` exists, then the iteration range is empty.) +/// +/// # Arguments +/// +/// * `start` - lower bound, inclusive +/// * `stop` - higher bound, exclusive +/// +/// # Examples +/// ~~~ {.rust} +/// let nums = [1,2,3,4,5,6,7]; +/// +/// for uint::range_step(0, nums.len() - 1, 2) |i| { +/// println(fmt!("%d & %d", nums[i], nums[i+1])); +/// } +/// ~~~ +/// +pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool { + range_step_core(start, stop, step, HalfOpen, it) +} + +#[inline] +/// +/// Iterate through a range with a given step value. +/// +/// Iterates through the range `[x_0, x_1, ..., x_n]` where +/// `x_i == start + step*i` and `x_n <= last < step + x_n`. +/// +/// (If no such nonnegative integer `n` exists, then the iteration +/// range is empty.) +/// +pub fn range_step_inclusive(start: $T, last: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool { + range_step_core(start, last, step, Closed, it) } #[inline] diff --git a/src/test/run-pass/num-range-rev.rs b/src/test/run-pass/num-range-rev.rs new file mode 100644 index 00000000000..e65c793a7b4 --- /dev/null +++ b/src/test/run-pass/num-range-rev.rs @@ -0,0 +1,114 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::int; +use std::uint; + +fn uint_range(lo: uint, hi: uint, it: &fn(uint) -> bool) -> bool { + uint::range(lo, hi, it) +} + +fn int_range(lo: int, hi: int, it: &fn(int) -> bool) -> bool { + int::range(lo, hi, it) +} + +fn uint_range_rev(hi: uint, lo: uint, it: &fn(uint) -> bool) -> bool { + uint::range_rev(hi, lo, it) +} + +fn int_range_rev(hi: int, lo: int, it: &fn(int) -> bool) -> bool { + int::range_rev(hi, lo, it) +} + +fn int_range_step(a: int, b: int, step: int, it: &fn(int) -> bool) -> bool { + int::range_step(a, b, step, it) +} + +fn uint_range_step(a: uint, b: uint, step: int, it: &fn(uint) -> bool) -> bool { + uint::range_step(a, b, step, it) +} + + +pub fn main() { + // int and uint have same result for + // Sum{100 > i >= 2} == (Sum{1 <= i <= 99} - 1) == n*(n+1)/2 - 1 for n=99 + let mut sum = 0u; + for uint_range_rev(99, 1) |i| { + sum += i; + } + assert_eq!(sum, 4949); + + let mut sum = 0i; + for int_range_rev(99, 1) |i| { + sum += i; + } + assert_eq!(sum, 4949); + + + // elements are visited in correct order + let primes = [2,3,5,7,11]; + let mut prod = 1i; + for uint_range_rev(4, 0) |i| { + println(fmt!("uint 4 downto 0: %u", i)); + prod *= int::pow(primes[i], i); + } + assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3); + let mut prod = 1i; + for int_range_rev(4, 0) |i| { + println(fmt!("int 4 downto 0: %d", i)); + prod *= int::pow(primes[i], i as uint); + } + assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3); + + + // range and range_rev are symmetric. + let mut sum_up = 0u; + for uint_range(10, 30) |i| { + sum_up += i; + } + let mut sum_down = 0u; + for uint_range_rev(29, 9) |i| { + sum_down += i; + } + assert_eq!(sum_up, sum_down); + + let mut sum_up = 0; + for int_range(-20, 10) |i| { + sum_up += i; + } + let mut sum_down = 0; + for int_range_rev(9, -21) |i| { + sum_down += i; + } + assert_eq!(sum_up, sum_down); + + + // empty ranges + for int_range_rev(10, 10) |_| { + fail!("range should be empty when start == stop"); + } + + for uint_range_rev(0, 1) |_| { + // fail!("range should be empty when start-1 underflows"); + } + + // range iterations do not wrap/underflow + let mut uflo_loop_visited = ~[]; + for int_range_step(int::min_value+15, int::min_value, -4) |x| { + uflo_loop_visited.push(x - int::min_value); + } + assert_eq!(uflo_loop_visited, ~[15, 11, 7, 3]); + + let mut uflo_loop_visited = ~[]; + for uint_range_step(uint::min_value+15, uint::min_value, -4) |x| { + uflo_loop_visited.push(x - uint::min_value); + } + assert_eq!(uflo_loop_visited, ~[15, 11, 7, 3]); +} diff --git a/src/test/run-pass/num-range.rs b/src/test/run-pass/num-range.rs new file mode 100644 index 00000000000..7c1f905a049 --- /dev/null +++ b/src/test/run-pass/num-range.rs @@ -0,0 +1,119 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::int; +use std::uint; + +fn uint_range(lo: uint, hi: uint, it: &fn(uint) -> bool) -> bool { + uint::range(lo, hi, it) +} + +fn int_range(lo: int, hi: int, it: &fn(int) -> bool) -> bool { + int::range(lo, hi, it) +} + +fn int_range_step(a: int, b: int, step: int, it: &fn(int) -> bool) -> bool { + int::range_step(a, b, step, it) +} + +fn uint_range_step(a: uint, b: uint, s: int, it: &fn(uint) -> bool) -> bool { + uint::range_step(a, b, s, it) +} + +pub fn main() { + println(fmt!("num-range start")); + // int and uint have same result for + // Sum{2 <= i < 100} == (Sum{1 <= i <= 99} - 1) == n*(n+1)/2 - 1 for n=99 + let mut sum = 0u; + for uint_range(2, 100) |i| { + sum += i; + } + assert_eq!(sum, 4949); + + let mut sum = 0i; + for int_range(2, 100) |i| { + sum += i; + } + assert_eq!(sum, 4949); + + + // elements are visited in correct order + let primes = [2,3,5,7]; + let mut prod = 1i; + for uint_range(0, 4) |i| { + prod *= int::pow(primes[i], i); + } + assert_eq!(prod, 1*3*5*5*7*7*7); + let mut prod = 1i; + for int_range(0, 4) |i| { + prod *= int::pow(primes[i], i as uint); + } + assert_eq!(prod, 1*3*5*5*7*7*7); + + + // empty ranges + for int_range(10, 10) |_| { + fail!("range should be empty when start == stop"); + } + + for uint_range(10, 10) |_| { + fail!("range should be empty when start == stop"); + } + + + // range iterations do not wrap/overflow + let mut oflo_loop_visited = ~[]; + for uint_range_step(uint::max_value-15, uint::max_value, 4) |x| { + oflo_loop_visited.push(uint::max_value - x); + } + assert_eq!(oflo_loop_visited, ~[15, 11, 7, 3]); + + let mut oflo_loop_visited = ~[]; + for int_range_step(int::max_value-15, int::max_value, 4) |x| { + oflo_loop_visited.push(int::max_value - x); + } + assert_eq!(oflo_loop_visited, ~[15, 11, 7, 3]); + + + // range_step never passes nor visits the stop element + for int_range_step(0, 21, 3) |x| { + assert!(x < 21); + } + + // range_step_inclusive will never pass stop element, and may skip it. + let mut saw21 = false; + for uint::range_step_inclusive(0, 21, 4) |x| { + assert!(x <= 21); + if x == 21 { saw21 = true; } + } + assert!(!saw21); + let mut saw21 = false; + for int::range_step_inclusive(0, 21, 4) |x| { + assert!(x <= 21); + if x == 21 { saw21 = true; } + } + assert!(!saw21); + + // range_step_inclusive will never pass stop element, but may visit it. + let mut saw21 = false; + for uint::range_step_inclusive(0, 21, 3) |x| { + assert!(x <= 21); + println(fmt!("saw: %u", x)); + if x == 21 { saw21 = true; } + } + assert!(saw21); + let mut saw21 = false; + for int::range_step_inclusive(0, 21, 3) |x| { + assert!(x <= 21); + if x == 21 { saw21 = true; } + } + assert!(saw21); + +} From db0a13b9865510ec07f7597e11009eabc2676afc Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 1 Jul 2013 12:30:14 +0200 Subject: [PATCH 2/3] Switch over to new range_rev semantics; fix #5270. --- src/libextra/smallintmap.rs | 4 ++-- src/libstd/num/int_macros.rs | 7 ++++--- src/libstd/num/uint_macros.rs | 7 ++++--- src/libstd/run.rs | 2 +- src/libstd/trie.rs | 2 +- src/test/run-pass/num-range-rev.rs | 18 +++++++++--------- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/libextra/smallintmap.rs b/src/libextra/smallintmap.rs index d952374ee5c..329d3a454b4 100644 --- a/src/libextra/smallintmap.rs +++ b/src/libextra/smallintmap.rs @@ -159,8 +159,8 @@ impl SmallIntMap { /// Visit all key-value pairs in reverse order pub fn each_reverse<'a>(&'a self, it: &fn(uint, &'a V) -> bool) -> bool { for uint::range_rev(self.v.len(), 0) |i| { - match self.v[i - 1] { - Some(ref elt) => if !it(i - 1, elt) { return false; }, + match self.v[i] { + Some(ref elt) => if !it(i, elt) { return false; }, None => () } } diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index 4fd30be80e6..cef32b5c7e4 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -132,9 +132,10 @@ pub fn range(lo: $T, hi: $T, it: &fn($T) -> bool) -> bool { } #[inline] -/// Iterate over the range [`hi`..`lo`) +/// Iterate over the range (`hi`..`lo`] pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool { - range_step(hi, lo, -1 as $T, it) + if hi == min_value { return true; } + range_step_inclusive(hi-1, lo, -1 as $T, it) } impl Num for $T {} @@ -897,7 +898,7 @@ mod tests { for range(0,3) |i| { l.push(i); } - for range_rev(13,10) |i| { + for range_rev(14,11) |i| { l.push(i); } for range_step(20,26,2) |i| { diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index 09397ecfd77..54c1327fa93 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -132,9 +132,10 @@ pub fn range(lo: $T, hi: $T, it: &fn($T) -> bool) -> bool { } #[inline] -/// Iterate over the range [`hi`..`lo`) +/// Iterate over the range (`hi`..`lo`] pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool { - range_step(hi, lo, -1 as $T_SIGNED, it) + if hi == min_value { return true; } + range_step_inclusive(hi-1, lo, -1 as $T_SIGNED, it) } impl Num for $T {} @@ -662,7 +663,7 @@ mod tests { for range(0,3) |i| { l.push(i); } - for range_rev(13,10) |i| { + for range_rev(14,11) |i| { l.push(i); } for range_step(20,26,2) |i| { diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 17dc604a178..883870db1e6 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -669,7 +669,7 @@ fn spawn_process_os(prog: &str, args: &[~str], fail!("failure in dup3(err_fd, 2): %s", os::last_os_error()); } // close all other fds - for int::range_rev(getdtablesize() as int - 1, 2) |fd| { + for int::range_rev(getdtablesize() as int, 3) |fd| { close(fd as c_int); } diff --git a/src/libstd/trie.rs b/src/libstd/trie.rs index 8ce02d59ab1..50552fd7547 100644 --- a/src/libstd/trie.rs +++ b/src/libstd/trie.rs @@ -261,7 +261,7 @@ impl TrieNode { fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { for uint::range_rev(self.children.len(), 0) |idx| { - match self.children[idx - 1] { + match self.children[idx] { Internal(ref x) => if !x.each_reverse(|i,t| f(i,t)) { return false }, External(k, ref v) => if !f(&k, v) { return false }, Nothing => () diff --git a/src/test/run-pass/num-range-rev.rs b/src/test/run-pass/num-range-rev.rs index e65c793a7b4..7262339e431 100644 --- a/src/test/run-pass/num-range-rev.rs +++ b/src/test/run-pass/num-range-rev.rs @@ -40,13 +40,13 @@ pub fn main() { // int and uint have same result for // Sum{100 > i >= 2} == (Sum{1 <= i <= 99} - 1) == n*(n+1)/2 - 1 for n=99 let mut sum = 0u; - for uint_range_rev(99, 1) |i| { + for uint_range_rev(100, 2) |i| { sum += i; } assert_eq!(sum, 4949); let mut sum = 0i; - for int_range_rev(99, 1) |i| { + for int_range_rev(100, 2) |i| { sum += i; } assert_eq!(sum, 4949); @@ -55,17 +55,17 @@ pub fn main() { // elements are visited in correct order let primes = [2,3,5,7,11]; let mut prod = 1i; - for uint_range_rev(4, 0) |i| { + for uint_range_rev(5, 0) |i| { println(fmt!("uint 4 downto 0: %u", i)); prod *= int::pow(primes[i], i); } - assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3); + assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3*1); let mut prod = 1i; - for int_range_rev(4, 0) |i| { + for int_range_rev(5, 0) |i| { println(fmt!("int 4 downto 0: %d", i)); prod *= int::pow(primes[i], i as uint); } - assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3); + assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3*1); // range and range_rev are symmetric. @@ -74,7 +74,7 @@ pub fn main() { sum_up += i; } let mut sum_down = 0u; - for uint_range_rev(29, 9) |i| { + for uint_range_rev(30, 10) |i| { sum_down += i; } assert_eq!(sum_up, sum_down); @@ -84,7 +84,7 @@ pub fn main() { sum_up += i; } let mut sum_down = 0; - for int_range_rev(9, -21) |i| { + for int_range_rev(10, -20) |i| { sum_down += i; } assert_eq!(sum_up, sum_down); @@ -96,7 +96,7 @@ pub fn main() { } for uint_range_rev(0, 1) |_| { - // fail!("range should be empty when start-1 underflows"); + fail!("range should be empty when start-1 underflows"); } // range iterations do not wrap/underflow From 3896d46c0a6b99ef166bdc29220ef6c85ad8bc8d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 11 Jul 2013 16:56:49 +0200 Subject: [PATCH 3/3] Fix #5270: another test I did not update properly. --- src/libstd/trie.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/trie.rs b/src/libstd/trie.rs index 50552fd7547..f4597f4b08e 100644 --- a/src/libstd/trie.rs +++ b/src/libstd/trie.rs @@ -462,7 +462,7 @@ mod tests { m.insert(x, x / 2); } - let mut n = uint::max_value - 9999; + let mut n = uint::max_value - 10000; for m.each |k, v| { if n == uint::max_value - 5000 { break } assert!(n < uint::max_value - 5000); @@ -499,7 +499,7 @@ mod tests { m.insert(x, x / 2); } - let mut n = uint::max_value; + let mut n = uint::max_value - 1; for m.each_reverse |k, v| { if n == uint::max_value - 5000 { break } assert!(n > uint::max_value - 5000);