Skip to content

Commit 3c19f1b

Browse files
committed
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.
1 parent 41dcec2 commit 3c19f1b

File tree

4 files changed

+381
-33
lines changed

4 files changed

+381
-33
lines changed

src/libstd/num/int_macros.rs

+72-16
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,38 @@ pub static bytes : uint = ($bits / 8);
2929
pub static min_value: $T = (-1 as $T) << (bits - 1);
3030
pub static max_value: $T = min_value - 1 as $T;
3131

32+
enum Range { Closed, HalfOpen }
33+
34+
#[inline]
3235
///
33-
/// Iterate over the range [`lo`..`hi`)
36+
/// Iterate through a range with a given step value.
3437
///
35-
/// # Arguments
38+
/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed;
39+
/// otherwise `term` denotes the half-open interval `[stop-step,stop)`.
40+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
41+
/// `x_j == start + step*j`, and `x_n` lies in the interval `term`.
3642
///
37-
/// * `lo` - lower bound, inclusive
38-
/// * `hi` - higher bound, exclusive
39-
///
40-
/// # Examples
41-
/// ~~~
42-
/// let mut sum = 0;
43-
/// for int::range(1, 5) |i| {
44-
/// sum += i;
45-
/// }
46-
/// assert!(sum == 10);
47-
/// ~~~
43+
/// If no such nonnegative integer `n` exists, then the iteration range
44+
/// is empty.
4845
///
49-
#[inline]
50-
pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
46+
fn range_step_core(start: $T, stop: $T, step: $T, r: Range, it: &fn($T) -> bool) -> bool {
5147
let mut i = start;
5248
if step == 0 {
5349
fail!(~"range_step called with step == 0");
50+
} else if step == (1 as $T) { // elide bounds check to tighten loop
51+
while i < stop {
52+
if !it(i) { return false; }
53+
// no need for overflow check;
54+
// cannot have i + 1 > max_value because i < stop <= max_value
55+
i += (1 as $T);
56+
}
57+
} else if step == (-1 as $T) { // elide bounds check to tighten loop
58+
while i > stop {
59+
if !it(i) { return false; }
60+
// no need for underflow check;
61+
// cannot have i - 1 < min_value because i > stop >= min_value
62+
i -= (1 as $T);
63+
}
5464
} else if step > 0 { // ascending
5565
while i < stop {
5666
if !it(i) { return false; }
@@ -66,9 +76,55 @@ pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
6676
i += step;
6777
}
6878
}
69-
return true;
79+
match r {
80+
HalfOpen => return true,
81+
Closed => return (i != stop || it(i))
82+
}
83+
}
84+
85+
#[inline]
86+
///
87+
/// Iterate through the range [`start`..`stop`) with a given step value.
88+
///
89+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
90+
/// * `x_i == start + step*i`, and
91+
/// * `n` is the greatest nonnegative integer such that `x_n < stop`
92+
///
93+
/// (If no such `n` exists, then the iteration range is empty.)
94+
///
95+
/// # Arguments
96+
///
97+
/// * `start` - lower bound, inclusive
98+
/// * `stop` - higher bound, exclusive
99+
///
100+
/// # Examples
101+
/// ~~~
102+
/// let mut sum = 0;
103+
/// for int::range(1, 5) |i| {
104+
/// sum += i;
105+
/// }
106+
/// assert!(sum == 10);
107+
/// ~~~
108+
///
109+
pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
110+
range_step_core(start, stop, step, HalfOpen, it)
111+
}
112+
113+
#[inline]
114+
///
115+
/// Iterate through a range with a given step value.
116+
///
117+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
118+
/// `x_i == start + step*i` and `x_n <= last < step + x_n`.
119+
///
120+
/// (If no such nonnegative integer `n` exists, then the iteration
121+
/// range is empty.)
122+
///
123+
pub fn range_step_inclusive(start: $T, last: $T, step: $T, it: &fn($T) -> bool) -> bool {
124+
range_step_core(start, last, step, Closed, it)
70125
}
71126
127+
72128
#[inline]
73129
/// Iterate over the range [`lo`..`hi`)
74130
pub fn range(lo: $T, hi: $T, it: &fn($T) -> bool) -> bool {

src/libstd/num/uint_macros.rs

+76-17
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,99 @@ pub static bytes : uint = ($bits / 8);
3030
pub static min_value: $T = 0 as $T;
3131
pub static max_value: $T = 0 as $T - 1 as $T;
3232

33+
enum Range { Closed, HalfOpen }
34+
3335
#[inline]
34-
/**
35-
* Iterate through a range with a given step value.
36-
*
37-
* # Examples
38-
* ~~~ {.rust}
39-
* let nums = [1,2,3,4,5,6,7];
40-
*
41-
* for uint::range_step(0, nums.len() - 1, 2) |i| {
42-
* println(fmt!("%d & %d", nums[i], nums[i+1]));
43-
* }
44-
* ~~~
45-
*/
46-
pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
36+
///
37+
/// Iterate through a range with a given step value.
38+
///
39+
/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed;
40+
/// otherwise `term` denotes the half-open interval `[stop-step,stop)`.
41+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
42+
/// `x_j == start + step*j`, and `x_n` lies in the interval `term`.
43+
///
44+
/// If no such nonnegative integer `n` exists, then the iteration range
45+
/// is empty.
46+
///
47+
fn range_step_core(start: $T, stop: $T, step: $T_SIGNED, r: Range, it: &fn($T) -> bool) -> bool {
4748
let mut i = start;
4849
if step == 0 {
4950
fail!("range_step called with step == 0");
50-
}
51-
if step >= 0 {
51+
} else if step == (1 as $T_SIGNED) { // elide bounds check to tighten loop
52+
while i < stop {
53+
if !it(i) { return false; }
54+
// no need for overflow check;
55+
// cannot have i + 1 > max_value because i < stop <= max_value
56+
i += (1 as $T);
57+
}
58+
} else if step == (-1 as $T_SIGNED) { // elide bounds check to tighten loop
59+
while i > stop {
60+
if !it(i) { return false; }
61+
// no need for underflow check;
62+
// cannot have i - 1 < min_value because i > stop >= min_value
63+
i -= (1 as $T);
64+
}
65+
} else if step > 0 { // ascending
5266
while i < stop {
5367
if !it(i) { return false; }
5468
// avoiding overflow. break if i + step > max_value
5569
if i > max_value - (step as $T) { return true; }
5670
i += step as $T;
5771
}
58-
} else {
72+
} else { // descending
5973
while i > stop {
6074
if !it(i) { return false; }
6175
// avoiding underflow. break if i + step < min_value
6276
if i < min_value + ((-step) as $T) { return true; }
6377
i -= -step as $T;
6478
}
6579
}
66-
return true;
80+
match r {
81+
HalfOpen => return true,
82+
Closed => return (i != stop || it(i))
83+
}
84+
}
85+
86+
#[inline]
87+
///
88+
/// Iterate through the range [`start`..`stop`) with a given step value.
89+
///
90+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
91+
/// - `x_i == start + step*i`, and
92+
/// - `n` is the greatest nonnegative integer such that `x_n < stop`
93+
///
94+
/// (If no such `n` exists, then the iteration range is empty.)
95+
///
96+
/// # Arguments
97+
///
98+
/// * `start` - lower bound, inclusive
99+
/// * `stop` - higher bound, exclusive
100+
///
101+
/// # Examples
102+
/// ~~~ {.rust}
103+
/// let nums = [1,2,3,4,5,6,7];
104+
///
105+
/// for uint::range_step(0, nums.len() - 1, 2) |i| {
106+
/// println(fmt!("%d & %d", nums[i], nums[i+1]));
107+
/// }
108+
/// ~~~
109+
///
110+
pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
111+
range_step_core(start, stop, step, HalfOpen, it)
112+
}
113+
114+
#[inline]
115+
///
116+
/// Iterate through a range with a given step value.
117+
///
118+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
119+
/// `x_i == start + step*i` and `x_n <= last < step + x_n`.
120+
///
121+
/// (If no such nonnegative integer `n` exists, then the iteration
122+
/// range is empty.)
123+
///
124+
pub fn range_step_inclusive(start: $T, last: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
125+
range_step_core(start, last, step, Closed, it)
67126
}
68127

