formualizer-eval 0.14.0

High-performance Arrow-backed Excel formula engine with dependency graph and incremental recalculation
Documentation
use super::common::arrow_eval_config;
use crate::{
    engine::{Engine, EvalConfig},
    test_workbook::TestWorkbook,
};
use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
use formualizer_parse::parser::parse;

fn engine(formulas: &[&str]) -> Engine<TestWorkbook> {
    crate::builtins::load_builtins();
    let mut e = Engine::new(
        TestWorkbook::new(),
        EvalConfig {
            enable_parallel: false,
            ..arrow_eval_config()
        },
    );
    e.enable_operation_diagnostics(3);
    for (i, formula) in formulas.iter().enumerate() {
        e.set_cell_formula("Sheet1", i as u32 + 1, 2, parse(formula).unwrap())
            .unwrap();
    }
    e
}

#[test]
fn recalc_contract_diagnostics_caught_and_inactive() {
    let mut e = engine(&[
        "=IFERROR(GAP_ALPHA(1),7)",
        "=IF(FALSE,GAP_INACTIVE(1),MISSING_NAME)",
        "=IFERROR(GAP_ALPHA(1),1)+IFERROR(GAP_BETA(1),2)",
        "=IFERROR(1/0,7)",
        "=IFERROR(7,GAP_INACTIVE(1))",
        "=ISERROR(GAP_ALPHA(1))",
    ]);
    e.evaluate_all().unwrap();
    let r = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(r.logical_cell_reason_count, 4);
    assert_eq!(r.logical_root_cell_count, 3);
    assert!(
        r.roots
            .iter()
            .all(|root| !format!("{:?}", root.operation).contains("INACTIVE"))
    );
    assert_eq!(
        e.get_cell_value("Sheet1", 1, 2),
        Some(LiteralValue::Number(7.0))
    );
}

#[test]
fn recalc_contract_diagnostics_callable_and_postfix() {
    let mut e = engine(&[
        "=LET(f,LAMBDA(x,IFERROR(GAP_LOCAL(x),7)),f(1))",
        "=IFERROR(LAMBDA(x,GAP_BODY(x))(1),7)",
        "=IFERROR(LET(x,GAP_BOUND(1),x),7)",
    ]);
    e.evaluate_all().unwrap();
    let r = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(r.logical_cell_reason_count, 3, "{r:?}");
    assert!(r.roots.iter().any(|root| matches!(
        root.operation.as_ref(),
        crate::operation_diagnostics::UnavailableOperation::PostfixInvocation
    )));
    assert!(!format!("{r:?}").contains("GAP_BODY"));
    for row in 1..=3 {
        assert_eq!(
            e.get_cell_value("Sheet1", row, 2),
            Some(LiteralValue::Number(7.0))
        );
    }
}

#[test]
fn recalc_contract_diagnostics_registered_partial_modes_report_at_leaf() {
    let mut e = engine(&[
        "=IFERROR(AGGREGATE(12,6,A1:A2),7)",
        "=IFERROR(SUM(INDIRECT(\"R1C1:R2C1\",FALSE)),7)",
        "=IFERROR(ISREF(INDIRECT(\"R1C1:R2C1\",FALSE)),7)",
        "=AGGREGATE(9,6,A1:A2)",
    ]);
    e.evaluate_all().unwrap();
    let report = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(report.logical_cell_reason_count, 3, "{report:?}");
    for root in &report.roots {
        assert!(
            matches!(root.operation.as_ref(),
            crate::operation_diagnostics::UnavailableOperation::UnimplementedFunction { name }
            if name == "AGGREGATE" || name == "INDIRECT"),
            "{root:?}"
        );
    }
}

#[test]
fn recalc_contract_diagnostics_inherited_error_not_new_root() {
    let mut e = engine(&[
        "=IFERROR(SUM(A1),7)",
        "=IF(FALSE,GAP_INACTIVE(1),A1)",
        "=IFERROR(LET(x,A1,x),7)",
        "=IFERROR(LET(f,LAMBDA(x,x),f(A1)),7)",
    ]);
    e.set_cell_value(
        "Sheet1",
        1,
        1,
        LiteralValue::Error(ExcelError::new(ExcelErrorKind::NImpl)),
    )
    .unwrap();
    e.evaluate_all().unwrap();
    assert_eq!(
        e.operation_diagnostics()
            .unwrap()
            .unwrap()
            .logical_cell_reason_count,
        0
    );
}

