use std::cmp::Ordering;
pub fn linear_search<T>(arr: &[T], target: &T) -> Result<usize, usize>
where
T: Ord,
{
linear_search_by(arr, target, T::cmp)
}
pub fn linear_search_by<T, Target, F>(arr: &[T], target: &Target, cmp: F) -> Result<usize, usize>
where
F: Fn(&Target, &T) -> Ordering,
{
for (i, existed) in arr.iter().enumerate() {
match cmp(target, existed) {
Ordering::Equal => return Ok(i),
Ordering::Less => return Err(i),
Ordering::Greater => (),
}
}
Err(arr.len())
}
#[cfg(feature = "vec_tools")]
#[inline(always)]
#[allow(dead_code)] pub(crate) fn search_by<T, Target, F>(arr: &[T], target: &Target, cmp: F) -> Result<usize, usize>
where
F: Fn(&Target, &T) -> Ordering,
{
crate::vec_tools::search::binary_search_by(arr, target, cmp)
}
#[cfg(not(feature = "vec_tools"))]
#[inline(always)]
#[allow(dead_code)] pub(crate) fn search_by<T, Target, F>(arr: &[T], target: &Target, cmp: F) -> Result<usize, usize>
where
F: Fn(&Target, &T) -> Ordering,
{
crate::linear_search_by(arr, target, cmp)
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use std::fmt::Debug;
#[macro_export]
macro_rules! test_search_slice {
($f:expr, $arr:expr $(,)?) => {
__test_search($f, $arr, *$arr.first().unwrap()..*$arr.last().unwrap())
};
}
pub(crate) fn __test_search<T, Search>(
search: Search,
arr: &mut [T],
boarder_range: impl IntoIterator<Item = T>,
) where
T: Ord + Debug,
Search: Fn(&[T], &T) -> Result<usize, usize>,
{
arr.sort();
for (i, target) in arr.iter().enumerate() {
let res = search(arr, target);
assert!(
arr[res.unwrap()] == arr[i],
"Error on target={target:?} and res={res:?}"
);
}
for target in boarder_range {
let found = arr.iter().any(|item| *item == target);
let res = search(arr, &target);
assert_eq!(res.is_ok(), found);
if !found {
let should_insert_to = res.unwrap_err();
assert!(should_insert_to >= arr.len() || arr[should_insert_to] >= target);
}
}
print!("test succeed on ");
match arr.len() {
0..=1000 => println!("{arr:?}"),
l => println!(
"[{:?}, {:?}, ..., {:?}; {l}]",
arr[0],
arr[1],
arr.last().unwrap()
),
}
}
#[macro_export(local_inner_macros)]
macro_rules! test_search {
($search:expr) => {
test_search_slice!($search, &mut [2, 4, 6, 7, 8]);
test_search_slice!($search, &mut [1, 3, 5, 7, 9]);
test_search_slice!($search, &mut [0, 0, 0, 0, 0]); test_search_slice!($search, &mut std::array::from_fn::<_, 100, _>(|i| i * i));
for gap in 1..=100 {
test_search_slice!(
$search,
&mut (0..10000).filter(|x| x % gap == 0).collect::<Vec<_>>()
);
}
test_search_slice!($search, &mut [-2, -4, -6, -7, -8]);
test_search_slice!($search, &mut [-1, -3, -5, -7, -9]);
test_search_slice!($search, &mut [0, -0, 0, -0, 0]); test_search_slice!(
$search,
&mut (0..10000)
.map(|x| if x & 1 == 0 { x } else { -x })
.collect::<Vec<_>>(),
);
test_search_slice!($search, &mut ['a', 'b', 'f', '你', '好', '😋', '✨']); test_search_slice!(
$search,
&mut "我们有权报复三体文明".chars().collect::<Vec<_>>()
); test_search_slice!($search, &mut ('\x00'..'\u{00ff}').collect::<Vec<_>>());
let mut strings = "\
Self {
prefixes: prefixes
.into_iter()
.map(|into_s| into_s.into())
.collect::<Vec<String>>(),
}"
.split_whitespace()
.collect::<Vec<_>>();
let strings_more =
"pub fn new(prefixes: impl IntoIterator<Item = impl Into<String>>) -> Self {
// ? 或许也可以「先新建空值,然后逐个添加」来实现,复杂度 ∑ 1 log 1 ~ n log n
Self {
prefixes: prefixes
.into_iter()
.map(|into_s| into_s.into())
.collect::<Vec<String>>(),
}
}"
.split_whitespace()
.collect::<Vec<_>>();
__test_search($search, &mut strings, strings_more);
};
}
#[test]
fn test_linear_search() {
test_search!(linear_search);
}
}