use super::column::{PAGE_BITS, PAGE_MASK};
use super::dataset::Data;
use super::ids::Ids;
use super::name::Name;
use super::table::{Field, Table};
use super::values::Kind;
use crate::{Error, Result};
const ENVELOPE_VALUES: usize = 1 << 26;
#[derive(Clone, PartialEq, Debug, serde::Serialize, schemars::JsonSchema)]
pub struct Description {
pub name: Name,
pub axis: Option<[Name; 2]>,
pub span: Option<[f64; 2]>,
pub tables: Vec<TableDescription>,
}
#[derive(Clone, PartialEq, Debug, serde::Serialize, schemars::JsonSchema)]
pub struct TableDescription {
pub name: Name,
pub rows: usize,
pub fields: Vec<FieldDescription>,
}
#[derive(Clone, PartialEq, Debug, serde::Serialize, schemars::JsonSchema)]
pub struct FieldDescription {
pub name: Name,
pub kind: Kind,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<Name>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span: Option<[f64; 2]>,
}
impl Data {
pub fn describe(&self) -> Description {
Description {
name: self.name().clone(),
axis: self.axis().map(|(name, unit)| [name.into(), unit.into()]),
span: self.span(),
tables: self.tables().map(Table::describe).collect(),
}
}
}
impl Table {
pub fn describe(&self) -> TableDescription {
let fields = self.fields.iter().map(|(name, field)| FieldDescription {
name: name.clone(),
kind: field.kind(),
to: match field {
Field::Reference(reference) => Some(reference.to.clone()),
_ => None,
},
span: match field {
Field::Sampled(samples, _) => samples.span(),
_ => None,
},
});
TableDescription { name: self.name.clone(), rows: self.len(), fields: fields.collect() }
}
pub fn envelope(&self, field: &str, range: [f64; 2], spans: usize, ids: Option<&Ids>) -> Result<Envelope> {
let Some(Field::Sampled(samples, _)) = self.field_named(field) else {
return Err(Error::invalid("not a sampled field").at(field));
};
if !(range[0].is_finite() && range[1].is_finite() && range[0] <= range[1]) || spans == 0 {
return Err(Error::invalid("an envelope spans a finite range, in one span or more"));
}
let rows: Vec<usize> = match ids {
None => (0..self.len()).collect(),
Some(ids) => (0..ids.len())
.map(|i| ids.key(i).and_then(|key| self.row(key)).ok_or_else(|| Error::invalid("no such row").at(i)))
.collect::<Result<_>>()?,
};
if rows.len().saturating_mul(spans) > ENVELOPE_VALUES {
return Err(Error::Limit(
format!("an envelope holds at most {ENVELOPE_VALUES} values: rows times spans").into(),
));
}
let width = (range[1] - range[0]) / spans as f64;
let at: Vec<f64> =
(0..=spans).map(|s| if s == spans { range[1] } else { range[0] + width * s as f64 }).collect();
let bounds: Vec<u64> = at.iter().enumerate().map(|(s, &t)| samples.bound(t, s == spans)).collect();
let pieces: Vec<Vec<(u32, u64)>> = bounds.windows(2).map(|b| samples.pieces(b[0]..b[1])).collect();
let mut least = vec![f32::NAN; rows.len() * spans];
let mut greatest = least.clone();
let mut by_block: Vec<(usize, usize)> =
rows.iter().enumerate().map(|(i, &row)| (row >> PAGE_BITS, i)).collect();
by_block.sort_unstable();
for run in by_block.chunk_by(|a, b| a.0 == b.0) {
let block = run[0].0;
let mut extent =
vec![[f32::NAN; 2]; run.iter().map(|&(_, i)| (rows[i] & PAGE_MASK) + 1).max().unwrap_or(0)];
for (s, pieces) in pieces.iter().enumerate() {
extent.fill([f32::NAN; 2]);
for &piece in pieces {
samples.fold(block, piece, &mut extent);
}
for &(_, i) in run {
let [lo, hi] = extent[rows[i] & PAGE_MASK];
(least[i * spans + s], greatest[i * spans + s]) = (lo, hi);
}
}
}
Ok(Envelope { at, least, greatest })
}
}
#[derive(Clone, PartialEq, Debug, serde::Serialize)]
pub struct Envelope {
pub at: Vec<f64>,
pub least: Vec<f32>,
pub greatest: Vec<f32>,
}