use std::cmp::Ordering;
use rudb_common::Result;
use rudb_common::Stat;
use rudb_common::bounds::{Bound, End, Frequencies, Spread, Test, Zones, kept};
use rudb_common::stat::{Direction, Provenance};
use rudb_storage::Probe;
use crate::Reader;
#[derive(Debug, Clone)]
pub struct Stripes {
reader: Reader,
}
impl Stripes {
#[must_use]
pub fn new(reader: Reader) -> Self {
Self { reader }
}
}
impl Zones for Stripes {
fn column(&self, name: &str) -> Option<usize> {
self.reader.table().fields().iter().position(|field| field.name == name)
}
fn surviving(&self, tests: &[Test]) -> Option<u64> {
let probes = probes(tests);
let mut total: u64 = 0;
for (at, stripe) in self.reader.table().stripes().iter().enumerate() {
if self.reader.stripe_skips(at, &probes) {
continue;
}
total = total.checked_add(u64::try_from(stripe.rows()).ok()?)?;
}
Some(total)
}
fn spread(&self, tests: &[Test]) -> Option<Spread> {
let mut passing = 0.0_f64;
let mut whole = 0.0_f64;
let mut read = 0;
for stripe in self.reader.table().stripes() {
let rows = rows(stripe.rows());
let spread = fraction(tests, stripe.zone());
whole += rows;
passing += rows * spread.fraction;
read = read.max(spread.read);
}
(read > 0 && whole > 0.0)
.then(|| Spread { fraction: (passing / whole).clamp(0.0, 1.0), read })
}
fn extreme(&self, column: usize, end: End) -> Stat<Bound> {
match self.reader.exact_extremes(column) {
Ok(Some((low, high))) => {
Stat::exact(if end == End::Low { low } else { high }, Provenance::ZoneMap)
}
_ => Stat::Unknown,
}
}
}
pub fn distincts(reader: &Reader) -> Result<Vec<(String, Stat<u64>)>> {
let table = reader.table();
let rows = u64::try_from(table.rows()).unwrap_or(u64::MAX);
let mut counted = Vec::new();
for (at, field) in table.fields().iter().enumerate() {
if let Some(exact) = reader.distinct_values(at)? {
counted.push((field.name.clone(), Stat::exact(exact, Provenance::Dictionary)));
continue;
}
let Some((Bound::Int(low), Bound::Int(high))) = reader.exact_extremes(at)? else {
continue;
};
let Some(span) = high.checked_sub(low).and_then(|span| u64::try_from(span).ok()) else {
continue;
};
let Some(span) = span.checked_add(1).filter(|&span| span < rows) else {
continue;
};
counted.push((
field.name.clone(),
Stat::certified(span, 1.0, Direction::AtMost, Provenance::ZoneMap),
));
}
Ok(counted)
}
#[derive(Debug, Clone)]
pub struct Common {
reader: Reader,
}
impl Common {
#[must_use]
pub fn new(reader: Reader) -> Self {
Self { reader }
}
}
impl Frequencies for Common {
fn column(&self, name: &str) -> Option<usize> {
self.reader.table().fields().iter().position(|field| field.name == name)
}
fn rows(&self) -> u64 {
u64::try_from(self.reader.table().rows()).unwrap_or(u64::MAX)
}
fn rows_with(&self, column: usize, value: &Bound) -> Stat<u64> {
let Ok(Some(entries)) = self.reader.exact_frequencies(column) else {
return Stat::Unknown;
};
let mut comparable = false;
for (held, count) in entries {
let Some(bound) = Bound::of_value(&held) else {
continue;
};
match bound.order(value) {
Some(Ordering::Equal) => return Stat::exact(count, Provenance::FrequencySynopsis),
Some(_) => comparable = true,
None => {}
}
}
if comparable { Stat::exact(0, Provenance::FrequencySynopsis) } else { Stat::Unknown }
}
}
fn probes(tests: &[Test]) -> Vec<Probe> {
tests
.iter()
.map(|test| Probe { column: test.column, op: test.op, value: test.value.clone() })
.collect()
}
#[expect(clippy::cast_precision_loss, reason = "a row count is a weight here and not an identity")]
fn rows(count: usize) -> f64 {
count as f64
}
fn fraction(tests: &[Test], zone: &rudb_storage::Zone) -> Spread {
let mut spread = Spread { fraction: 1.0, read: 0 };
for (position, test) in tests.iter().enumerate() {
if tests[..position].iter().any(|earlier| earlier.column == test.column) {
continue;
}
let Some(range) = zone.column(test.column) else { continue };
let (Some(low), Some(high)) = (range.low.as_ref(), range.high.as_ref()) else { continue };
let Some(kept) = kept(tests, test.column, low, high) else { continue };
spread.fraction *= kept.fraction;
spread.read += kept.read;
}
spread
}