1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use std::ops::{Shr, Add, BitAnd};
pub trait Betweenable where Self: Copy {
fn between(x: Self, y: Self) -> Option<Self>;
}
impl<X> Betweenable for X
where
X: Copy,
X: Shr<i8, Output=X>,
X: Add<X, Output=X>,
X: BitAnd<X, Output=X>,
X: From<u8>,
X: PartialOrd {
fn between(low: Self, high: Self) -> Option<Self> {
let one = X::from(1);
if high <= low + one {
None
} else {
let mid = (low >> 1) + (high >> 1) + (low & high & one);
Some(mid)
}
}
}
pub enum Direction<A, B> {
Low(A),
High(B),
}
pub fn binary_search<X, A, B, F>(
low: (X, A),
high: (X, B),
f: F,
) -> ((X, A), (X, B))
where
X: Betweenable,
F: Fn(X) -> Direction<A, B> {
match X::between(low.0, high.0) {
None => {
(low, high)
},
Some(x) => {
match (f)(x) {
Direction::Low(low) => {
binary_search((x, low), high, f)
},
Direction::High(high) => {
binary_search(low, (x, high), f)
},
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_usize() {
assert_eq!(usize::between(1, 0), None);
assert_eq!(usize::between(1, 1), None);
assert_eq!(usize::between(1, 2), None);
assert_eq!(usize::between(1, 3), Some(2));
assert_eq!(
usize::between(usize::max_value()-3, usize::max_value()-1),
Some(usize::max_value()-2),
);
assert_eq!(
usize::between(usize::max_value()-2, usize::max_value()),
Some(usize::max_value()-1),
);
}
#[test]
fn binary_search_test() {
let result =
binary_search((1 as usize, ()), (100, ()), |x| {
if x < 23 {
Direction::Low(())
} else {
Direction::High(())
}
});
assert_eq!(result, ((22, ()), (23, ())))
}
}