Skip to content

Commit

Permalink
auto merge of #7684 : pnkfelix/rust/fsk-invert-range-rev-halfclosedne…
Browse files Browse the repository at this point in the history
…ss-issue5270-2ndpr, r=cmr

Changes int/uint range_rev to iterate over range `(hi,lo]` instead of `[hi,lo)`.

Fix #5270.

Also:
* Adds unit tests for int/uint range functions
* Updates the uses of `range_rev` to account for the new semantics.  (Note that pretty much all of the updates there were strict improvements to the code in question; yay!)
* Exposes new function, `range_step_inclusive`, which does the range `[hi,lo]`, (at least when `hi-lo` is a multiple of the `step` parameter).
* Special-cases when `|step| == 1` removing unnecessary bounds-check.  (I did not check whether LLVM was already performing this optimization; I figure it would be a net win to not leave that analysis to the compiler.  If reviewer objects, I can easily remove that from the refactored code.)

(This pull request is a rebased version of PR #7524, which went stale due to recent unrelated changes to num libraries.)
  • Loading branch information
bors committed Jul 16, 2013
2 parents 9db1903 + 3896d46 commit 53e934c
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 45 deletions.
4 changes: 2 additions & 2 deletions src/libextra/smallintmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ impl<V> SmallIntMap<V> {
/// 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 => ()
}
}
Expand Down
95 changes: 76 additions & 19 deletions src/libstd/num/int_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

enum Range { Closed, HalfOpen }

#[inline]
///
/// Iterate over the range [`lo`..`hi`)
/// Iterate through a range with a given step value.
///
/// # Arguments
/// 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`.
///
/// * `lo` - lower bound, inclusive
/// * `hi` - higher bound, exclusive
///
/// # Examples
/// ~~~
/// let mut sum = 0;
/// for int::range(1, 5) |i| {
/// sum += i;
/// }
/// assert!(sum == 10);
/// ~~~
/// If no such nonnegative integer `n` exists, then the iteration range
/// is empty.
///
#[inline]
pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
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; }
Expand All @@ -66,19 +76,66 @@ 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 {
range_step(lo, hi, 1 as $T, it)
}
#[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 {}
Expand Down Expand Up @@ -841,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| {
Expand Down
100 changes: 80 additions & 20 deletions src/libstd/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,40 +30,99 @@ 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
if i < min_value + ((-step) as $T) { return true; }
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]
Expand All @@ -73,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 {}
Expand Down Expand Up @@ -603,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| {
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/libstd/trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ impl<T> TrieNode<T> {

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 => ()
Expand Down Expand Up @@ -488,7 +488,7 @@ mod test_map {
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);
Expand Down Expand Up @@ -525,7 +525,7 @@ mod test_map {
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);
Expand Down
Loading

0 comments on commit 53e934c

Please sign in to comment.