#[test]
fn recalc_contract_diagnostics_named_formula_symbol_origin() {
    use crate::engine::named_range::{NameScope, NamedDefinition};
    let mut e = engine(&["=IFERROR(GAP_NAME,7)"]);
    e.define_name(
        "GAP_NAME",
        NamedDefinition::Formula {
            ast: parse("=IFERROR(GAP_NAMED_BODY(1),9)").unwrap(),
            dependencies: Vec::new(),
            range_deps: Vec::new(),
        },
        NameScope::Workbook,
    )
    .unwrap();
    e.evaluate_all().unwrap();
    let r = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(r.logical_named_reason_count, 1, "{r:?}");
    assert_eq!(r.logical_cell_reason_count, 0, "{r:?}");
    assert_eq!(r.roots.len(), 1);
    assert!(r.roots[0].named_vertex.is_some());
    assert!(r.roots[0].cell.is_none());
}

#[test]
fn recalc_contract_diagnostics_tree_and_arena_agree() {
    let e = engine(&["=1"]);
    let cell = e.graph.make_cell_ref("Sheet1", 0, 1);
    let interpreter = crate::interpreter::Interpreter::new_with_cell(&e, "Sheet1", cell);
    let value = interpreter
        .evaluate_ast(
            &parse("=IFERROR(GAP_TREE(1),7)+IFERROR(LAMBDA(x,GAP_BODY(x))(1),2)").unwrap(),
        )
        .unwrap()
        .into_literal();
    assert_eq!(value, LiteralValue::Number(9.0));
    let r = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(r.logical_cell_reason_count, 2, "{r:?}");
    assert!(!format!("{r:?}").contains("GAP_BODY"));
}

#[test]
fn recalc_contract_diagnostics_resource_failure_remains_failure() {
    use crate::engine::{CancelToken, EvaluationBudgets, WorkResourceBudget};
    let mut e = engine(&["=IFERROR(GAP_ALPHA(1),7)"]);
    e.set_evaluation_budgets_for_test(EvaluationBudgets {
        work: WorkResourceBudget {
            max_work_units: Some(0),
        },
        ..EvaluationBudgets::default()
    });
    let error = e.evaluate_all_cancellable(CancelToken::new()).unwrap_err();
    assert!(matches!(
        error.extra(),
        formualizer_common::ExcelErrorExtra::Resource { .. }
    ));
    let r = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(r.logical_cell_reason_count, 0);
    assert_eq!(r.physical_function_dispatches, Some(0));
}

#[test]
fn recalc_contract_diagnostics_samples_and_request_reset() {
    for limit in [0, 1, 3] {
        let mut e = engine(&["=IFERROR(GAP_ALPHA(1),1)+IFERROR(GAP_BETA(1),2)"]);
        e.enable_operation_diagnostics(limit);
        e.evaluate_all().unwrap();
        let r = e.operation_diagnostics().unwrap().unwrap();
        assert_eq!(r.logical_cell_reason_count, 2);
        assert_eq!(r.samples.len(), limit.min(2));
        e.evaluate_all().unwrap();
        assert_eq!(
            e.operation_diagnostics()
                .unwrap()
                .unwrap()
                .logical_cell_reason_count,
            0,
            "request events are not a retained-result coverage certificate"
        );
    }
}

#[test]
fn recalc_contract_cell_valid_unsupported_mode_is_not_invalid_input() {
    let mut e = engine(&[
        "=IFERROR(CELL(\"format\",A1),\"fallback\")",
        "=IF(FALSE,CELL(\"format\",A1),7)",
        "=CELL(\"not_an_info_type\",A1)",
        "=CELL(\"row\",A1)",
    ]);
    e.set_cell_value("Sheet1", 1, 1, LiteralValue::Number(2.0))
        .unwrap();
    e.evaluate_all().unwrap();
    let report = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(report.logical_root_cell_count, 1, "{report:?}");
    assert_eq!(report.logical_cell_reason_count, 1, "{report:?}");
}
fn pivot_mode_report(values: &str) -> crate::operation_diagnostics::OperationDiagnostics {
    crate::builtins::load_builtins();
    let mut e = Engine::new(TestWorkbook::new(), arrow_eval_config());
    e.enable_operation_diagnostics(8);
    for row in 1..=2 {
        e.set_cell_value("Sheet1", row, 1, LiteralValue::Text("x".into()))
            .unwrap();
        e.set_cell_value("Sheet1", row, 2, LiteralValue::Text("p".into()))
            .unwrap();
        e.set_cell_value("Sheet1", row, 3, LiteralValue::Number(10.0 * row as f64))
            .unwrap();
        e.set_cell_value("Sheet1", row, 4, LiteralValue::Number(100.0 * row as f64))
            .unwrap();
    }
    let formula = format!("=PIVOTBY(A1:A2,B1:B2,{values},\"SUM\",0,0,0,0,0)");
    e.set_cell_formula("Sheet1", 1, 6, parse(&formula).unwrap())
        .unwrap();
    e.evaluate_all().unwrap();
    e.operation_diagnostics().unwrap().unwrap()
}
#[test]
fn recalc_contract_pivotby_supported_and_partial_shapes() {
    assert_eq!(pivot_mode_report("C1:C2").logical_root_cell_count, 0);
    assert_eq!(pivot_mode_report("C1:D2").logical_root_cell_count, 1);
}

