Skip to content

Commit 64946b4

Browse files
committed
Report an estimate of remaining steps.
1 parent 5eafc84 commit 64946b4

File tree

2 files changed

+86
-16
lines changed

2 files changed

+86
-16
lines changed

src/least_satisfying.rs

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ use std::fmt;
44
pub fn least_satisfying<T, P>(slice: &[T], mut predicate: P) -> usize
55
where
66
T: fmt::Display + fmt::Debug,
7-
P: FnMut(&T) -> Satisfies,
7+
P: FnMut(&T, usize, usize) -> Satisfies,
88
{
99
let mut cache = BTreeMap::new();
10-
let mut predicate = |idx: usize| *cache.entry(idx).or_insert_with(|| predicate(&slice[idx]));
10+
let mut predicate = |idx: usize, remaining, estimate| {
11+
*cache
12+
.entry(idx)
13+
.or_insert_with(|| predicate(&slice[idx], remaining, estimate))
14+
};
1115
let mut unknown_ranges: Vec<(usize, usize)> = Vec::new();
1216
// presume that the slice starts with a no
1317
// this should be tested before call
@@ -40,7 +44,11 @@ where
4044
}
4145
}
4246

43-
let r = predicate(next);
47+
let mut range = lm_yes - rm_no + 1;
48+
// FIXME: This does not consider unknown_ranges.
49+
let mut remaining = range / 2;
50+
let estimate = estimate_steps(range);
51+
let r = predicate(next, remaining, estimate);
4452
match r {
4553
Satisfies::Yes => {
4654
lm_yes = next;
@@ -52,12 +60,20 @@ where
5260
}
5361
Satisfies::Unknown => {
5462
let mut left = next;
55-
while left > 0 && predicate(left) == Satisfies::Unknown {
63+
while left > 0
64+
&& predicate(left, remaining, estimate_steps(range)) == Satisfies::Unknown
65+
{
5666
left -= 1;
67+
remaining = remaining.saturating_sub(1);
68+
range = range.saturating_sub(1);
5769
}
5870
let mut right = next;
59-
while right + 1 < slice.len() && predicate(right) == Satisfies::Unknown {
71+
while right + 1 < slice.len()
72+
&& predicate(right, remaining, estimate_steps(range)) == Satisfies::Unknown
73+
{
6074
right += 1;
75+
remaining = remaining.saturating_sub(1);
76+
range = range.saturating_sub(1);
6177
}
6278
unknown_ranges.push((left + 1, right - 1));
6379
next = left;
@@ -66,10 +82,33 @@ where
6682
}
6783
}
6884

85+
fn estimate_steps(range: usize) -> usize {
86+
// Replace with int_log when it is stabilized.
87+
let log2 = |mut n| {
88+
let mut r = 0;
89+
while n > 1 {
90+
r += 1;
91+
n >>= 1;
92+
}
93+
r
94+
};
95+
if range < 3 {
96+
return 0;
97+
}
98+
let n = log2(range);
99+
let e = 1 << n;
100+
let x = range - e;
101+
if e < 3 * x {
102+
n
103+
} else {
104+
n - 1
105+
}
106+
}
107+
69108
#[cfg(test)]
70109
mod tests {
71110
use super::Satisfies::{No, Unknown, Yes};
72-
use super::{least_satisfying, Satisfies};
111+
use super::{estimate_steps, least_satisfying, Satisfies};
73112
use quickcheck::{QuickCheck, TestResult};
74113

75114
fn prop(xs: Vec<Option<bool>>) -> TestResult {
@@ -89,59 +128,62 @@ mod tests {
89128
}
90129
}
91130

92-
let res = least_satisfying(&satisfies_v, |i| *i);
131+
let res = least_satisfying(&satisfies_v, |i, _, _| *i);
93132
let exp = first_yes.unwrap();
94133
TestResult::from_bool(res == exp)
95134
}
96135

97136
#[test]
98137
fn least_satisfying_1() {
99138
assert_eq!(
100-
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i| *i),
139+
least_satisfying(&[No, Unknown, Unknown, No, Yes], |i, _, _| *i),
101140
4
102141
);
103142
}
104143

105144
#[test]
106145
fn least_satisfying_2() {
107146
assert_eq!(
108-
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i| *i),
147+
least_satisfying(&[No, Unknown, Yes, Unknown, Yes], |i, _, _| *i),
109148
2
110149
);
111150
}
112151

113152
#[test]
114153
fn least_satisfying_3() {
115-
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i| *i), 4);
154+
assert_eq!(least_satisfying(&[No, No, No, No, Yes], |i, _, _| *i), 4);
116155
}
117156

