use crate::cs::error::{Error, Result};
pub fn search<T: Ord>(data: &[T], target: &T) -> Result<Option<usize>> {
if data.is_empty() {
return Ok(None);
}
if !is_sorted(data) {
return Err(Error::invalid_input(
"Fibonacci search requires sorted input",
));
}
let mut fib2 = 0; let mut fib1 = 1; let mut fib = fib1 + fib2;
while fib < data.len() {
fib2 = fib1;
fib1 = fib;
fib = fib1 + fib2;
}
let mut offset = -1;
while fib > 1 {
let i = ((offset + fib2 as i32) as usize).min(data.len() - 1);
match target.cmp(&data[i]) {
std::cmp::Ordering::Less => {
fib = fib2;
fib1 -= fib2;
fib2 = fib - fib1;
}
std::cmp::Ordering::Greater => {
fib = fib1;
fib1 = fib2;
fib2 = fib - fib1;
offset = i as i32;
}
std::cmp::Ordering::Equal => return Ok(Some(i)),
}
}
if fib1 == 1 && (offset + 1) < data.len() as i32 {
let i = (offset + 1) as usize;
if target == &data[i] {
return Ok(Some(i));
}
}
Ok(None)
}
fn is_sorted<T: Ord>(data: &[T]) -> bool {
data.windows(2).all(|w| w[0] <= w[1])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_slice() {
let data: Vec<i32> = vec![];
assert!(matches!(search(&data, &5).unwrap(), None));
}
#[test]
fn test_single_element_found() {
let data = vec![5];
assert!(matches!(search(&data, &5).unwrap(), Some(0)));
}
#[test]
fn test_single_element_not_found() {
let data = vec![5];
assert!(matches!(search(&data, &3).unwrap(), None));
}
#[test]
fn test_multiple_elements_found_first() {
let data = vec![1, 2, 3, 4, 5];
assert!(matches!(search(&data, &1).unwrap(), Some(0)));
}
#[test]
fn test_multiple_elements_found_last() {
let data = vec![1, 2, 3, 4, 5];
assert!(matches!(search(&data, &5).unwrap(), Some(4)));
}
#[test]
fn test_multiple_elements_found_middle() {
let data = vec![1, 2, 3, 4, 5];
assert!(matches!(search(&data, &3).unwrap(), Some(2)));
}
#[test]
fn test_multiple_elements_not_found() {
let data = vec![1, 2, 3, 4, 5];
assert!(matches!(search(&data, &6).unwrap(), None));
}
#[test]
fn test_with_duplicates() {
let data = vec![1, 2, 2, 2, 3, 4];
let result = search(&data, &2).unwrap().unwrap();
assert!(result >= 1 && result <= 3);
}
#[test]
fn test_unsorted_input() {
let data = vec![3, 1, 4, 1, 5];
assert!(matches!(search(&data, &4), Err(Error::InvalidInput(_))));
}
#[test]
fn test_large_sorted_dataset() {
let data: Vec<i32> = (0..10_000).collect();
assert!(matches!(search(&data, &5000).unwrap(), Some(5000)));
assert!(matches!(search(&data, &10_000).unwrap(), None));
}
#[test]
fn test_with_strings() {
let data = vec!["apple", "banana", "orange", "pear"];
assert!(matches!(search(&data, &"orange").unwrap(), Some(2)));
assert!(matches!(search(&data, &"grape").unwrap(), None));
}
#[test]
fn test_boundary_values() {
let data = vec![i32::MIN, -5, 0, 5, i32::MAX];
assert!(matches!(search(&data, &i32::MIN).unwrap(), Some(0)));
assert!(matches!(search(&data, &i32::MAX).unwrap(), Some(4)));
assert!(matches!(search(&data, &0).unwrap(), Some(2)));
}
#[test]
fn test_fibonacci_sequence_lengths() {
let data1: Vec<i32> = (0..3).collect(); let data2: Vec<i32> = (0..5).collect(); let data3: Vec<i32> = (0..8).collect(); let data4: Vec<i32> = (0..13).collect();
assert!(matches!(search(&data1, &1).unwrap(), Some(1)));
assert!(matches!(search(&data2, &3).unwrap(), Some(3)));
assert!(matches!(search(&data3, &5).unwrap(), Some(5)));
assert!(matches!(search(&data4, &8).unwrap(), Some(8)));
}
#[test]
fn test_non_fibonacci_lengths() {
let data1: Vec<i32> = (0..4).collect(); let data2: Vec<i32> = (0..7).collect(); let data3: Vec<i32> = (0..10).collect();
assert!(matches!(search(&data1, &2).unwrap(), Some(2)));
assert!(matches!(search(&data2, &4).unwrap(), Some(4)));
assert!(matches!(search(&data3, &6).unwrap(), Some(6)));
}
}