Path: blob/main/crates/polars-utils/src/binary_search.rs
6939 views
use std::cmp::Ordering;1use std::cmp::Ordering::{Greater, Less};23/// Find the index of the first element of `arr` that is greater4/// or equal to `val`.5/// Assumes that `arr` is sorted.6pub fn find_first_ge_index<T>(arr: &[T], val: T) -> usize7where8T: Ord,9{10match arr.binary_search(&val) {11Ok(x) => x,12Err(x) => x,13}14}1516/// Find the index of the first element of `arr` that is greater17/// than `val`.18/// Assumes that `arr` is sorted.19pub fn find_first_gt_index<T>(arr: &[T], val: T) -> usize20where21T: Ord,22{23match arr.binary_search(&val) {24Ok(x) => x + 1,25Err(x) => x,26}27}2829// https://en.wikipedia.org/wiki/Exponential_search30// Use if you expect matches to be close by. Otherwise use binary search.31pub trait ExponentialSearch<T> {32fn exponential_search_by<F>(&self, f: F) -> Result<usize, usize>33where34F: FnMut(&T) -> Ordering;3536fn partition_point_exponential<P>(&self, mut pred: P) -> usize37where38P: FnMut(&T) -> bool,39{40self.exponential_search_by(|x| if pred(x) { Less } else { Greater })41.unwrap_or_else(|i| i)42}43}4445impl<T: std::fmt::Debug> ExponentialSearch<T> for &[T] {46fn exponential_search_by<F>(&self, mut f: F) -> Result<usize, usize>47where48F: FnMut(&T) -> Ordering,49{50if self.is_empty() {51return Err(0);52}5354let mut bound = 1;5556while bound < self.len() {57// SAFETY58// Bound is always >=0 and < len.59let cmp = f(unsafe { self.get_unchecked(bound) });6061if cmp == Greater {62break;63}64bound *= 265}66let end_bound = std::cmp::min(self.len(), bound);67// SAFETY:68// We checked the end bound and previous bound was within slice as per the `while` condition.69let prev_bound = bound / 2;7071let slice = unsafe { self.get_unchecked(prev_bound..end_bound) };7273match slice.binary_search_by(f) {74Ok(i) => Ok(i + prev_bound),75Err(i) => Err(i + prev_bound),76}77}78}7980#[cfg(test)]81mod test {82use super::*;8384#[test]85fn test_partition_point() {86let v = [1, 2, 3, 3, 5, 6, 7];87let i = v.as_slice().partition_point_exponential(|&x| x < 5);88assert_eq!(i, 4);89}90}919293