#![no_std]
#![deny(missing_docs)]
pub fn upper_bound(search: Result<usize, usize>, cap: usize) -> Option<usize> {
match search {
Ok(index) => Some(index),
Err(index) => {
if index < cap {
Some(index)
} else {
None
}
}
}
}
pub fn upper_bound_always(search: Result<usize, usize>, cap: usize) -> usize {
upper_bound(search, cap).unwrap_or(cap - 1)
}
#[inline(always)]
pub fn lower_bound(search: Result<usize, usize>) -> Option<usize> {
match search {
Ok(index) => Some(index),
Err(0) => None,
Err(index) => Some(index - 1),
}
}
#[inline(always)]
pub fn lower_bound_always(search: Result<usize, usize>) -> usize {
lower_bound(search).unwrap_or(0)
}
use core::borrow::Borrow;
pub trait Search {
fn search<T: Ord>(slice: &[T], x: &T) -> Result<usize, usize> {
Self::search_by_key(slice, x)
}
fn search_by_key<K: Ord, T: Borrow<K>>(slice: &[T], x: &K) -> Result<usize, usize>;
}
pub struct BinarySearch;
impl Search for BinarySearch {
fn search_by_key<K: Ord, T: Borrow<K>>(slice: &[T], x: &K) -> Result<usize, usize> {
slice.binary_search_by(|y| y.borrow().cmp(x))
}
}
pub struct LinearSearch;
impl Search for LinearSearch {
fn search_by_key<K: Ord, T: Borrow<K>>(slice: &[T], x: &K) -> Result<usize, usize> {
let mut index = 0;
let size = slice.len();
while index < size && unsafe { slice.get_unchecked(index).borrow() } < x {
index += 1;
}
if index >= size {
Err(size)
} else if unsafe { slice.get_unchecked(index).borrow() } == x {
Ok(index)
} else {
Err(index)
}
}
}
const BINARY_SEARCH_CUTOFF: usize = 1024;
pub struct OptimalSearch;
impl Search for OptimalSearch {
fn search_by_key<K: Ord, T: Borrow<K>>(slice: &[T], x: &K) -> Result<usize, usize> {
if slice.len() * core::mem::size_of::<K>() > BINARY_SEARCH_CUTOFF {
BinarySearch::search_by_key(slice, x)
} else {
LinearSearch::search_by_key(slice, x)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_upper_bound() {
assert_eq!(upper_bound(Ok(3), 5), Some(3));
assert_eq!(upper_bound(Err(3), 5), Some(3));
assert_eq!(upper_bound(Err(5), 5), None);
assert_eq!(upper_bound(Err(7), 5), None);
assert_eq!(upper_bound_always(Ok(3), 5), 3);
assert_eq!(upper_bound_always(Err(3), 5), 3);
assert_eq!(upper_bound_always(Err(5), 5), 4);
assert_eq!(upper_bound_always(Err(7), 5), 4);
}
#[test]
fn test_lower_bound() {
assert_eq!(lower_bound(Ok(3)), Some(3));
assert_eq!(lower_bound(Err(3)), Some(2));
assert_eq!(lower_bound(Err(0)), None);
assert_eq!(lower_bound_always(Ok(3)), 3);
assert_eq!(lower_bound_always(Err(3)), 2);
assert_eq!(lower_bound_always(Err(0)), 0);
}
#[test]
fn binary_linear_search() {
let array = [1, 2, 3, 4, 7, 10, 24, 55, 56, 57, 100];
for i in -10..110 {
assert_eq!(
BinarySearch::search(&array[..], &i),
LinearSearch::search(&array[..], &i)
);
}
}
#[test]
fn binary_optimal_search() {
let array = [1, 2, 3, 4, 7, 10, 24, 55, 56, 57, 100];
for i in 0..1_000 {
assert_eq!(
BinarySearch::search(&array[..], &i),
OptimalSearch::search(&array[..], &i)
);
}
}
}