use std::{cmp::Ordering, ops::Index};
use Ordering::{Equal, Greater, Less};
pub trait BinarySearch: Index<usize> {
fn binary_search_with_cmp<E, F>(
&self,
start: usize,
end: usize,
target: &E,
cmp: F,
) -> Result<usize, Option<usize>>
where
F: Fn(&<Self as Index<usize>>::Output, &E) -> Ordering;
}
impl<T> BinarySearch for Vec<T> {
fn binary_search_with_cmp<E, F>(
&self,
start: usize,
end_exclusive: usize,
target: &E,
cmp: F,
) -> Result<usize, Option<usize>>
where
F: Fn(&<Self as Index<usize>>::Output, &E) -> Ordering, {
if start >= end_exclusive {
return Err(None);
}
let mut start = start;
let mut end = end_exclusive - 1;
if cmp(&self[start], target) == Ordering::Greater {
return Err(Some(start));
}
if cmp(&self[end], target) == Ordering::Less {
return Err(Some(end + 1));
}
let mut mid = (start + end) / 2;
while end > start {
match cmp(&self[mid], target) {
Less => {
start = mid + 1;
mid = (start + end) / 2;
}
Greater => {
end = mid;
mid = (start + end) / 2;
}
Equal => return Ok(mid),
};
}
match cmp(&self[mid], target) {
Equal => Ok(mid),
Less => Err(Some(mid + 1)),
Greater => Err(Some(mid)),
}
}
}
#[cfg(test)]
mod tests {
use super::BinarySearch;
#[test]
fn test_vec_binary_search() {
let v = vec![1, 2, 3, 6, 29, 43, 69, 100, 340];
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &30, |x, y| x.cmp(y)),
Err(Some(5))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &-10, |x, y| x.cmp(y)),
Err(Some(0))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &0, |x, y| x.cmp(y)),
Err(Some(0))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &341, |x, y| x.cmp(y)),
Err(Some(v.len()))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &1000, |x, y| x.cmp(y)),
Err(Some(v.len()))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &1, |x, y| x.cmp(y)),
Ok(0)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &2, |x, y| x.cmp(y)),
Ok(1)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &3, |x, y| x.cmp(y)),
Ok(2)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &6, |x, y| x.cmp(y)),
Ok(3)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &29, |x, y| x.cmp(y)),
Ok(4)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &43, |x, y| x.cmp(y)),
Ok(5)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &69, |x, y| x.cmp(y)),
Ok(6)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &100, |x, y| x.cmp(y)),
Ok(7)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &340, |x, y| x.cmp(y)),
Ok(8)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &6, |x, y| x.cmp(y)),
Ok(3)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &29, |x, y| x.cmp(y)),
Ok(4)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &43, |x, y| x.cmp(y)),
Ok(5)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &340, |x, y| x.cmp(y)),
Err(Some(6))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &2, |x, y| x.cmp(y)),
Err(Some(3))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &3, |x, y| x.cmp(y)),
Err(Some(3))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 3, 6, &69, |x, y| x.cmp(y)),
Err(Some(6))
);
let v = vec![-10];
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &9, |x, y| x.cmp(y)),
Err(Some(1))
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &-10, |x, y| x.cmp(y)),
Ok(0)
);
let v = vec![-10, 1];
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &1, |x, y| x.cmp(y)),
Ok(1)
);
assert_eq!(
BinarySearch::binary_search_with_cmp(&v, 0, v.len(), &-10, |x, y| x.cmp(y)),
Ok(0)
);
}
}