mod primitive;
use std::cmp::Ordering;
use std::cmp::Ordering::Equal;
use std::cmp::Ordering::Greater;
use std::cmp::Ordering::Less;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::hint;
pub use primitive::*;
use vortex_error::VortexResult;
use crate::ArrayRef;
use crate::VortexSessionExecute;
use crate::legacy_session;
use crate::scalar::Scalar;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SearchSortedSide {
Left,
Right,
}
impl Display for SearchSortedSide {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SearchSortedSide::Left => write!(f, "left"),
SearchSortedSide::Right => write!(f, "right"),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum SearchResult {
Found(usize),
NotFound(usize),
}
impl SearchResult {
pub fn to_found(self) -> Option<usize> {
match self {
Self::Found(i) => Some(i),
Self::NotFound(_) => None,
}
}
pub fn to_index(self) -> usize {
match self {
Self::Found(i) => i,
Self::NotFound(i) => i,
}
}
pub fn to_offsets_index(self, len: usize, side: SearchSortedSide) -> usize {
match self {
SearchResult::Found(i) => {
if side == SearchSortedSide::Right || i == len {
i.saturating_sub(1)
} else {
i
}
}
SearchResult::NotFound(i) => i.saturating_sub(1),
}
}
pub fn to_ends_index(self, len: usize) -> usize {
let idx = self.to_index();
if idx == len { idx - 1 } else { idx }
}
}
impl Display for SearchResult {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SearchResult::Found(i) => write!(f, "Found({i})"),
SearchResult::NotFound(i) => write!(f, "NotFound({i})"),
}
}
}
pub trait IndexOrd<V> {
fn index_cmp(&self, idx: usize, elem: &V) -> VortexResult<Option<Ordering>>;
#[inline]
fn index_lt(&self, idx: usize, elem: &V) -> VortexResult<bool> {
Ok(matches!(self.index_cmp(idx, elem)?, Some(Less)))
}
#[inline]
fn index_le(&self, idx: usize, elem: &V) -> VortexResult<bool> {
Ok(matches!(self.index_cmp(idx, elem)?, Some(Less | Equal)))
}
fn index_gt(&self, idx: usize, elem: &V) -> VortexResult<bool> {
Ok(matches!(self.index_cmp(idx, elem)?, Some(Greater)))
}
fn index_ge(&self, idx: usize, elem: &V) -> VortexResult<bool> {
Ok(matches!(self.index_cmp(idx, elem)?, Some(Greater | Equal)))
}
fn index_len(&self) -> usize;
}
pub trait SearchSorted<T> {
#[inline]
fn search_sorted(&self, value: &T, side: SearchSortedSide) -> VortexResult<SearchResult>
where
Self: IndexOrd<T>,
{
match side {
SearchSortedSide::Left => self.search_sorted_by(
|idx| Ok(self.index_cmp(idx, value)?.unwrap_or(Less)),
|idx| {
Ok(if self.index_lt(idx, value)? {
Less
} else {
Greater
})
},
side,
),
SearchSortedSide::Right => self.search_sorted_by(
|idx| Ok(self.index_cmp(idx, value)?.unwrap_or(Less)),
|idx| {
Ok(if self.index_le(idx, value)? {
Less
} else {
Greater
})
},
side,
),
}
}
fn search_sorted_by<
F: FnMut(usize) -> VortexResult<Ordering>,
N: FnMut(usize) -> VortexResult<Ordering>,
>(
&self,
find: F,
side_find: N,
side: SearchSortedSide,
) -> VortexResult<SearchResult>;
}
impl<S, T> SearchSorted<T> for S
where
S: IndexOrd<T> + ?Sized,
{
#[inline]
fn search_sorted_by<
F: FnMut(usize) -> VortexResult<Ordering>,
N: FnMut(usize) -> VortexResult<Ordering>,
>(
&self,
find: F,
side_find: N,
side: SearchSortedSide,
) -> VortexResult<SearchResult> {
match search_sorted_side_idx(find, 0, self.index_len())? {
SearchResult::Found(found) => {
let idx_search = match side {
SearchSortedSide::Left => search_sorted_side_idx(side_find, 0, found)?,
SearchSortedSide::Right => {
search_sorted_side_idx(side_find, found, self.index_len())?
}
};
match idx_search {
SearchResult::NotFound(i) => Ok(SearchResult::Found(i)),
_ => unreachable!(
"searching amongst equal values should never return Found result"
),
}
}
s => Ok(s),
}
}
}
#[inline]
fn search_sorted_side_idx<F: FnMut(usize) -> VortexResult<Ordering>>(
mut find: F,
from: usize,
to: usize,
) -> VortexResult<SearchResult> {
let mut size = to - from;
if size == 0 {
return Ok(SearchResult::NotFound(0));
}
let mut base = from;
while size > 1 {
let half = size / 2;
let mid = base + half;
let cmp = find(mid)?;
base = hint::select_unpredictable(cmp == Greater, base, mid);
size -= half;
}
let cmp = find(base)?;
if cmp == Equal {
unsafe { hint::assert_unchecked(base < to) };
Ok(SearchResult::Found(base))
} else {
let result = base + (cmp == Less) as usize;
unsafe { hint::assert_unchecked(result <= to) };
Ok(SearchResult::NotFound(result))
}
}
impl IndexOrd<Scalar> for ArrayRef {
#[allow(clippy::disallowed_methods)]
fn index_cmp(&self, idx: usize, elem: &Scalar) -> VortexResult<Option<Ordering>> {
let scalar_a = self.execute_scalar(idx, &mut legacy_session().create_execution_ctx())?;
Ok(scalar_a.partial_cmp(elem))
}
fn index_len(&self) -> usize {
Self::len(self)
}
}
impl<T: PartialOrd> IndexOrd<T> for [T] {
#[inline]
fn index_cmp(&self, idx: usize, elem: &T) -> VortexResult<Option<Ordering>> {
Ok(unsafe { self.get_unchecked(idx) }.partial_cmp(elem))
}
#[inline]
fn index_len(&self) -> usize {
self.len()
}
}
#[cfg(test)]
mod tests {
use vortex_error::VortexResult;
use crate::search_sorted::SearchResult;
use crate::search_sorted::SearchSorted;
use crate::search_sorted::SearchSortedSide;
#[test]
fn left_side_equal() -> VortexResult<()> {
let arr = [0, 1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9];
let res = arr.search_sorted(&2, SearchSortedSide::Left)?;
assert_eq!(arr[res.to_index()], 2);
assert_eq!(res, SearchResult::Found(2));
Ok(())
}
#[test]
fn right_side_equal() -> VortexResult<()> {
let arr = [0, 1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9];
let res = arr.search_sorted(&2, SearchSortedSide::Right)?;
assert_eq!(arr[res.to_index() - 1], 2);
assert_eq!(res, SearchResult::Found(6));
Ok(())
}
#[test]
fn left_side_equal_beginning() -> VortexResult<()> {
let arr = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let res = arr.search_sorted(&0, SearchSortedSide::Left)?;
assert_eq!(arr[res.to_index()], 0);
assert_eq!(res, SearchResult::Found(0));
Ok(())
}
#[test]
fn right_side_equal_beginning() -> VortexResult<()> {
let arr = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let res = arr.search_sorted(&0, SearchSortedSide::Right)?;
assert_eq!(arr[res.to_index() - 1], 0);
assert_eq!(res, SearchResult::Found(4));
Ok(())
}
#[test]
fn left_side_equal_end() -> VortexResult<()> {
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9];
let res = arr.search_sorted(&9, SearchSortedSide::Left)?;
assert_eq!(arr[res.to_index()], 9);
assert_eq!(res, SearchResult::Found(9));
Ok(())
}
#[test]
fn right_side_equal_end() -> VortexResult<()> {
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9];
let res = arr.search_sorted(&9, SearchSortedSide::Right)?;
assert_eq!(arr[res.to_index() - 1], 9);
assert_eq!(res, SearchResult::Found(13));
Ok(())
}
}