formualizer-eval 0.6.0

High-performance Arrow-backed Excel formula engine with dependency graph and incremental recalculation
Documentation
use std::sync::Arc;

use crate::engine::{
    Engine, EvalConfig, FormulaIngestBatch, FormulaIngestRecord, FormulaPlaneMode,
};
use crate::test_workbook::TestWorkbook;
use formualizer_common::LiteralValue;
use formualizer_parse::parser::parse;

fn authoritative_engine() -> Engine<TestWorkbook> {
    let cfg =
        EvalConfig::default().with_formula_plane_mode(FormulaPlaneMode::AuthoritativeExperimental);
    Engine::new(TestWorkbook::default(), cfg)
}

fn record(
    engine: &mut Engine<TestWorkbook>,
    row: u32,
    col: u32,
    formula: &str,
) -> FormulaIngestRecord {
    let ast = parse(formula).unwrap();
    let ast_id = engine.intern_formula_ast(&ast);
    FormulaIngestRecord::new(row, col, ast_id, Some(Arc::<str>::from(formula)))
}

fn active_span_count(engine: &Engine<TestWorkbook>) -> usize {
    engine.baseline_stats().formula_plane_active_span_count
}

fn assert_number(engine: &Engine<TestWorkbook>, row: u32, col: u32, expected: f64) {
    assert_eq!(
        engine.get_cell_value("Sheet1", row, col),
        Some(LiteralValue::Number(expected))
    );
}

fn build_column_family(rows: u32) -> Engine<TestWorkbook> {
    let mut engine = authoritative_engine();
    let mut formulas = Vec::with_capacity((rows * 5) as usize);
    for row in 1..=rows {
        engine
            .set_cell_value("Sheet1", row, 1, LiteralValue::Number(row as f64))
            .unwrap();
        for col in 7..=11 {
            engine
                .set_cell_value(
                    "Sheet1",
                    row,
                    col,
                    LiteralValue::Number((row * 100 + col) as f64),
                )
                .unwrap();
        }
        for (idx, col) in (2..=6).enumerate() {
            let addend = idx + 1;
            formulas.push(record(&mut engine, row, col, &format!("=A{row}+{addend}")));
        }
    }
    engine
        .ingest_formula_batches(vec![FormulaIngestBatch::new("Sheet1", formulas)])
        .unwrap();
    assert_eq!(active_span_count(&engine), 5);
    engine.evaluate_all().unwrap();
    engine
}

#[test]
fn column_delete_outside_span_region_with_dirty_closure_no_recompute() {
    let mut engine = build_column_family(1000);
    assert_eq!(active_span_count(&engine), 5);
    for col in 2..=6 {
        assert_number(&engine, 123, col, 123.0 + f64::from(col - 1));
    }

    engine.delete_columns("Sheet1", 7, 1).unwrap();

    assert_eq!(active_span_count(&engine), 5);
    for col in 2..=6 {
        assert_number(&engine, 123, col, 123.0 + f64::from(col - 1));
    }
    let result = engine.evaluate_all().unwrap();
    assert_eq!(result.computed_vertices, 0, "result={result:?}");
    assert_eq!(active_span_count(&engine), 5);
    for col in 2..=6 {
        assert_number(&engine, 987, col, 987.0 + f64::from(col - 1));
    }
}

#[test]
fn column_insert_outside_span_region_with_dirty_closure_no_recompute() {
    let mut engine = build_column_family(1000);
    assert_eq!(active_span_count(&engine), 5);

    engine.insert_columns("Sheet1", 7, 1).unwrap();

    assert_eq!(active_span_count(&engine), 5);
    for col in 2..=6 {
        assert_number(&engine, 321, col, 321.0 + f64::from(col - 1));
    }
    let result = engine.evaluate_all().unwrap();
    assert_eq!(result.computed_vertices, 0, "result={result:?}");
    assert_eq!(active_span_count(&engine), 5);
    for col in 2..=6 {
        assert_number(&engine, 654, col, 654.0 + f64::from(col - 1));
    }
}