118157
#[test]
119158
fn least_satisfying_4() {
120-
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i| *i), 2);
159+
assert_eq!(least_satisfying(&[No, No, Yes, Yes, Yes], |i, _, _| *i), 2);
121160
}
122161

123162
#[test]
124163
fn least_satisfying_5() {
125-
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i| *i), 1);
164+
assert_eq!(least_satisfying(&[No, Yes, Yes, Yes, Yes], |i, _, _| *i), 1);
126165
}
127166

128167
#[test]
129168
fn least_satisfying_6() {
130169
assert_eq!(
131-
least_satisfying(&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes], |i| *i),
170+
least_satisfying(
171+
&[No, Yes, Yes, Unknown, Unknown, Yes, Unknown, Yes],
172+
|i, _, _| *i
173+
),
132174
1
133175
);
134176
}
135177

136178
#[test]
137179
fn least_satisfying_7() {
138-
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i| *i), 1);
180+
assert_eq!(least_satisfying(&[No, Yes, Unknown, Yes], |i, _, _| *i), 1);
139181
}
140182

141183
#[test]
142184
fn least_satisfying_8() {
143185
assert_eq!(
144-
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i| *i),
186+
least_satisfying(&[No, Unknown, No, No, Unknown, Yes, Yes], |i, _, _| *i),
145187
5
146188
);
147189
}
@@ -150,6 +192,22 @@ mod tests {
150192
fn qc_prop() {
151193
QuickCheck::new().quickcheck(prop as fn(_) -> _);
152194
}
195+
196+
#[test]
197+
fn estimates() {
198+
for (n, expect) in &[
199+
(0, 0),
200+
(1, 0),
201+
(2, 0),
202+
(3, 1),
203+
(4, 1),
204+
(5, 1),
205+
(6, 2),
206+
(150, 6),
207+
] {
208+
assert_eq!(estimate_steps(*n), *expect);
209+
}
210+
}
153211
}
154212

155213
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,11 @@ impl Config {
637637
eprintln!("searched toolchains {} through {}", start, end);
638638

639639
if toolchains[*found] == *toolchains.last().unwrap() {
640+
// FIXME: Ideally the BisectionResult would contain the final result.
641+
// This ends up testing a toolchain that was already tested.
642+
// I believe this is one of the duplicates mentioned in
643+
// https://github.com/rust-lang/cargo-bisect-rustc/issues/85
644+
eprintln!("checking last toolchain to determine final result");
640645
let t = &toolchains[*found];
641646
let r = match t.install(&self.client, dl_spec) {
642647
Ok(()) => {
@@ -839,7 +844,10 @@ impl Config {
839844
}
840845

841846
fn bisect_to_regression(&self, toolchains: &[Toolchain], dl_spec: &DownloadParams) -> usize {
842-
least_satisfying(toolchains, |t| {
847+
least_satisfying(toolchains, |t, remaining, estimate| {
848+
eprintln!(
849+
"{remaining} versions remaining to test after this (roughly {estimate} steps)"
850+
);
843851
self.install_and_test(t, dl_spec)
844852
.unwrap_or(Satisfies::Unknown)
845853
})
@@ -925,6 +933,7 @@ impl Config {
925933
);
926934
}
927935

936+
eprintln!("checking the start range to find a passing nightly");
928937
match self.install_and_test(&t, &dl_spec) {
929938
Ok(r) => {
930939
// If Satisfies::No, then the regression was not identified in this nightly.
@@ -971,6 +980,7 @@ impl Config {
971980
t_end.std_targets.sort();
972981
t_end.std_targets.dedup();
973982

983+
eprintln!("checking the end range to verify it does not pass");
974984
let result_nightly = self.install_and_test(&t_end, &dl_spec)?;
975985
// The regression was not identified in this nightly.
976986
if result_nightly == Satisfies::No {
@@ -1117,6 +1127,7 @@ impl Config {
11171127

11181128
if !toolchains.is_empty() {
11191129
// validate commit at start of range
1130+
eprintln!("checking the start range to verify it passes");
11201131
let start_range_result = self.install_and_test(&toolchains[0], &dl_spec)?;
11211132
if start_range_result == Satisfies::Yes {
11221133
bail!(
@@ -1126,6 +1137,7 @@ impl Config {
11261137
}
11271138

11281139
// validate commit at end of range
1140+
eprintln!("checking the end range to verify it does not pass");
11291141
let end_range_result =
11301142
self.install_and_test(&toolchains[toolchains.len() - 1], &dl_spec)?;
11311143
if end_range_result == Satisfies::No {

0 commit comments

Comments
 (0)