fix(incremental): a new aggregation reaches the stems that carry its measure; ordered evaluation re-runs the passes over a zero (improvise-shbc)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
c11666ff7a
commit
28e9836759
@@ -292,3 +292,109 @@ fn a_new_record_adds_its_stem_and_a_cleared_one_takes_it_away() {
|
||||
];
|
||||
same_model(&wb.model, &none, &edits, Evaluation::Ordered).unwrap();
|
||||
}
|
||||
|
||||
/// Region {East, West}, a Time category folded away, Revenue by quarter: `R0 = East + West`
|
||||
/// targets Region, so its stems carry the measure but no formula names it.
|
||||
fn region_target_model() -> Model {
|
||||
let mut model = Model::new("regions");
|
||||
for (cat, items) in [("Region", vec!["East", "West"]), ("Time", vec!["Q1", "Q2"])] {
|
||||
model.add_category(cat).unwrap();
|
||||
for item in items {
|
||||
model.category_mut(cat).unwrap().add_item(item);
|
||||
}
|
||||
}
|
||||
model.category_mut("_Measure").unwrap().add_item("Revenue");
|
||||
for (region, quarter, v) in [
|
||||
("East", "Q1", 1.0),
|
||||
("East", "Q2", 3.0),
|
||||
("West", "Q1", 10.0),
|
||||
] {
|
||||
let key = CellKey::new(vec![
|
||||
("Region".into(), region.into()),
|
||||
("Time".into(), quarter.into()),
|
||||
("_Measure".into(), "Revenue".into()),
|
||||
]);
|
||||
model.set_cell(key, CellValue::Number(v));
|
||||
}
|
||||
model.add_formula(parse_formula("R0 = East + West", "Region").unwrap());
|
||||
model
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_aggregation_reaches_the_stems_of_a_formula_that_targets_another_category() {
|
||||
let model = region_target_model();
|
||||
let none = vec!["Time".to_string()];
|
||||
let edits = [Edit::SetMeasureAgg {
|
||||
measure: "Revenue".into(),
|
||||
func: improvise_core::formula::ast::AggFunc::Max,
|
||||
}];
|
||||
for by in [Evaluation::Ordered, Evaluation::Plain] {
|
||||
same_model(&model, &none, &edits, by).unwrap();
|
||||
}
|
||||
let mut backend = Incremental::new(model, none);
|
||||
backend.apply(&edits[0]);
|
||||
let at = CellKey::new(vec![
|
||||
("Region".into(), "R0".into()),
|
||||
("_Measure".into(), "Revenue".into()),
|
||||
]);
|
||||
let want = Some(CellValue::Number(13.0));
|
||||
assert_eq!(
|
||||
backend.value(&at, Aggregation::OverHidden),
|
||||
want,
|
||||
"Max 3 + Max 10"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_aggregation_reaches_a_product_targeted_filtered_sum() {
|
||||
let mut model = Model::new("products");
|
||||
for (cat, items) in [("Region", vec!["East", "West"]), ("Product", vec!["F0"])] {
|
||||
model.add_category(cat).unwrap();
|
||||
for item in items {
|
||||
model.category_mut(cat).unwrap().add_item(item);
|
||||
}
|
||||
}
|
||||
model.category_mut("_Measure").unwrap().add_item("Cost");
|
||||
for (region, v) in [("East", 4.0), ("West", 9.0)] {
|
||||
let key = CellKey::new(vec![
|
||||
("Region".into(), region.into()),
|
||||
("Product".into(), "F0".into()),
|
||||
("_Measure".into(), "Cost".into()),
|
||||
]);
|
||||
model.set_cell(key, CellValue::Number(v));
|
||||
}
|
||||
model.add_formula(parse_formula("P0 = SUM(F0 WHERE Region = East)", "Product").unwrap());
|
||||
let none = vec![];
|
||||
let edits = [Edit::SetMeasureAgg {
|
||||
measure: "Cost".into(),
|
||||
func: improvise_core::formula::ast::AggFunc::Min,
|
||||
}];
|
||||
for by in [Evaluation::Ordered, Evaluation::Plain] {
|
||||
same_model(&model, &none, &edits, by).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// A stored `0.0` at `F1`, then `F3 = F1`, `F4 = F3 ^ -1` and last `F1 = -0`: eager's first
|
||||
/// pass reads the stored zero, and its later passes call `-0.0` equal to it and stop.
|
||||
#[test]
|
||||
fn ordered_evaluation_keeps_the_zero_eager_settles_on() {
|
||||
let mut model = Model::new("zeros");
|
||||
model.add_category("Region").unwrap();
|
||||
model.category_mut("Region").unwrap().add_item("East");
|
||||
let key = CellKey::new(vec![
|
||||
("Region".into(), "East".into()),
|
||||
("_Measure".into(), "F1".into()),
|
||||
]);
|
||||
model.set_cell(key, CellValue::Number(0.0));
|
||||
let edits: Vec<Edit> = [
|
||||
("F3 = F1", "_Measure"),
|
||||
("F4 = F3 ^ -1", "_Measure"),
|
||||
("F1 = -0", "_Measure"),
|
||||
]
|
||||
.iter()
|
||||
.map(|(raw, cat)| Edit::AddFormula(parse_formula(raw, cat).unwrap()))
|
||||
.collect();
|
||||
for by in [Evaluation::Ordered, Evaluation::Plain] {
|
||||
same_model(&model, &[], &edits, by).unwrap_or_else(|e| panic!("{by:?}: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user