Skip to content

Commit d31d8e3

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 6a2ad08 commit d31d8e3

File tree

4 files changed

+381
-33
lines changed

4 files changed

+381
-33
lines changed

src/libstd/num/int_macros.rs

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,28 +83,38 @@ pub fn ge(x: $T, y: $T) -> bool { x >= y }
8383
#[inline]
8484
pub fn gt(x: $T, y: $T) -> bool { x > y }
8585

86+
enum Range { Closed, HalfOpen }
87+
88+
#[inline]
8689
///
87-
/// Iterate over the range [`lo`..`hi`)
90+
/// Iterate through a range with a given step value.
8891
///
89-
/// # Arguments
92+
/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed;
93+
/// otherwise `term` denotes the half-open interval `[stop-step,stop)`.
94+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
95+
/// `x_j == start + step*j`, and `x_n` lies in the interval `term`.
9096
///
91-
/// * `lo` - lower bound, inclusive
92-
/// * `hi` - higher bound, exclusive
93-
///
94-
/// # Examples
95-
/// ~~~
96-
/// let mut sum = 0;
97-
/// for int::range(1, 5) |i| {
98-
/// sum += i;
99-
/// }
100-
/// assert!(sum == 10);
101-
/// ~~~
97+
/// If no such nonnegative integer `n` exists, then the iteration range
98+
/// is empty.
10299
///
103-
#[inline]
104-
pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
100+
fn range_step_core(start: $T, stop: $T, step: $T, r: Range, it: &fn($T) -> bool) -> bool {
105101
let mut i = start;
106102
if step == 0 {
107103
fail!(~"range_step called with step == 0");
104+
} else if step == (1 as $T) { // elide bounds check to tighten loop
105+
while i < stop {
106+
if !it(i) { return false; }
107+
// no need for overflow check;
108+
// cannot have i + 1 > max_value because i < stop <= max_value
109+
i += (1 as $T);
110+
}
111+
} else if step == (-1 as $T) { // elide bounds check to tighten loop
112+
while i > stop {
113+
if !it(i) { return false; }
114+
// no need for underflow check;
115+
// cannot have i - 1 < min_value because i > stop >= min_value
116+
i -= (1 as $T);
117+
}
108118
} else if step > 0 { // ascending
109119
while i < stop {
110120
if !it(i) { return false; }
@@ -120,9 +130,55 @@ pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
120130
i += step;
121131
}
122132
}
123-
return true;
133+
match r {
134+
HalfOpen => return true,
135+
Closed => return (i != stop || it(i))
136+
}
137+
}
138+
139+
#[inline]
140+
///
141+
/// Iterate through the range [`start`..`stop`) with a given step value.
142+
///
143+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
144+
/// * `x_i == start + step*i`, and
145+
/// * `n` is the greatest nonnegative integer such that `x_n < stop`
146+
///
147+
/// (If no such `n` exists, then the iteration range is empty.)
148+
///
149+
/// # Arguments
150+
///
151+
/// * `start` - lower bound, inclusive
152+
/// * `stop` - higher bound, exclusive
153+
///
154+
/// # Examples
155+
/// ~~~
156+
/// let mut sum = 0;
157+
/// for int::range(1, 5) |i| {
158+
/// sum += i;
159+
/// }
160+
/// assert!(sum == 10);
161+
/// ~~~
162+
///
163+
pub fn range_step(start: $T, stop: $T, step: $T, it: &fn($T) -> bool) -> bool {
164+
range_step_core(start, stop, step, HalfOpen, it)
165+
}
166+
167+
#[inline]
168+
///
169+
/// Iterate through a range with a given step value.
170+
///
171+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
172+
/// `x_i == start + step*i` and `x_n <= last < step + x_n`.
173+
///
174+
/// (If no such nonnegative integer `n` exists, then the iteration
175+
/// range is empty.)
176+
///
177+
pub fn range_step_inclusive(start: $T, last: $T, step: $T, it: &fn($T) -> bool) -> bool {
178+
range_step_core(start, last, step, Closed, it)
124179
}
125180
181+
126182
#[inline]
127183
/// Iterate over the range [`lo`..`hi`)
128184
pub fn range(lo: $T, hi: $T, it: &fn($T) -> bool) -> bool {

src/libstd/num/uint_macros.rs

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,40 +64,99 @@ pub fn ge(x: $T, y: $T) -> bool { x >= y }
6464
#[inline]
6565
pub fn gt(x: $T, y: $T) -> bool { x > y }
6666

67+
enum Range { Closed, HalfOpen }
68+
6769
#[inline]
68-
/**
69-
* Iterate through a range with a given step value.
70-
*
71-
* # Examples
72-
* ~~~ {.rust}
73-
* let nums = [1,2,3,4,5,6,7];
74-
*
75-
* for uint::range_step(0, nums.len() - 1, 2) |i| {
76-
* println(fmt!("%d & %d", nums[i], nums[i+1]));
77-
* }
78-
* ~~~
79-
*/
80-
pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
70+
///
71+
/// Iterate through a range with a given step value.
72+
///
73+
/// Let `term` denote the closed interval `[stop-step,stop]` if `r` is Closed;
74+
/// otherwise `term` denotes the half-open interval `[stop-step,stop)`.
75+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
76+
/// `x_j == start + step*j`, and `x_n` lies in the interval `term`.
77+
///
78+
/// If no such nonnegative integer `n` exists, then the iteration range
79+
/// is empty.
80+
///
81+
fn range_step_core(start: $T, stop: $T, step: $T_SIGNED, r: Range, it: &fn($T) -> bool) -> bool {
8182
let mut i = start;
8283
if step == 0 {
8384
fail!("range_step called with step == 0");
84-
}
85-
if step >= 0 {
85+
} else if step == (1 as $T_SIGNED) { // elide bounds check to tighten loop
86+
while i < stop {
87+
if !it(i) { return false; }
88+
// no need for overflow check;
89+
// cannot have i + 1 > max_value because i < stop <= max_value
90+
i += (1 as $T);
91+
}
92+
} else if step == (-1 as $T_SIGNED) { // elide bounds check to tighten loop
93+
while i > stop {
94+
if !it(i) { return false; }
95+
// no need for underflow check;
96+
// cannot have i - 1 < min_value because i > stop >= min_value
97+
i -= (1 as $T);
98+
}
99+
} else if step > 0 { // ascending
86100
while i < stop {
87101
if !it(i) { return false; }
88102
// avoiding overflow. break if i + step > max_value
89103
if i > max_value - (step as $T) { return true; }
90104
i += step as $T;
91105
}
92-
} else {
106+
} else { // descending
93107
while i > stop {
94108
if !it(i) { return false; }
95109
// avoiding underflow. break if i + step < min_value
96110
if i < min_value + ((-step) as $T) { return true; }
97111
i -= -step as $T;
98112
}
99113
}
100-
return true;
114+
match r {
115+
HalfOpen => return true,
116+
Closed => return (i != stop || it(i))
117+
}
118+
}
119+
120+
#[inline]
121+
///
122+
/// Iterate through the range [`start`..`stop`) with a given step value.
123+
///
124+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
125+
/// - `x_i == start + step*i`, and
126+
/// - `n` is the greatest nonnegative integer such that `x_n < stop`
127+
///
128+
/// (If no such `n` exists, then the iteration range is empty.)
129+
///
130+
/// # Arguments
131+
///
132+
/// * `start` - lower bound, inclusive
133+
/// * `stop` - higher bound, exclusive
134+
///
135+
/// # Examples
136+
/// ~~~ {.rust}
137+
/// let nums = [1,2,3,4,5,6,7];
138+
///
139+
/// for uint::range_step(0, nums.len() - 1, 2) |i| {
140+
/// println(fmt!("%d & %d", nums[i], nums[i+1]));
141+
/// }
142+
/// ~~~
143+
///
144+
pub fn range_step(start: $T, stop: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
145+
range_step_core(start, stop, step, HalfOpen, it)
146+
}
147+
148+
#[inline]
149+
///
150+
/// Iterate through a range with a given step value.
151+
///
152+
/// Iterates through the range `[x_0, x_1, ..., x_n]` where
153+
/// `x_i == start + step*i` and `x_n <= last < step + x_n`.
154+
///
155+
/// (If no such nonnegative integer `n` exists, then the iteration
156+
/// range is empty.)
157+
///
158+
pub fn range_step_inclusive(start: $T, last: $T, step: $T_SIGNED, it: &fn($T) -> bool) -> bool {
159+
range_step_core(start, last, step, Closed, it)
101160
}
102161

103162
#[inline]

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

Lines changed: 114 additions & 0 deletions
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)