69128
#[inline]

src/test/run-pass/num-range-rev.rs

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::int;
12+
use std::uint;
13+
14+
fn uint_range(lo: uint, hi: uint, it: &fn(uint) -> bool) -> bool {
15+
uint::range(lo, hi, it)
16+
}
17+
18+
fn int_range(lo: int, hi: int, it: &fn(int) -> bool) -> bool {
19+
int::range(lo, hi, it)
20+
}
21+
22+
fn uint_range_rev(hi: uint, lo: uint, it: &fn(uint) -> bool) -> bool {
23+
uint::range_rev(hi, lo, it)
24+
}
25+
26+
fn int_range_rev(hi: int, lo: int, it: &fn(int) -> bool) -> bool {
27+
int::range_rev(hi, lo, it)
28+
}
29+
30+
fn int_range_step(a: int, b: int, step: int, it: &fn(int) -> bool) -> bool {
31+
int::range_step(a, b, step, it)
32+
}
33+
34+
fn uint_range_step(a: uint, b: uint, step: int, it: &fn(uint) -> bool) -> bool {
35+
uint::range_step(a, b, step, it)
36+
}
37+
38+
39+
pub fn main() {
40+
// int and uint have same result for
41+
// Sum{100 > i >= 2} == (Sum{1 <= i <= 99} - 1) == n*(n+1)/2 - 1 for n=99
42+
let mut sum = 0u;
43+
for uint_range_rev(99, 1) |i| {
44+
sum += i;
45+
}
46+
assert_eq!(sum, 4949);
47+
48+
let mut sum = 0i;
49+
for int_range_rev(99, 1) |i| {
50+
sum += i;
51+
}
52+
assert_eq!(sum, 4949);
53+
54+
55+
// elements are visited in correct order
56+
let primes = [2,3,5,7,11];
57+
let mut prod = 1i;
58+
for uint_range_rev(4, 0) |i| {
59+
println(fmt!("uint 4 downto 0: %u", i));
60+
prod *= int::pow(primes[i], i);
61+
}
62+
assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3);
63+
let mut prod = 1i;
64+
for int_range_rev(4, 0) |i| {
65+
println(fmt!("int 4 downto 0: %d", i));
66+
prod *= int::pow(primes[i], i as uint);
67+
}
68+
assert_eq!(prod, 11*11*11*11*7*7*7*5*5*3);
69+
70+
71+
// range and range_rev are symmetric.
72+
let mut sum_up = 0u;
73+
for uint_range(10, 30) |i| {
74+
sum_up += i;
75+
}
76+
let mut sum_down = 0u;
77+
for uint_range_rev(29, 9) |i| {
78+
sum_down += i;
79+
}
80+
assert_eq!(sum_up, sum_down);
81+
82+
let mut sum_up = 0;
83+
for int_range(-20, 10) |i| {
84+
sum_up += i;
85+
}
86+
let mut sum_down = 0;
87+
for int_range_rev(9, -21) |i| {
88+
sum_down += i;
89+
}
90+
assert_eq!(sum_up, sum_down);
91+
92+
93+
// empty ranges
94+
for int_range_rev(10, 10) |_| {
95+
fail!("range should be empty when start == stop");
96+
}
97+
98+
for uint_range_rev(0, 1) |_| {
99+
// fail!("range should be empty when start-1 underflows");
100+
}
101+
102+
// range iterations do not wrap/underflow
103+
let mut uflo_loop_visited = ~[];
104+
for int_range_step(int::min_value+15, int::min_value, -4) |x| {
105+
uflo_loop_visited.push(x - int::min_value);
106+
}
107+
assert_eq!(uflo_loop_visited, ~[15, 11, 7, 3]);
108+
109+
let mut uflo_loop_visited = ~[];
110+
for uint_range_step(uint::min_value+15, uint::min_value, -4) |x| {
111+
uflo_loop_visited.push(x - uint::min_value);
112+
}
113+
assert_eq!(uflo_loop_visited, ~[15, 11, 7, 3]);
114+
}

0 commit comments

Comments
 (0)