use crate::{InvalidOrderError, OrderResult};
use core::cmp::Ordering;
pub trait TryBinarySearch<T> {
#[inline]
fn try_binary_search(&self, x: &T) -> OrderResult<Result<usize, usize>>
where
T: PartialOrd<T>,
{
self.try_binary_search_by(|a| a.partial_cmp(x))
}
fn try_binary_search_by<F>(&self, compare: F) -> OrderResult<Result<usize, usize>>
where
F: FnMut(&T) -> Option<Ordering>;
#[inline]
fn try_binary_search_by_key<K, F>(&self, b: &K, f: F) -> OrderResult<Result<usize, usize>>
where
F: FnMut(&T) -> Option<K>,
K: PartialOrd<K>,
{
let mut fk = f;
self.try_binary_search_by(|a| fk(a)?.partial_cmp(b))
}
}
impl<T> TryBinarySearch<T> for [T] {
#[inline]
fn try_binary_search_by<F>(&self, compare: F) -> OrderResult<Result<usize, usize>>
where
F: FnMut(&T) -> Option<Ordering>,
{
try_binary_search_by_inner(self, compare).ok_or(InvalidOrderError)
}
}
fn try_binary_search_by_inner<T, F>(slice: &[T], mut compare: F) -> Option<Result<usize, usize>>
where
F: FnMut(&T) -> Option<Ordering>,
{
let mut size = slice.len();
let mut left = 0;
let mut right = size;
while size > 0 {
let mid = left + size / 2;
let cmp = compare(unsafe { slice.get_unchecked(mid) })?;
if cmp == Ordering::Less {
left = mid + 1;
} else if cmp == Ordering::Greater {
right = mid;
} else {
return Some(Ok(mid));
}
size = right - left;
}
Some(Err(left))
}
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use crate::*;
use rand::distributions::Standard;
use rand::prelude::*;
use std::vec::Vec;
#[test]
fn try_binary_search_ok() {
let rng = thread_rng();
let mut v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
assert!(v.try_sort().is_ok());
let b = random();
let i = v.try_binary_search(&b);
assert!(i.is_ok());
let ik = i.unwrap().unwrap_or_else(|e| e);
for sm in v[..ik].iter() {
assert!(sm < &b);
}
for sm in v[ik..].iter() {
assert!(sm >= &b);
}
}
}