fn telemetry_report(physical: bool) -> crate::operation_diagnostics::OperationDiagnostics {
    let mut e = engine(&[
        "=IFERROR(GAP_TELEMETRY(1),7)",
        "=ABS(-2)",
        "=IF(FALSE,GAP_INACTIVE(1),5)",
    ]);
    e.configure_operation_diagnostics(crate::operation_diagnostics::OperationDiagnosticOptions {
        sample_limit: 0,
        physical_telemetry: physical,
        ..Default::default()
    });
    e.evaluate_all().unwrap();
    e.take_operation_diagnostics().unwrap().unwrap()
}

#[test]
fn recalc_contract_sparse_evidence_does_not_require_physical_telemetry() {
    let sparse = telemetry_report(false);
    let full = telemetry_report(true);
    assert_eq!(sparse.roots, full.roots);
    assert_eq!(sparse.logical_root_cell_count, 1);
    assert!(sparse.samples.is_empty());
    assert_eq!(sparse.physical_function_dispatches, None);
    assert_eq!(sparse.physical_unavailable_observations, None);
    assert!(
        full.physical_function_dispatches
            .is_some_and(|count| count > 0)
    );
    assert!(
        full.physical_unavailable_observations
            .is_some_and(|count| count > 0)
    );
}

#[test]
fn recalc_contract_exact_mode_evidence_uses_executed_leaf_origin() {
    use crate::operation_diagnostics::UnavailableOperation;
    let mut e = engine(&[
        "=IFERROR(CELL(\"format\",A1),\"fallback\")",
        "=IF(TRUE,7,CELL(\"width\",A1))",
        "=CELL(\"not_an_info_type\",A1)",
        "=IFERROR(CELL(\"format\",1/0),8)",
    ]);
    e.set_cell_value("Sheet1", 1, 1, LiteralValue::Number(2.0))
        .unwrap();
    e.evaluate_all().unwrap();
    let report = e.operation_diagnostics().unwrap().unwrap();
    assert_eq!(report.roots.len(), 1, "{report:?}");
    assert!(matches!(
        report.roots[0].operation.as_ref(),
        UnavailableOperation::UnimplementedFunctionMode { name, mode }
        if name == "CELL" && *mode == "format"
    ));
    let cell = report.roots[0].cell.unwrap();
    assert_eq!((cell.coord.row(), cell.coord.col()), (0, 1));
    assert!(report.roots[0].named_vertex.is_none());
}

#[test]
fn recalc_contract_evidence_failure_survives_caught_values_and_request_reset() {
    let mut e = engine(&["=IFERROR(GAP_ONE(1),7)", "=IFERROR(GAP_TWO(1),8)"]);
    e.configure_operation_diagnostics(crate::operation_diagnostics::OperationDiagnosticOptions {
        sample_limit: 0,
        max_roots: Some(1),
        ..Default::default()
    });
    e.evaluate_all().unwrap();
    assert_eq!(
        e.get_cell_value("Sheet1", 1, 2),
        Some(LiteralValue::Number(7.0))
    );
    let error = e.take_operation_diagnostics().unwrap_err();
    assert!(matches!(
        error.extra(),
        formualizer_common::ExcelErrorExtra::Resource { detail }
        if detail.reason == formualizer_common::ResourceExhaustionReason::OperationEvidenceRoots
    ));
    e.set_cell_formula("Sheet1", 1, 2, parse("=1+1").unwrap())
        .unwrap();
    e.set_cell_formula("Sheet1", 2, 2, parse("=2+2").unwrap())
        .unwrap();
    e.evaluate_all().unwrap();
    let next = e.take_operation_diagnostics().unwrap().unwrap();
    assert!(next.roots.is_empty());
    assert_eq!(next.physical_function_dispatches, None);
    assert!(e.take_operation_diagnostics().is_err());
}