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.
This commit is contained in:
Felix S. Klock II 2013-06-27 14:20:42 +02:00
parent 41dcec2fe1
commit 3c19f1bca8
4 changed files with 384 additions and 36 deletions

View file

@ -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 {

View file

@ -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]