use std::cmp::Ordering;
pub fn linear<T: PartialEq>(slice: &[T], value: &T) -> Option<usize> {
for (i, v) in slice.iter().enumerate() {
if value == v {
return Some(i);
}
}
None
}
pub fn binary<T: Ord>(slice: &[T], value: &T) -> Option<usize> {
let mid = slice.len() / 2;
match value.cmp(&slice[mid]) {
Ordering::Less if mid > 0 => binary(&slice[0..mid], value),
Ordering::Equal => Some(mid),
Ordering::Greater if mid < slice.len() - 1 => {
match binary(&slice[(mid + 1)..slice.len()], value) {
Some(x) => Some(x + mid + 1),
None => None,
}
}
_ => None,
}
}
pub fn binary_first<T: Ord>(slice: &[T], value: &T) -> Option<usize> {
let pos = binary(slice, value);
match pos {
Some(pos) => {
for (i, v) in slice[0..pos].iter().enumerate().rev() {
if v < value {
return Some(i + 1);
}
}
Some(0)
}
None => None,
}
}
pub fn jump_step<T: Ord>(slice: &[T], value: &T, step: usize) -> Option<usize> {
if step == 1 {
return linear(slice, value);
} else if step == 0 {
if &slice[0] == value {
return Some(0);
} else {
return None;
}
}
let mut iter = slice.iter();
let mut pos: usize = 0;
let mut found = false;
for i in 0..(slice.len() / step) {
match value.cmp(iter.next().unwrap()) {
Ordering::Less => {
if i == 0 {
return None;
}
found = true;
break;
}
Ordering::Equal => return Some(i * step),
Ordering::Greater => {
pos = i * step;
if i >= slice.len() {
return None;
}
}
}
iter.nth(step - 2).unwrap();
}
let mut end = pos + step;
if !found {
end = slice.len()
}
linear(&slice[(pos + 1)..end], value).map(|x| x + pos + 1)
}
pub fn jump<T: Ord>(slice: &[T], value: &T) -> Option<usize> {
jump_step(slice, value, (slice.len() as f64).sqrt() as usize)
}
pub fn exp<T: Ord>(slice: &[T], value: &T) -> Option<usize> {
if &slice[0] == value {
return Some(0);
}
let mut exp = 0;
let start = loop {
let i = usize::pow(2, exp);
match value.cmp(&slice[i]) {
Ordering::Less if i > 1 => break i / 2,
Ordering::Equal => return Some(i),
Ordering::Greater if i < slice.len() - 1 => {}
_ => return None,
}
exp += 1;
};
binary_first(&slice[start..(start * 2)], value).map(|x| x + start)
}
#[cfg(test)]
mod tests {
use super::binary;
use super::binary_first;
use super::jump;
use super::linear;
#[test]
fn linear_test() {
assert_eq!(linear(&[0, 5, -7, 100, 67, -23], &-7), Some(2));
assert_eq!(linear(&[11, -25, 12, 85, -8], &6), None)
}
#[test]
fn binary_test() {
let fib = [1, 1, 2, 3, 5, 8, 13, 21];
assert_eq!(binary(&fib, &5), Some(4));
assert_eq!(binary(&fib, &21), Some(7));
let primes = [1, 2, 3, 5, 7, 11, 13, 17];
assert_eq!(binary(&primes, &8), None);
assert_eq!(binary(&primes, &0), None);
assert_eq!(binary(&primes, &18), None);
}
#[test]
fn binary_first_test() {
assert_eq!(binary(&[1, 1, 2, 3], &1), Some(1));
assert_eq!(binary_first(&[1, 1, 2, 3], &1), Some(0));
}
#[test]
fn jump_test() {
assert_eq!(jump(&[2, 5, 6, 11], &5), Some(1));
assert_eq!(jump(&[2, 5, 6, 11], &4), None);
}
#[test]
fn exp_test() {
let slice = [-2, 0, 3, 6, 7, 12, 23, 25, 31, 41];
assert_eq!(jump(&slice, &12), Some(5));
assert_eq!(jump(&slice, &13), None);
}
}