Shared Rust kernels
catalogue/database
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
label · 10 lines · calc-func/src/catalogue/database.rs on GitHub/// A field label's text: text as it is; a number or a logical as their text.
fn label(site: &Site<'_>, index: usize, value: &CalcValue) -> Step<Option<Vec<u16>>> {
match value.core_ref() {
CoreRef::Empty | CoreRef::Missing => Ok(None),
CoreRef::Error(code) => fail(code),
_ => Ok(Some(
site.argument(index, crate::coerce::text(value))?.into_owned(),
)),
}
}
Used by 12 functions.
same_label · 4 lines · calc-func/src/catalogue/database.rs on GitHubfn same_label(a: &[u16], b: &[u16]) -> bool {
use crate::ported::text_kernels::fold_unit;
a.len() == b.len() && a.iter().zip(b).all(|(&x, &y)| fold_unit(x) == fold_unit(y))
}
Used by 12 functions.
field · 24 lines · calc-func/src/catalogue/database.rs on GitHub/// The database's column for a field argument.
fn field(site: &Site<'_>, a: &[CalcValue], headers: &[Option<Vec<u16>>], optional: bool) -> Step<Option<usize>> {
let value = &a[1];
match value.core_ref() {
CoreRef::Missing | CoreRef::Empty if optional => Ok(None),
CoreRef::Text(text) => {
let units = text.utf16_code_units();
headers
.iter()
.position(|h| h.as_deref().is_some_and(|h| same_label(h, units)))
.map(Some)
.ok_or(Exit::Error(W::Value))
}
CoreRef::Error(code) => fail(code),
_ => {
let n = site.number_argument(1, value)?;
let column = n.trunc();
if column < 1.0 || column > headers.len() as f64 {
return fail(W::Value);
}
Ok(Some(column as usize - 1))
}
}
}
Used by 12 functions.
matching · 60 lines · calc-func/src/catalogue/database.rs on GitHub/// The records that meet the criteria, by row index below the label row.
fn matching(site: &Site<'_>, database: &Grid, headers: &[Option<Vec<u16>>], criteria: &Grid) -> Step<Vec<usize>> {
let records = database.rows.saturating_sub(1);
if criteria.rows < 2 {
return Ok((0..records).collect());
}
// Each criteria row: (database column, criterion) pairs.
let mut rows = Vec::new();
for r in 1..criteria.rows {
let mut conditions = Vec::new();
for c in 0..criteria.cols {
let cell = &criteria.cells[r * criteria.cols + c];
let Some(criterion) = database_criterion(site, 2, cell)? else {
continue;
};
let Some(name) = label(site, 2, &criteria.cells[c])? else {
return fail(W::Value);
};
let column = headers
.iter()
.position(|h| h.as_deref().is_some_and(|h| same_label(h, &name)))
.ok_or(Exit::Error(W::Value))?;
conditions.push((column, criterion));
}
rows.push(conditions);
}
let mut found = Vec::new();
for record in 0..records {
let mut any = false;
let mut unknown = false;
for conditions in &rows {
let mut verdict = Match::Yes;
for (column, criterion) in conditions {
let cell = &database.cells[(record + 1) * database.cols + column];
match criterion_verdict(site, criterion, cell)? {
Match::No => {
verdict = Match::No;
break;
}
Match::Unknown => verdict = Match::Unknown,
Match::Yes => {}
}
}
match verdict {
Match::Yes => {
any = true;
break;
}
Match::Unknown => unknown = true,
Match::No => {}
}
}
if any {
found.push(record);
} else if unknown {
return Err(site.not_modelled(Some(2)));
}
}
Ok(found)
}
Used by 12 functions.
criterion_verdict · 3 lines · calc-func/src/catalogue/database.rs on GitHubfn criterion_verdict(site: &Site<'_>, criterion: &Criterion, cell: &CalcValue) -> Step<Match> {
criterion.verdict(site, 0, cell)
}
Used by 12 functions.
database · 99 lines · calc-func/src/catalogue/database.rs on GitHubfn database(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>, kind: Kind) -> Step<CalcValue> {
if !matches!(a[0].core_ref(), CoreRef::Reference(_) | CoreRef::Array(_)) {
return match a[0].core_ref() {
CoreRef::Error(code) => fail(code),
_ => fail(W::Value),
};
}
let database = grid(site, a, 0, sv)?;
if database.rows == 0 || database.cols == 0 {
return fail(W::Value);
}
let mut headers = Vec::with_capacity(database.cols);
for c in 0..database.cols {
headers.push(label(site, 0, &database.cells[c])?);
}
let optional = matches!(kind, Kind::Count | Kind::CountA);
let column = field(site, a, &headers, optional)?;
if !matches!(a[2].core_ref(), CoreRef::Reference(_) | CoreRef::Array(_)) {
return match a[2].core_ref() {
CoreRef::Error(code) => fail(code),
_ => fail(W::Value),
};
}
let criteria = grid(site, a, 2, sv)?;
let records = matching(site, &database, &headers, &criteria)?;
let Some(column) = column else {
return site.number(records.len() as f64);
};
let cell = |record: usize| &database.cells[(record + 1) * database.cols + column];
if kind == Kind::Get {
return match records.as_slice() {
[] => fail(W::Value),
[one] => {
let value = cell(*one).clone();
Ok(if matches!(value.core_ref(), CoreRef::Empty) {
CalcValue::number(0.0)
} else {
value
})
}
_ => fail(W::Num),
};
}
if kind == Kind::CountA {
let mut count = 0usize;
for &record in &records {
if !matches!(cell(record).core_ref(), CoreRef::Empty | CoreRef::Missing) {
count += 1;
}
}
return site.number(count as f64);
}
let mut numbers = Vec::new();
for &record in &records {
match cell(record).core_ref() {
CoreRef::Number(n) => numbers.push(site.argument(0, crate::coerce::operand(n))?),
CoreRef::Error(code) => return fail(code),
_ => {}
}
}
match kind {
Kind::Count => site.number(numbers.len() as f64),
Kind::Sum => {
let mut total = 0.0;
for n in numbers {
total = site.add(total, n)?;
}
site.number(total)
}
Kind::Average => {
if numbers.is_empty() {
return fail(W::Div0);
}
let count = numbers.len() as f64;
let mut total = 0.0;
for n in numbers {
total = site.add(total, n)?;
}
site.number(site.divide(total, count)?)
}
Kind::Max => site.number(numbers.into_iter().reduce(f64::max).unwrap_or(0.0)),
Kind::Min => site.number(numbers.into_iter().reduce(f64::min).unwrap_or(0.0)),
Kind::Product => {
if numbers.is_empty() {
return site.number(0.0);
}
let mut product = 1.0;
for n in numbers {
product = site.multiply(product, n)?;
}
site.number(product)
}
Kind::Stdev => published(site, stdev_from_values(&numbers, VarianceDivisor::Sample)),
Kind::StdevP => published(site, stdev_from_values(&numbers, VarianceDivisor::Population)),
Kind::Var => published(site, variance_from_values(&numbers, VarianceDivisor::Sample)),
Kind::VarP => published(site, variance_from_values(&numbers, VarianceDivisor::Population)),
Kind::Get | Kind::CountA => unreachable!("handled above"),
}
}
Used by 12 functions.
published · 7 lines · calc-func/src/catalogue/database.rs on GitHubfn published(site: &Site<'_>, result: Result<f64, W>) -> Step<CalcValue> {
site.binary64()?;
match result {
Ok(n) => site.number(n),
Err(code) => fail(code),
}
}
Used by 12 functions.