use super::pgm::{LearnedIndex, PgmIndex};
use std::collections::HashSet;
#[inline]
fn i64_key(v: i64) -> u64 {
(v as u64) ^ (1u64 << 63)
}
#[inline]
fn f64_key(v: f64) -> u64 {
let bits = v.to_bits();
if bits & (1u64 << 63) != 0 {
!bits
} else {
bits ^ (1u64 << 63)
}
}
#[derive(Debug, Clone)]
pub struct ColumnLearnedRange {
keys: Vec<u64>, row_ids: Vec<u64>, pgm: PgmIndex,
}
impl ColumnLearnedRange {
pub fn build_i64(pairs: &[(i64, u64)]) -> Self {
let mut sorted: Vec<(u64, u64)> = pairs.iter().map(|(v, r)| (i64_key(*v), *r)).collect();
sorted.sort_unstable_by_key(|(k, _)| *k);
let keys: Vec<u64> = sorted.iter().map(|(k, _)| *k).collect();
let row_ids: Vec<u64> = sorted.iter().map(|(_, r)| *r).collect();
let points: Vec<(u64, usize)> = keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
let pgm = if points.is_empty() {
PgmIndex::build(&[], 16)
} else {
PgmIndex::build(&points, 16)
};
Self { keys, row_ids, pgm }
}
fn lower_bound(&self, key: u64) -> usize {
let n = self.keys.len();
if n == 0 {
return 0;
}
let (lo, hi) = self.pgm.predict(key);
let lo = lo.min(n);
let hi = hi.min(n).max(lo);
let mut idx = lo + self.keys[lo..hi].partition_point(|k| *k < key);
if idx > 0 && self.keys[idx - 1] >= key {
let mut step = 1usize;
while idx >= step && self.keys[idx - step] >= key {
step <<= 1;
}
let start = idx - step.min(idx);
idx = start + self.keys[start..idx].partition_point(|k| *k < key);
}
if idx < n && self.keys[idx] < key {
let mut step = 1usize;
while idx + step <= n && self.keys[(idx + step).min(n) - 1] < key {
step <<= 1;
}
let end = (idx + step).min(n);
idx = idx + self.keys[idx..end].partition_point(|k| *k < key);
}
idx
}
fn upper_bound(&self, key: u64) -> usize {
let n = self.keys.len();
if n == 0 {
return 0;
}
let (lo, hi) = self.pgm.predict(key);
let lo = lo.min(n);
let hi = hi.min(n).max(lo);
let mut idx = lo + self.keys[lo..hi].partition_point(|k| *k <= key);
if idx > 0 && self.keys[idx - 1] > key {
let mut step = 1usize;
while idx >= step && self.keys[idx - step] > key {
step <<= 1;
}
let start = idx - step.min(idx);
idx = start + self.keys[start..idx].partition_point(|k| *k <= key);
}
if idx < n && self.keys[idx] <= key {
let mut step = 1usize;
while idx + step <= n && self.keys[(idx + step).min(n) - 1] <= key {
step <<= 1;
}
let end = (idx + step).min(n);
idx = idx + self.keys[idx..end].partition_point(|k| *k <= key);
}
idx
}
pub fn range(&self, lo: i64, hi: i64) -> HashSet<u64> {
if hi < lo || self.keys.is_empty() {
return HashSet::new();
}
let start = self.lower_bound(i64_key(lo));
let end = self.upper_bound(i64_key(hi));
self.row_ids[start..end].iter().copied().collect()
}
pub fn build_f64(pairs: &[(f64, u64)]) -> Self {
let mut sorted: Vec<(u64, u64)> = pairs.iter().map(|(v, r)| (f64_key(*v), *r)).collect();
sorted.sort_unstable_by_key(|(k, _)| *k);
let keys: Vec<u64> = sorted.iter().map(|(k, _)| *k).collect();
let row_ids: Vec<u64> = sorted.iter().map(|(_, r)| *r).collect();
let points: Vec<(u64, usize)> = keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
let pgm = if points.is_empty() {
PgmIndex::build(&[], 16)
} else {
PgmIndex::build(&points, 16)
};
Self { keys, row_ids, pgm }
}
pub fn range_f64(
&self,
lo: f64,
lo_inclusive: bool,
hi: f64,
hi_inclusive: bool,
) -> HashSet<u64> {
if self.keys.is_empty() {
return HashSet::new();
}
let lo_key = f64_key(lo);
let hi_key = f64_key(hi);
let (start, end) = if hi < lo {
return HashSet::new();
} else if lo_inclusive && hi_inclusive {
(self.lower_bound(lo_key), self.upper_bound(hi_key))
} else if lo_inclusive {
(self.lower_bound(lo_key), self.lower_bound(hi_key))
} else if hi_inclusive {
(self.upper_bound(lo_key), self.upper_bound(hi_key))
} else {
(self.upper_bound(lo_key), self.lower_bound(hi_key))
};
self.row_ids[start..end].iter().copied().collect()
}
pub fn snapshot(&self) -> ColumnLearnedRangeSnapshot {
ColumnLearnedRangeSnapshot {
keys: self.keys.clone(),
row_ids: self.row_ids.clone(),
pgm: self.pgm.clone(),
}
}
pub fn from_snapshot(snap: ColumnLearnedRangeSnapshot) -> Self {
Self {
keys: snap.keys,
row_ids: snap.row_ids,
pgm: snap.pgm,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ColumnLearnedRangeSnapshot {
pub keys: Vec<u64>,
pub row_ids: Vec<u64>,
pub pgm: PgmIndex,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn range_returns_exact_slice() {
let pairs = vec![
(100i64, 0u64),
(-5, 1),
(50, 2),
(100, 3),
(1000, 4),
(-5, 5),
];
let idx = ColumnLearnedRange::build_i64(&pairs);
let r = idx.range(50, 100);
assert_eq!(r, [2, 0, 3].into_iter().collect::<HashSet<_>>());
let all = idx.range(i64::MIN, i64::MAX);
assert_eq!(all.len(), 6);
let none = idx.range(200, 300);
assert!(none.is_empty());
let negs = idx.range(i64::MIN, -5);
assert_eq!(negs, [1, 5].into_iter().collect::<HashSet<_>>());
}
}