Skip to main content

visi_core/core/engine/sheet/
mod.rs

1mod edit;
2mod functions;
3
4pub use edit::get_word_boundaries_from_str;
5
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet, VecDeque};
8use web_time::Instant;
9
10use super::cell::{
11    CellRef, CellType, Dependency, EngineError, EvalError, TextCellRef, generate_unique_id,
12};
13use super::column::DataColumn;
14use super::result_data::ResultData;
15/// Context for evaluating expressions, containing references to other sheets
16#[derive(Default)]
17pub struct Context<'a> {
18    /// Map of sheet names to sheet references for cross-sheet lookups
19    pub sheets: HashMap<String, &'a Sheet>,
20    /// Every pivot table in the workbook, so `GETPIVOTDATA` can resolve a
21    /// rendered pivot's destination cell back to its definition
22    pub pivot_tables: &'a [crate::core::pivot::PivotTable],
23    /// Store sheet order, so `SHEET()` works.
24    /// `sheets` is a HashMap, so this is needed
25    pub sheet_order: Vec<String>,
26}
27
28impl<'a> Context<'a> {
29    /// Create a new empty context
30    pub fn new() -> Self {
31        Self {
32            sheets: HashMap::new(),
33            pivot_tables: &[],
34            sheet_order: Vec::new(),
35        }
36    }
37
38    /// Add a sheet to the context for lookup during evaluation
39    pub fn add_table(&mut self, name: String, sheet: &'a Sheet) {
40        self.sheets.insert(name, sheet);
41    }
42}
43
44enum LetScope<'a> {
45    Empty,
46    Bound {
47        name: &'a str,
48        value: &'a ResultData,
49        parent: &'a LetScope<'a>,
50    },
51}
52
53impl<'a> LetScope<'a> {
54    fn get(&self, name: &str) -> Option<&ResultData> {
55        match self {
56            LetScope::Empty => None,
57            LetScope::Bound {
58                name: n,
59                value,
60                parent,
61            } => {
62                if n.eq_ignore_ascii_case(name) {
63                    Some(value)
64                } else {
65                    parent.get(name)
66                }
67            }
68        }
69    }
70}
71
72/// Which way a fill or selection extends from its anchor cell.
73#[derive(Debug, Clone, Copy, PartialEq)]
74pub enum Direction {
75    /// No direction; the operation is a no-op.
76    None,
77    /// Toward row 0.
78    Up,
79    /// Toward the last row.
80    Down,
81    /// Toward column 0.
82    Left,
83    /// Toward the last column.
84    Right,
85}
86
87/// A worksheet
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Sheet {
90    /// Workbook-unique identifier
91    #[serde(default = "generate_unique_id")]
92    pub id: u64,
93    /// Display name, as it appears in a cross-sheet reference.
94    pub name: String,
95    /// The cells, one entry per column. Row `r` of column `c` is
96    /// `columns[c]`'s entry `r`.
97    ///
98    /// Every column has the same number of rows
99    pub(crate) columns: Vec<DataColumn>,
100    /// Excel/OpenXML row heights in point units, aligned with sheet rows.
101    #[serde(default)]
102    pub(crate) row_heights: Vec<Option<f64>>,
103    /// Excel Tables (ListObjects) defined on this sheet.
104    #[serde(default)]
105    pub tables: Vec<crate::core::table::ExcelTable>,
106    /// Forward edges: which cells must be recomputed when a dependency
107    /// changes
108    #[serde(skip, default)]
109    pub dependencies: HashMap<Dependency, HashSet<CellRef>>,
110    /// Reverse edges: what each cell currently reads, so its old edges can be
111    /// dropped when its formula changes
112    #[serde(skip, default)]
113    pub dependencies_rev: HashMap<CellRef, HashSet<Dependency>>,
114    /// Edits made since the last commit, for callers that want to observe or
115    /// replay them.
116    #[serde(skip)]
117    pub uncommitted_actions: Vec<crate::core::SheetAction>,
118    /// Regional locale for date and number parsing.
119    #[serde(default)]
120    pub locale: crate::core::locale::Locale,
121}
122
123/// Arguments for [`Sheet::new`]
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct SheetInit {
126    /// Identifier to use; `None` generates a fresh one.
127    #[serde(default)]
128    pub id: Option<u64>,
129    /// Name to use; `None` means `table_1`.
130    pub name: Option<String>,
131    /// Rows to allocate.
132    pub rows: usize,
133    /// Columns to allocate.
134    pub cols: usize,
135}
136
137impl Default for SheetInit {
138    fn default() -> Self {
139        Self {
140            id: None,
141            name: None,
142            rows: 10,
143            cols: 5,
144        }
145    }
146}
147
148#[derive(Clone, Copy, PartialEq, Eq)]
149enum BlankPolicy {
150    /// Counts as 0 (MULTINOMIAL).
151    Zero,
152    /// Dropped entirely, shifting later elements (SERIESSUM).
153    Skip,
154    /// #VALUE!, like text (LINEST/TREND/GROWTH/LOGEST/MMULT).
155    Reject,
156}
157
158impl Sheet {
159    /// Creates a sheet of `args.rows` x `args.cols` empty cells, every one of
160    /// them queued as a pending edit so the first [`Sheet::commit`] sees them.
161    pub fn new(args: SheetInit) -> Sheet {
162        let SheetInit {
163            id,
164            name,
165            rows,
166            cols,
167        } = args;
168        let sheet_id = id.unwrap_or_else(generate_unique_id);
169        let sheet_name = name.unwrap_or_else(|| "table_1".to_string());
170
171        let mut columns = Vec::with_capacity(cols);
172        for _ in 0..cols {
173            columns.push(DataColumn::new(rows));
174        }
175
176        let mut uncommitted_actions = Vec::new();
177        for c in 0..cols {
178            for r in 0..rows {
179                uncommitted_actions.push(crate::core::SheetAction::SetCellSrc {
180                    sheet_name: sheet_name.clone(),
181                    col: c,
182                    row: r,
183                    src: String::new(),
184                });
185            }
186        }
187
188        Self {
189            id: sheet_id,
190            name: sheet_name,
191            columns,
192            row_heights: vec![None; rows],
193            tables: Vec::new(),
194            dependencies: HashMap::new(),
195            dependencies_rev: HashMap::new(),
196            uncommitted_actions,
197            locale: crate::core::locale::Locale::default(),
198        }
199    }
200
201    /// Rebuilds what serialization drops.
202    pub fn setup_after_deserialization(&mut self) {
203        for col in &mut self.columns {
204            col.rebuild_after_load();
205        }
206        let row_count = self.row_count();
207        self.row_heights.resize(row_count, None);
208        self.mark_all_dirty();
209    }
210
211    pub(crate) fn get_all_sheets_for_compilation(&self, context: Option<&Context>) -> Vec<Sheet> {
212        let mut list = vec![self.clone()];
213        let mut seen = std::collections::HashSet::new();
214        seen.insert(self.id);
215        if let Some(ctx) = context {
216            for sheet in ctx.sheets.values() {
217                if !seen.contains(&sheet.id) {
218                    seen.insert(sheet.id);
219                    list.push((*sheet).clone());
220                }
221            }
222        }
223        list
224    }
225
226    /// Queues every cell for recomputation on the next [`Sheet::commit`].
227    pub fn mark_all_dirty(&mut self) {
228        for col in &mut self.columns {
229            col.dirty_indices.clear();
230            col.dirty_indices.extend(0..col.src.len());
231        }
232    }
233
234    /// Commit all changed src items with a context for sheet lookups
235    pub fn commit(&mut self, context: Option<&Context>) -> Result<HashSet<CellRef>, EngineError> {
236        let mut queue: VecDeque<CellRef> = VecDeque::new();
237        let mut queue_set: HashSet<CellRef> = HashSet::new();
238        let mut updated_cells: HashSet<CellRef> = HashSet::new();
239
240        for (col_idx, col_data) in self.columns.iter_mut().enumerate() {
241            for row_idx in &col_data.dirty_indices {
242                let cell = CellRef::new(*row_idx, col_idx);
243                queue.push_back(cell);
244                queue_set.insert(cell);
245                updated_cells.insert(cell);
246            }
247            col_data.dirty_indices.clear();
248        }
249
250        let initial_queue_len = queue.len();
251        if initial_queue_len == 0 {
252            return Ok(updated_cells);
253        }
254
255        let start_commit = Instant::now();
256        log::info!(
257            "Sheet '{}' commit starting for {} dirty cells",
258            self.name,
259            initial_queue_len
260        );
261        let max_ops = 10000.max(initial_queue_len * 3);
262        let mut ops = 0;
263
264        let mut sheets_for_compilation = self.get_all_sheets_for_compilation(context);
265        let mut last_log_time = Instant::now();
266
267        while let Some(cell_ref) = queue.pop_front() {
268            queue_set.remove(&cell_ref);
269            ops += 1;
270            if ops > max_ops {
271                println!("Circular dependency or too many updates detected");
272                break;
273            }
274
275            if ops % 50000 == 0 {
276                log::info!(
277                    "Sheet '{}' commit progress: {}/{} cells processed ({:.2?})",
278                    self.name,
279                    ops,
280                    initial_queue_len,
281                    last_log_time.elapsed()
282                );
283                last_log_time = Instant::now();
284            }
285
286            let cell_type_hint = self
287                .columns
288                .get(cell_ref.col)
289                .and_then(|c| c.cell_types.get(cell_ref.row).copied())
290                .unwrap_or(CellType::Empty);
291
292            let mut detected_num_format: Option<String> = None;
293            let (result, new_deps, compiled_to_cache, mut final_cell_type) = {
294                let src = self.get_src_str_ref(&cell_ref).unwrap_or("");
295                if !src.starts_with('=') && cell_type_hint == CellType::String {
296                    let val = if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
297                        src[1..src.len() - 1].to_string()
298                    } else {
299                        src.to_string()
300                    };
301                    (ResultData::String(val), vec![], None, CellType::String)
302                } else if !src.starts_with('=') {
303                    let (res, c_type) = if let Some(stripped) = src.strip_prefix('\'') {
304                        (ResultData::String(stripped.to_string()), CellType::String)
305                    } else if matches!(
306                        cell_type_hint,
307                        CellType::DateTimeIso | CellType::DurationIso
308                    ) {
309                        (ResultData::String(src.to_string()), cell_type_hint)
310                    } else if cell_type_hint == CellType::DateTime {
311                        if let Ok(f) = src.trim().parse::<f64>() {
312                            (ResultData::Float(f), CellType::DateTime)
313                        } else if let Some((date, format)) =
314                            crate::core::date::parse_date_with_locale(
315                                src.trim_matches(' '),
316                                &self.locale,
317                            )
318                        {
319                            detected_num_format = Some(format.to_format_code());
320                            (
321                                ResultData::Float(crate::core::date::date_to_excel_serial(date)),
322                                CellType::DateTime,
323                            )
324                        } else if let Some(f) = crate::core::date_fn::parse_time_fraction(src) {
325                            (ResultData::Float(f), CellType::DateTime)
326                        } else {
327                            (ResultData::String(src.to_string()), CellType::String)
328                        }
329                    } else if cell_type_hint == CellType::Float {
330                        if let Ok(f) = src.trim().parse::<f64>()
331                            && f.is_finite()
332                        {
333                            (ResultData::Float(f), CellType::Float)
334                        } else {
335                            (ResultData::String(src.to_string()), CellType::String)
336                        }
337                    } else if cell_type_hint == CellType::Int {
338                        if let Ok(i) = src.trim().parse::<i64>() {
339                            (ResultData::Integer(i), CellType::Int)
340                        } else {
341                            (ResultData::String(src.to_string()), CellType::String)
342                        }
343                    } else if cell_type_hint == CellType::Bool {
344                        if src == "1" || src.eq_ignore_ascii_case("true") {
345                            (ResultData::Boolean(true), CellType::Bool)
346                        } else if src == "0" || src.eq_ignore_ascii_case("false") {
347                            (ResultData::Boolean(false), CellType::Bool)
348                        } else {
349                            (ResultData::String(src.to_string()), CellType::String)
350                        }
351                    } else if cell_type_hint == CellType::Error {
352                        (ResultData::Error(src.to_uppercase()), CellType::Error)
353                    } else if src.is_empty() {
354                        (ResultData::None, CellType::Empty)
355                    } else if src.starts_with('"') && src.ends_with('"') && src.len() >= 2 {
356                        (
357                            ResultData::String(src[1..src.len() - 1].to_string()),
358                            CellType::String,
359                        )
360                    } else if let Ok(i) = src.trim().parse::<i64>() {
361                        (ResultData::Integer(i), CellType::Int)
362                    } else if let Ok(f) = src.trim().parse::<f64>()
363                        && f.is_finite()
364                    {
365                        (ResultData::Float(f), CellType::Float)
366                    } else if crate::core::engine::result_data::is_excel_error_code(src) {
367                        (ResultData::Error(src.to_uppercase()), CellType::Error)
368                    } else if src.eq_ignore_ascii_case("true") {
369                        (ResultData::Boolean(true), CellType::Bool)
370                    } else if src.eq_ignore_ascii_case("false") {
371                        (ResultData::Boolean(false), CellType::Bool)
372                    } else if let Some((date, format)) = crate::core::date::parse_date_with_locale(
373                        src.trim_matches(' '),
374                        &self.locale,
375                    ) {
376                        detected_num_format = Some(format.to_format_code());
377                        (
378                            ResultData::Float(crate::core::date::date_to_excel_serial(date)),
379                            CellType::DateTime,
380                        )
381                    } else if let Some(f) = crate::core::date_fn::parse_time_fraction(src) {
382                        (ResultData::Float(f), CellType::DateTime)
383                    } else {
384                        (ResultData::String(src.to_string()), CellType::String)
385                    };
386                    (res, vec![], None, c_type)
387                } else {
388                    let compiled =
389                        crate::core::parser::compile_formula(src, &sheets_for_compilation);
390                    let eval_src =
391                        crate::core::parser::serialize_formula(&compiled, &sheets_for_compilation);
392                    let (res, deps) = match self.eval_with_row(
393                        &eval_src,
394                        context,
395                        Some(cell_ref.row),
396                        Some(cell_ref.col),
397                    ) {
398                        Ok(r) => r,
399                        Err(e) => (ResultData::Error(e.to_string()), vec![]),
400                    };
401                    let final_res = if let ResultData::None = res {
402                        ResultData::Float(0.0)
403                    } else {
404                        res
405                    };
406                    let final_cell_type = match &final_res {
407                        ResultData::None => CellType::Empty,
408                        ResultData::Integer(_) => CellType::Int,
409                        ResultData::Float(_) => CellType::Float,
410                        ResultData::String(_) => CellType::String,
411                        ResultData::Boolean(_) => CellType::Bool,
412                        ResultData::Error(_) => CellType::Error,
413                        ResultData::List(_) | ResultData::Dict(_) => CellType::String,
414                    };
415                    (final_res, deps, Some(compiled), final_cell_type)
416                }
417            };
418
419            if let Some(src_str) = self.get_src_str_ref(&cell_ref)
420                && let Some(stripped) = src_str.strip_prefix('\'')
421            {
422                let stripped_str = stripped.to_string();
423                if let Some(col) = self.columns.get_mut(cell_ref.col)
424                    && cell_ref.row < col.src.len()
425                {
426                    col.src[cell_ref.row] = stripped_str;
427                }
428            }
429
430            if let Some(col) = self.columns.get_mut(cell_ref.col)
431                && cell_ref.row < col.compiled_src.len()
432            {
433                col.compiled_src[cell_ref.row] = compiled_to_cache.unwrap_or_default();
434            }
435
436            if let Some(old_deps) = self.dependencies_rev.remove(&cell_ref) {
437                for provider in old_deps {
438                    if let Some(dependents) = self.dependencies.get_mut(&provider) {
439                        dependents.remove(&cell_ref);
440                    }
441                }
442            }
443
444            if !new_deps.is_empty() {
445                let mut new_deps_set = HashSet::new();
446                for provider in new_deps {
447                    new_deps_set.insert(provider.clone());
448                    self.dependencies
449                        .entry(provider)
450                        .or_default()
451                        .insert(cell_ref);
452                }
453                self.dependencies_rev.insert(cell_ref, new_deps_set);
454            }
455
456            let inherited = if detected_num_format.is_some()
457                || !matches!(&result, ResultData::Float(_) | ResultData::Integer(_))
458            {
459                None
460            } else {
461                self.get_src_str_ref(&cell_ref)
462                    .and_then(|src| src.strip_prefix('='))
463                    .and_then(|body| crate::core::parser::parse_excel_formula(body).ok())
464                    .and_then(|ast| self.inherited_date_format(&ast))
465            };
466            if let Some(code) = detected_num_format.or(inherited) {
467                if matches!(&result, ResultData::Float(_) | ResultData::Integer(_)) {
468                    final_cell_type = CellType::DateTime;
469                }
470                let existing = self
471                    .get_cell_style(cell_ref.row, cell_ref.col)
472                    .and_then(|s| s.num_format.clone());
473                if existing.is_none() {
474                    self.update_cell_style(cell_ref.row, cell_ref.col, |style| {
475                        style.num_format = Some(code);
476                    });
477                }
478            }
479
480            if let Some(col) = self.columns.get_mut(cell_ref.col)
481                && cell_ref.row < col.data.len()
482            {
483                col.cell_types[cell_ref.row] = final_cell_type;
484                col.data.set(cell_ref.row, result.clone());
485                updated_cells.insert(cell_ref);
486            }
487            if let Some(comp_sheet) = sheets_for_compilation
488                .iter_mut()
489                .find(|s| s.name == self.name)
490                && let Some(col) = comp_sheet.columns.get_mut(cell_ref.col)
491                && cell_ref.row < col.data.len()
492            {
493                col.cell_types[cell_ref.row] = final_cell_type;
494                col.data.set(cell_ref.row, result);
495            }
496
497            let local_dep_key = Dependency::Local(cell_ref);
498            if let Some(dependents) = self.dependencies.get(&local_dep_key) {
499                for dependent in dependents {
500                    if !queue_set.contains(dependent) {
501                        queue.push_back(*dependent);
502                        queue_set.insert(*dependent);
503                    }
504                }
505            }
506
507            let local_col_dep_key = Dependency::LocalColumn(cell_ref.col);
508            if let Some(dependents) = self.dependencies.get(&local_col_dep_key) {
509                for dependent in dependents {
510                    if !queue_set.contains(dependent) {
511                        queue.push_back(*dependent);
512                        queue_set.insert(*dependent);
513                    }
514                }
515            }
516        }
517        if initial_queue_len > 0 {
518            log::info!(
519                "Sheet '{}' commit finished. Processed {} cell updates. Total time: {:.2?}",
520                self.name,
521                ops,
522                start_commit.elapsed()
523            );
524        }
525        Ok(updated_cells)
526    }
527
528    /// Evaluates cell source text without storing it, as
529    /// [`Sheet::eval`] does, but from the point of view of `(row, col)`.
530    pub fn eval_with_row(
531        &self,
532        input: &str,
533        context: Option<&Context>,
534        row: Option<usize>,
535        col: Option<usize>,
536    ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
537        if input.is_empty() {
538            return Ok((ResultData::None, vec![]));
539        }
540        if let Some(formula) = input.strip_prefix('=') {
541            self.eval_excel(formula, context, row, col)
542        } else {
543            if let Ok(i) = input.parse::<i64>() {
544                Ok((ResultData::Integer(i), vec![]))
545            } else if let Ok(f) = input.parse::<f64>() {
546                Ok((ResultData::Float(f), vec![]))
547            } else if let Ok(b) = input.parse::<bool>() {
548                Ok((ResultData::Boolean(b), vec![]))
549            } else {
550                Ok((ResultData::String(input.to_string()), vec![]))
551            }
552        }
553    }
554
555    /// Evaluates cell source text against this sheet without storing it,
556    /// returning the value and the references it read.
557    pub fn eval(
558        &self,
559        input: &str,
560        context: Option<&Context>,
561    ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
562        self.eval_with_row(input, context, None, None)
563    }
564
565    fn eval_excel(
566        &self,
567        code: &str,
568        context: Option<&Context>,
569        row: Option<usize>,
570        col: Option<usize>,
571    ) -> Result<(ResultData, Vec<Dependency>), EngineError> {
572        let ast = crate::core::parser::parse_excel_formula(code)
573            .map_err(|e| EngineError::EvalError(EvalError::UnknownFunction(e)))?;
574
575        let mut deps = Vec::new();
576        let result = match self.evaluate_ast(&ast, context, row, col, &mut deps, &LetScope::Empty) {
577            Ok(r) => r,
578            Err(EngineError::EvalError(EvalError::UnknownFunction(err_str)))
579                if err_str.starts_with('#') =>
580            {
581                ResultData::Error(err_str)
582            }
583            Err(e) => return Err(e),
584        };
585        Ok((result, deps))
586    }
587
588    fn evaluate_ast(
589        &self,
590        ast: &crate::core::parser::Expr,
591        context: Option<&Context>,
592        row: Option<usize>,
593        col: Option<usize>,
594        deps: &mut Vec<Dependency>,
595        scope: &LetScope<'_>,
596    ) -> Result<ResultData, EngineError> {
597        use crate::core::SheetSection;
598        use crate::core::parser::Expr;
599        use crate::core::parser::Op;
600
601        match ast {
602            Expr::Number(n) => Ok(ResultData::Float(*n)),
603            Expr::String(s) => Ok(ResultData::String(s.clone())),
604            Expr::Boolean(b) => Ok(ResultData::Boolean(*b)),
605            Expr::Error(code) => Ok(ResultData::Error(code.to_string())),
606            Expr::Identifier(name) => match scope.get(name) {
607                Some(val) => Ok(val.clone()),
608                None => Ok(ResultData::Error("#NAME?".to_string())),
609            },
610            Expr::StructuredRef {
611                sheet,
612                column,
613                is_this_row,
614                section,
615            } => {
616                let ref_name = match sheet {
617                    Some(name) => name.clone(),
618                    None => self.name.clone(),
619                };
620
621                let mut found: Option<(&Sheet, &crate::core::table::ExcelTable)> =
622                    self.find_table(&ref_name).map(|t| (self, t));
623                if found.is_none()
624                    && let Some(ctx) = context
625                {
626                    for s in ctx.sheets.values() {
627                        if let Some(t) = s.find_table(&ref_name) {
628                            found = Some((s, t));
629                            break;
630                        }
631                    }
632                }
633
634                if let Some((table_sheet, excel_table)) = found {
635                    let is_self = table_sheet.name == self.name;
636                    let sheet_name = table_sheet.name.clone();
637
638                    let col_indices: Vec<(usize, usize)> = if let Some(col_name) = column {
639                        let local = excel_table.local_column_index(col_name).ok_or_else(|| {
640                            EngineError::EvalError(EvalError::UnknownFunction(format!(
641                                "Column not found: {}",
642                                col_name
643                            )))
644                        })?;
645                        vec![(local, excel_table.start_col + local)]
646                    } else {
647                        (0..excel_table.columns.len())
648                            .map(|local| (local, excel_table.start_col + local))
649                            .collect()
650                    };
651                    let is_whole_table = column.is_none();
652
653                    match section {
654                        SheetSection::Headers => {
655                            let names: Vec<ResultData> = col_indices
656                                .iter()
657                                .map(|&(local, _)| {
658                                    ResultData::String(
659                                        excel_table.columns.get(local).cloned().unwrap_or_default(),
660                                    )
661                                })
662                                .collect();
663                            if is_whole_table {
664                                Ok(ResultData::List(names))
665                            } else {
666                                Ok(names.into_iter().next().unwrap_or(ResultData::None))
667                            }
668                        }
669                        SheetSection::Totals => {
670                            if let Some(totals_row) = excel_table.totals_row() {
671                                let mut results = Vec::new();
672                                for &(_, col_idx) in &col_indices {
673                                    let cell_ref = CellRef::new(totals_row, col_idx);
674                                    if is_self {
675                                        deps.push(Dependency::Local(cell_ref));
676                                    } else {
677                                        deps.push(Dependency::Remote {
678                                            sheet: sheet_name.clone(),
679                                            cell: cell_ref,
680                                        });
681                                    }
682                                    results.push(table_sheet.get_result_data(&cell_ref));
683                                }
684                                if is_whole_table {
685                                    Ok(ResultData::List(results))
686                                } else {
687                                    Ok(results.into_iter().next().unwrap_or(ResultData::None))
688                                }
689                            } else {
690                                Ok(ResultData::None)
691                            }
692                        }
693                        SheetSection::Data | SheetSection::All => {
694                            if *is_this_row {
695                                let r = row.ok_or_else(|| {
696                                    EngineError::EvalError(EvalError::UnknownFunction(
697                                        "This row reference cannot be evaluated without row context"
698                                            .to_string(),
699                                    ))
700                                })?;
701                                let mut results = Vec::new();
702                                for &(_, col_idx) in &col_indices {
703                                    let cell_ref = CellRef::new(r, col_idx);
704                                    if is_self {
705                                        deps.push(Dependency::Local(cell_ref));
706                                    } else {
707                                        deps.push(Dependency::Remote {
708                                            sheet: sheet_name.clone(),
709                                            cell: cell_ref,
710                                        });
711                                    }
712                                    results.push(table_sheet.get_result_data(&cell_ref));
713                                }
714                                if is_whole_table {
715                                    Ok(ResultData::List(results))
716                                } else {
717                                    Ok(results.into_iter().next().unwrap_or(ResultData::None))
718                                }
719                            } else {
720                                let mut results = Vec::new();
721                                for &(_, col_idx) in &col_indices {
722                                    for r in
723                                        excel_table.data_start_row()..=excel_table.data_end_row()
724                                    {
725                                        let cell_ref = CellRef::new(r, col_idx);
726                                        if is_self {
727                                            deps.push(Dependency::Local(cell_ref));
728                                        } else {
729                                            deps.push(Dependency::Remote {
730                                                sheet: sheet_name.clone(),
731                                                cell: cell_ref,
732                                            });
733                                        }
734                                        results.push(table_sheet.get_result_data(&cell_ref));
735                                    }
736                                }
737                                Ok(ResultData::List(results))
738                            }
739                        }
740                    }
741                } else {
742                    let sheet_name = ref_name;
743                    let is_self = sheet_name == self.name;
744
745                    let target_sheet = if is_self {
746                        self
747                    } else if let Some(ctx) = context {
748                        if let Some(sheet) = ctx.sheets.get(&sheet_name) {
749                            sheet
750                        } else {
751                            return Err(EngineError::EvalError(EvalError::UnknownFunction(
752                                format!("Sheet not found: {}", sheet_name),
753                            )));
754                        }
755                    } else {
756                        return Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
757                            "No context to resolve sheet reference: {}",
758                            sheet_name
759                        ))));
760                    };
761
762                    let col_indices: Vec<usize> = if let Some(col_name) = column {
763                        let pos = target_sheet
764                            .columns
765                            .iter()
766                            .position(|c| c.name == *col_name)
767                            .ok_or_else(|| {
768                                EngineError::EvalError(EvalError::UnknownFunction(format!(
769                                    "Column not found: {}",
770                                    col_name
771                                )))
772                            })?;
773                        vec![pos]
774                    } else {
775                        (0..target_sheet.columns.len()).collect()
776                    };
777                    let is_whole_table = column.is_none();
778
779                    match section {
780                        SheetSection::Headers => {
781                            let names: Vec<ResultData> = col_indices
782                                .iter()
783                                .map(|&idx| {
784                                    ResultData::String(
785                                        target_sheet
786                                            .columns
787                                            .get(idx)
788                                            .map(|c| c.name.clone())
789                                            .unwrap_or_default(),
790                                    )
791                                })
792                                .collect();
793                            if is_whole_table {
794                                Ok(ResultData::List(names))
795                            } else {
796                                Ok(names.into_iter().next().unwrap_or(ResultData::None))
797                            }
798                        }
799                        SheetSection::Totals => Ok(ResultData::None),
800                        SheetSection::Data | SheetSection::All => {
801                            if *is_this_row {
802                                let r = row.ok_or_else(|| {
803                                    EngineError::EvalError(EvalError::UnknownFunction(
804                                        "This row reference cannot be evaluated without row context"
805                                            .to_string(),
806                                    ))
807                                })?;
808                                let mut results = Vec::new();
809                                for &col_idx in &col_indices {
810                                    let cell_ref = CellRef::new(r, col_idx);
811                                    if is_self {
812                                        deps.push(Dependency::Local(cell_ref));
813                                    } else {
814                                        deps.push(Dependency::Remote {
815                                            sheet: sheet_name.clone(),
816                                            cell: cell_ref,
817                                        });
818                                    }
819                                    results.push(target_sheet.get_result_data(&cell_ref));
820                                }
821                                if is_whole_table {
822                                    Ok(ResultData::List(results))
823                                } else {
824                                    Ok(results.into_iter().next().unwrap_or(ResultData::None))
825                                }
826                            } else {
827                                let mut results = Vec::new();
828                                for &col_idx in &col_indices {
829                                    if is_self {
830                                        deps.push(Dependency::LocalColumn(col_idx));
831                                    } else {
832                                        deps.push(Dependency::RemoteColumn {
833                                            sheet: sheet_name.clone(),
834                                            col: col_idx,
835                                        });
836                                    }
837                                    for r in 0..target_sheet.row_count() {
838                                        let cell_ref = CellRef::new(r, col_idx);
839                                        results.push(target_sheet.get_result_data(&cell_ref));
840                                    }
841                                }
842                                Ok(ResultData::List(results))
843                            }
844                        }
845                    }
846                }
847            }
848            Expr::CellRef {
849                sheet,
850                row: r_val,
851                col,
852                ..
853            } => {
854                let cell_ref = CellRef::new(*r_val, *col);
855                let is_self = match sheet {
856                    Some(name) => name == &self.name,
857                    None => true,
858                };
859
860                if is_self {
861                    deps.push(Dependency::Local(cell_ref));
862                    Ok(self.get_result_data(&cell_ref))
863                } else {
864                    let name = sheet.as_ref().unwrap().clone();
865                    deps.push(Dependency::Remote {
866                        sheet: name.clone(),
867                        cell: cell_ref,
868                    });
869
870                    if let Some(ctx) = context {
871                        if let Some(t) = ctx.sheets.get(&name) {
872                            Ok(t.get_result_data(&cell_ref))
873                        } else {
874                            Err(EngineError::EvalError(EvalError::UnknownFunction(format!(
875                                "Sheet not found: {}",
876                                name
877                            ))))
878                        }
879                    } else {
880                        Err(EngineError::EvalError(EvalError::UnknownFunction(
881                            "No context to resolve sheet reference".to_string(),
882                        )))
883                    }
884                }
885            }
886            Expr::RangeRef {
887                sheet,
888                start_row,
889                start_col,
890                end_row,
891                end_col,
892                ..
893            } => {
894                let is_self = match sheet {
895                    Some(name) => name == &self.name,
896                    None => true,
897                };
898
899                let target_sheet = if is_self {
900                    Some(self)
901                } else {
902                    context.and_then(|ctx| ctx.sheets.get(sheet.as_ref().unwrap()).copied())
903                };
904
905                let actual_end_row = if *end_row == usize::MAX {
906                    target_sheet
907                        .map(|t| t.row_count().saturating_sub(1))
908                        .unwrap_or(0)
909                } else {
910                    *end_row
911                };
912                let actual_end_col = if *end_col == usize::MAX {
913                    target_sheet
914                        .map(|t| t.col_count().saturating_sub(1))
915                        .unwrap_or(0)
916                } else {
917                    *end_col
918                };
919
920                let is_col_range = *end_row == usize::MAX;
921
922                let mut seen_col_deps: HashSet<usize> = HashSet::new();
923
924                let mut results = Vec::new();
925                for r in *start_row..=actual_end_row {
926                    for c in *start_col..=actual_end_col {
927                        let cell_ref = CellRef::new(r, c);
928                        if is_self {
929                            if is_col_range {
930                                if seen_col_deps.insert(c) {
931                                    let col_dep = Dependency::LocalColumn(c);
932                                    if !deps.contains(&col_dep) {
933                                        deps.push(col_dep);
934                                    }
935                                }
936                            } else {
937                                deps.push(Dependency::Local(cell_ref));
938                            }
939                            if row == Some(r) && col == Some(c) {
940                                results.push(ResultData::None);
941                            } else {
942                                results.push(self.get_result_data(&cell_ref));
943                            }
944                        } else {
945                            let name = sheet.as_ref().unwrap().clone();
946                            if is_col_range {
947                                if seen_col_deps.insert(c) {
948                                    let col_dep = Dependency::RemoteColumn {
949                                        sheet: name.clone(),
950                                        col: c,
951                                    };
952                                    if !deps.contains(&col_dep) {
953                                        deps.push(col_dep);
954                                    }
955                                }
956                            } else {
957                                deps.push(Dependency::Remote {
958                                    sheet: name.clone(),
959                                    cell: cell_ref,
960                                });
961                            }
962                            if let Some(ctx) = context {
963                                if let Some(t) = ctx.sheets.get(&name) {
964                                    results.push(t.get_result_data(&cell_ref));
965                                } else {
966                                    return Err(EngineError::EvalError(
967                                        EvalError::UnknownFunction(format!(
968                                            "Sheet not found: {}",
969                                            name
970                                        )),
971                                    ));
972                                }
973                            } else {
974                                return Err(EngineError::EvalError(EvalError::UnknownFunction(
975                                    "No context to resolve sheet reference".to_string(),
976                                )));
977                            }
978                        }
979                    }
980                }
981                Ok(ResultData::List(results))
982            }
983            Expr::List(list) => {
984                let mut results = Vec::new();
985                for item in list {
986                    results.push(self.evaluate_ast(item, context, row, col, deps, scope)?);
987                }
988                Ok(ResultData::List(results))
989            }
990            Expr::Slice { expr, start, end } => {
991                let target_val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
992                if let ResultData::Error(_) = &target_val {
993                    return Ok(target_val);
994                }
995                if let ResultData::List(list) = target_val {
996                    let len = list.len() as isize;
997                    let start_idx = if let Some(start_expr) = start {
998                        let s_val =
999                            self.evaluate_ast(start_expr, context, row, col, deps, scope)?;
1000                        if let ResultData::Error(_) = &s_val {
1001                            return Ok(s_val);
1002                        }
1003                        let s = self.to_f64(&s_val).unwrap_or(0.0) as isize;
1004                        if s < 0 {
1005                            (len + s).max(0) as usize
1006                        } else {
1007                            s.min(len) as usize
1008                        }
1009                    } else {
1010                        0
1011                    };
1012
1013                    let end_idx = if let Some(end_expr) = end {
1014                        let e_val = self.evaluate_ast(end_expr, context, row, col, deps, scope)?;
1015                        if let ResultData::Error(_) = &e_val {
1016                            return Ok(e_val);
1017                        }
1018                        let e = self.to_f64(&e_val).unwrap_or(len as f64) as isize;
1019                        if e < 0 {
1020                            (len + e).max(0) as usize
1021                        } else {
1022                            e.min(len) as usize
1023                        }
1024                    } else {
1025                        len as usize
1026                    };
1027
1028                    let sliced = if start_idx < end_idx && start_idx < list.len() {
1029                        list[start_idx..end_idx.min(list.len())].to_vec()
1030                    } else {
1031                        Vec::new()
1032                    };
1033                    Ok(ResultData::List(sliced))
1034                } else {
1035                    Ok(ResultData::None)
1036                }
1037            }
1038            Expr::UnaryOp { op, expr } => {
1039                let val = self.evaluate_ast(expr, context, row, col, deps, scope)?;
1040                match op {
1041                    Op::Sub => match val {
1042                        ResultData::Float(f) => Ok(ResultData::Float(-f)),
1043                        ResultData::Integer(i) => Ok(ResultData::Integer(-i)),
1044                        _ => Err(EngineError::EvalError(EvalError::UnknownFunction(
1045                            "Unary minus expects number".to_string(),
1046                        ))),
1047                    },
1048                    _ => Ok(val),
1049                }
1050            }
1051            Expr::BinaryOp { op, left, right } => {
1052                let l_val = self.evaluate_ast(left, context, row, col, deps, scope)?;
1053
1054                match op {
1055                    Op::Eq | Op::Ne | Op::Lt | Op::Gt | Op::Le | Op::Ge => {
1056                        if let ResultData::Error(_) = &l_val {
1057                            return Ok(l_val);
1058                        }
1059                        let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1060                        if let ResultData::Error(_) = &r_val {
1061                            return Ok(r_val);
1062                        }
1063                        let ord = Self::compare_excel_values(&l_val, &r_val);
1064                        let b = match op {
1065                            Op::Eq => ord.is_eq(),
1066                            Op::Ne => !ord.is_eq(),
1067                            Op::Lt => ord.is_lt(),
1068                            Op::Gt => ord.is_gt(),
1069                            Op::Le => ord.is_le(),
1070                            Op::Ge => ord.is_ge(),
1071                            _ => unreachable!(),
1072                        };
1073                        Ok(ResultData::Boolean(b))
1074                    }
1075                    _ => {
1076                        if let ResultData::Error(_) = &l_val {
1077                            return Ok(l_val);
1078                        }
1079                        let lf = match self.to_f64(&l_val) {
1080                            Some(f) => f,
1081                            None => return Ok(ResultData::Error("#VALUE!".to_string())),
1082                        };
1083                        let r_val = self.evaluate_ast(right, context, row, col, deps, scope)?;
1084                        if let ResultData::Error(_) = &r_val {
1085                            return Ok(r_val);
1086                        }
1087                        let rf = match self.to_f64(&r_val) {
1088                            Some(f) => f,
1089                            None => return Ok(ResultData::Error("#VALUE!".to_string())),
1090                        };
1091                        match op {
1092                            Op::Add => Ok(ResultData::Float(lf + rf)),
1093                            Op::Sub => Ok(ResultData::Float(lf - rf)),
1094                            Op::Mul => Ok(ResultData::Float(lf * rf)),
1095                            Op::Div => {
1096                                if rf == 0.0 {
1097                                    return Ok(ResultData::Error("#DIV/0!".to_string()));
1098                                }
1099                                Ok(ResultData::Float(lf / rf))
1100                            }
1101                            Op::Exp => {
1102                                if lf == 0.0 && rf == 0.0 {
1103                                    return Ok(ResultData::Error("#NUM!".to_string()));
1104                                }
1105                                if lf == 0.0 && rf < 0.0 {
1106                                    return Ok(ResultData::Error("#DIV/0!".to_string()));
1107                                }
1108                                if lf < 0.0 {
1109                                    if rf.fract() != 0.0 || rf.abs() > 1e6 {
1110                                        return Ok(ResultData::Error("#NUM!".to_string()));
1111                                    }
1112                                    let res = lf.powi(rf as i32);
1113                                    if res.is_nan() || res.is_infinite() {
1114                                        return Ok(ResultData::Error("#NUM!".to_string()));
1115                                    }
1116                                    return Ok(ResultData::Float(res));
1117                                }
1118                                let res = lf.powf(rf);
1119                                if res.is_nan() || res.is_infinite() {
1120                                    return Ok(ResultData::Error("#NUM!".to_string()));
1121                                }
1122                                Ok(ResultData::Float(res))
1123                            }
1124                            _ => unreachable!(),
1125                        }
1126                    }
1127                }
1128            }
1129            Expr::FunctionCall { name, args } => {
1130                self.evaluate_function(name, args, context, row, col, deps, scope)
1131            }
1132        }
1133    }
1134
1135    fn excel_type_rank(val: &ResultData) -> u8 {
1136        match val {
1137            ResultData::None => 0,
1138            ResultData::Integer(_) | ResultData::Float(_) => 1,
1139            ResultData::String(_) => 2,
1140            ResultData::Boolean(_) => 3,
1141            _ => 4,
1142        }
1143    }
1144
1145    fn compare_excel_values(l: &ResultData, r: &ResultData) -> std::cmp::Ordering {
1146        match (l, r) {
1147            (ResultData::None, ResultData::None) => return std::cmp::Ordering::Equal,
1148            (ResultData::None, ResultData::Integer(b)) => {
1149                return 0.0
1150                    .partial_cmp(&(*b as f64))
1151                    .unwrap_or(std::cmp::Ordering::Equal);
1152            }
1153            (ResultData::None, ResultData::Float(b)) => {
1154                return 0.0.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal);
1155            }
1156            (ResultData::Integer(a), ResultData::None) => {
1157                return (*a as f64)
1158                    .partial_cmp(&0.0)
1159                    .unwrap_or(std::cmp::Ordering::Equal);
1160            }
1161            (ResultData::Float(a), ResultData::None) => {
1162                return a.partial_cmp(&0.0).unwrap_or(std::cmp::Ordering::Equal);
1163            }
1164            (ResultData::None, ResultData::String(b)) => {
1165                return "".cmp(b.to_lowercase().as_str());
1166            }
1167            (ResultData::String(a), ResultData::None) => {
1168                return a.to_lowercase().as_str().cmp("");
1169            }
1170            (ResultData::None, ResultData::Boolean(b)) => {
1171                return false.cmp(b);
1172            }
1173            (ResultData::Boolean(a), ResultData::None) => {
1174                return a.cmp(&false);
1175            }
1176            _ => {}
1177        }
1178
1179        let rank_l = Self::excel_type_rank(l);
1180        let rank_r = Self::excel_type_rank(r);
1181        if rank_l != rank_r {
1182            return rank_l.cmp(&rank_r);
1183        }
1184        match (l, r) {
1185            (ResultData::Integer(a), ResultData::Integer(b)) => a.cmp(b),
1186            (ResultData::Float(a), ResultData::Float(b)) => {
1187                a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
1188            }
1189            (ResultData::Integer(a), ResultData::Float(b)) => (*a as f64)
1190                .partial_cmp(b)
1191                .unwrap_or(std::cmp::Ordering::Equal),
1192            (ResultData::Float(a), ResultData::Integer(b)) => a
1193                .partial_cmp(&(*b as f64))
1194                .unwrap_or(std::cmp::Ordering::Equal),
1195            (ResultData::Boolean(a), ResultData::Boolean(b)) => a.cmp(b),
1196            (ResultData::String(a), ResultData::String(b)) => Self::compare_excel_strings(a, b),
1197            _ => std::cmp::Ordering::Equal,
1198        }
1199    }
1200
1201    fn sort_compare_blanks_last(
1202        l: &ResultData,
1203        r: &ResultData,
1204        sort_order: f64,
1205    ) -> std::cmp::Ordering {
1206        match (matches!(l, ResultData::None), matches!(r, ResultData::None)) {
1207            (true, true) => std::cmp::Ordering::Equal,
1208            (true, false) => std::cmp::Ordering::Greater,
1209            (false, true) => std::cmp::Ordering::Less,
1210            (false, false) => {
1211                let ord = Self::compare_excel_values(l, r);
1212                if sort_order < 0.0 { ord.reverse() } else { ord }
1213            }
1214        }
1215    }
1216
1217    fn is_excel_number_str(s: &str) -> bool {
1218        let s = s.trim();
1219        if s.is_empty() {
1220            return false;
1221        }
1222        let bytes = s.as_bytes();
1223        let first = bytes[0];
1224        if first == b'e' || first == b'E' {
1225            return false;
1226        }
1227        if (first == b'+' || first == b'-') && bytes.len() > 1 {
1228            let second = bytes[1];
1229            if second == b'e' || second == b'E' {
1230                return false;
1231            }
1232        }
1233        true
1234    }
1235
1236    fn compare_excel_strings(a: &str, b: &str) -> std::cmp::Ordering {
1237        let char_weight = |ch: char| -> u32 {
1238            match ch {
1239                ' ' => 0,
1240                '_' => 1,
1241                '-' => 2,
1242                ',' => 3,
1243                ';' => 4,
1244                ':' => 5,
1245                '!' => 6,
1246                '?' => 7,
1247                '.' => 8,
1248                '\'' => 9,
1249                '"' => 10,
1250                '(' => 11,
1251                ')' => 12,
1252                '[' => 13,
1253                ']' => 14,
1254                '{' => 15,
1255                '}' => 16,
1256                '@' => 17,
1257                '*' => 18,
1258                '/' => 19,
1259                '\\' => 20,
1260                '&' => 21,
1261                '#' => 22,
1262                '%' => 23,
1263                '`' => 24,
1264                '^' => 25,
1265                '+' => 26,
1266                '<' => 27,
1267                '=' => 28,
1268                '>' => 29,
1269                '|' => 30,
1270                '~' => 31,
1271                '$' => 32,
1272                '0'..='9' => 33 + (ch as u32 - '0' as u32),
1273                'A'..='Z' => 43 + (ch as u32 - 'A' as u32),
1274                'a'..='z' => 43 + (ch as u32 - 'a' as u32),
1275                _ => ch
1276                    .to_lowercase()
1277                    .next()
1278                    .map(|c| c as u32 + 200)
1279                    .unwrap_or(ch as u32 + 200),
1280            }
1281        };
1282
1283        for (ca, cb) in a.chars().zip(b.chars()) {
1284            let wa = char_weight(ca);
1285            let wb = char_weight(cb);
1286            if wa != wb {
1287                return wa.cmp(&wb);
1288            }
1289        }
1290        a.len().cmp(&b.len())
1291    }
1292
1293    pub(crate) fn clean_float(val: f64) -> f64 {
1294        if val == 0.0 || !val.is_finite() {
1295            return val;
1296        }
1297        let abs_val = val.abs();
1298        let exp = abs_val.log10().floor() as i32;
1299        let factor = 10.0f64.powi(15 - 1 - exp);
1300        if factor.is_finite() && factor != 0.0 {
1301            let rounded = (val * factor).round() / factor;
1302            if (val - rounded).abs() <= 1e-14 * abs_val {
1303                return rounded;
1304            }
1305        }
1306        val
1307    }
1308
1309    pub(crate) fn to_f64(&self, val: &ResultData) -> Option<f64> {
1310        match val {
1311            ResultData::None => Some(0.0),
1312            ResultData::Float(f) => Some(*f),
1313            ResultData::Integer(i) => Some(*i as f64),
1314            ResultData::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1315            ResultData::String(s) => {
1316                let s_trim = s.trim();
1317                if Self::is_excel_number_str(s_trim) {
1318                    if let Ok(f) = s_trim.parse::<f64>() {
1319                        return Some(f);
1320                    }
1321                    if let Some((date, _)) =
1322                        crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1323                    {
1324                        return Some(crate::core::date::date_to_excel_serial(date));
1325                    }
1326                    None
1327                } else if let Some((date, _)) =
1328                    crate::core::date::parse_date_with_locale(s_trim, &self.locale)
1329                {
1330                    Some(crate::core::date::date_to_excel_serial(date))
1331                } else {
1332                    None
1333                }
1334            }
1335            _ => None,
1336        }
1337    }
1338
1339    fn to_f64_arg(&self, arg_opt: Option<&ResultData>, fn_name: &str) -> Result<f64, EngineError> {
1340        let val = arg_opt.ok_or_else(|| {
1341            EngineError::EvalError(EvalError::UnknownFunction(format!(
1342                "{} requires argument",
1343                fn_name
1344            )))
1345        })?;
1346        if let ResultData::Error(e) = val {
1347            return Err(EngineError::EvalError(EvalError::UnknownFunction(
1348                e.clone(),
1349            )));
1350        }
1351        self.to_f64(val).ok_or_else(|| {
1352            EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1353        })
1354    }
1355
1356    fn find_error_in_args(args: &[ResultData]) -> Option<ResultData> {
1357        for arg in args {
1358            match arg {
1359                ResultData::Error(_) => return Some(arg.clone()),
1360                ResultData::List(list) => {
1361                    if let Some(err) = Self::find_error_in_args(list) {
1362                        return Some(err);
1363                    }
1364                }
1365                _ => {}
1366            }
1367        }
1368        None
1369    }
1370
1371    fn check_arg_errors(&self, args: &[ResultData], is_direct: &[bool]) -> Option<ResultData> {
1372        for (i, arg) in args.iter().enumerate() {
1373            match arg {
1374                ResultData::Error(_) => return Some(arg.clone()),
1375                ResultData::List(list) => {
1376                    if let Some(err) = self.check_arg_errors(list, &[]) {
1377                        return Some(err);
1378                    }
1379                }
1380                ResultData::String(_)
1381                    if is_direct.get(i).copied().unwrap_or(false) && self.to_f64(arg).is_none() =>
1382                {
1383                    return Some(ResultData::Error("#VALUE!".to_string()));
1384                }
1385                _ => {}
1386            }
1387        }
1388        None
1389    }
1390
1391    fn sum_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1392        match arg {
1393            ResultData::Float(f) => *f,
1394            ResultData::Integer(i) => *i as f64,
1395            ResultData::Boolean(b) => {
1396                if is_direct {
1397                    if *b { 1.0 } else { 0.0 }
1398                } else {
1399                    0.0
1400                }
1401            }
1402            ResultData::String(_) => {
1403                if is_direct {
1404                    self.to_f64(arg).unwrap_or(0.0)
1405                } else {
1406                    0.0
1407                }
1408            }
1409            ResultData::List(list) => {
1410                let mut sum = 0.0;
1411                for item in list {
1412                    sum += self.sum_helper(item, false);
1413                }
1414                sum
1415            }
1416            _ => 0.0,
1417        }
1418    }
1419
1420    fn flatten_finance_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1421        match arg {
1422            ResultData::Float(f) => vec![*f],
1423            ResultData::Integer(i) => vec![*i as f64],
1424            ResultData::Boolean(b) => {
1425                if is_direct {
1426                    vec![if *b { 1.0 } else { 0.0 }]
1427                } else {
1428                    vec![]
1429                }
1430            }
1431            ResultData::String(_) => {
1432                if is_direct {
1433                    self.to_f64(arg).into_iter().collect()
1434                } else {
1435                    vec![]
1436                }
1437            }
1438            ResultData::List(list) => list
1439                .iter()
1440                .flat_map(|v| self.flatten_finance_numbers(v, false))
1441                .collect(),
1442            _ => vec![],
1443        }
1444    }
1445
1446    fn flatten_stat_numbers(&self, arg: &ResultData, is_direct: bool) -> Vec<f64> {
1447        match arg {
1448            ResultData::Float(f) => vec![*f],
1449            ResultData::Integer(i) => vec![*i as f64],
1450            ResultData::Boolean(b) => {
1451                if is_direct {
1452                    vec![if *b { 1.0 } else { 0.0 }]
1453                } else {
1454                    vec![]
1455                }
1456            }
1457            ResultData::String(_) => {
1458                if is_direct {
1459                    self.to_f64(arg).into_iter().collect()
1460                } else {
1461                    vec![]
1462                }
1463            }
1464            ResultData::List(list) => list
1465                .iter()
1466                .flat_map(|v| self.flatten_stat_numbers(v, false))
1467                .collect(),
1468            _ => vec![],
1469        }
1470    }
1471
1472    fn flatten_positional(
1473        &self,
1474        arg: &ResultData,
1475        out: &mut Vec<Option<f64>>,
1476        first_err: &mut Option<String>,
1477    ) {
1478        match arg {
1479            ResultData::List(items) => {
1480                for item in items {
1481                    self.flatten_positional(item, out, first_err);
1482                }
1483            }
1484            ResultData::Float(f) => out.push(Some(*f)),
1485            ResultData::Integer(i) => out.push(Some(*i as f64)),
1486            ResultData::Error(e) => {
1487                if first_err.is_none() {
1488                    *first_err = Some(e.clone());
1489                }
1490                out.push(None);
1491            }
1492            _ => out.push(None),
1493        }
1494    }
1495
1496    fn positional_numbers(
1497        &self,
1498        arg: Option<&ResultData>,
1499        first_err: &mut Option<String>,
1500    ) -> Vec<Option<f64>> {
1501        let mut out = Vec::new();
1502        if let Some(a) = arg {
1503            self.flatten_positional(a, &mut out, first_err);
1504        }
1505        out
1506    }
1507
1508    fn pair_and_filter(
1509        xs_raw: Vec<Option<f64>>,
1510        ys_raw: Vec<Option<f64>>,
1511    ) -> Result<(Vec<f64>, Vec<f64>), String> {
1512        if xs_raw.len() != ys_raw.len() {
1513            return Err("#N/A".to_string());
1514        }
1515        let mut xs = Vec::with_capacity(xs_raw.len());
1516        let mut ys = Vec::with_capacity(ys_raw.len());
1517        for (x, y) in xs_raw.into_iter().zip(ys_raw) {
1518            if let (Some(x), Some(y)) = (x, y) {
1519                xs.push(x);
1520                ys.push(y);
1521            }
1522        }
1523        Ok((xs, ys))
1524    }
1525
1526    fn paired_args(
1527        &self,
1528        x_arg: Option<&ResultData>,
1529        y_arg: Option<&ResultData>,
1530    ) -> Result<(Vec<f64>, Vec<f64>), String> {
1531        for arg in [x_arg, y_arg].into_iter().flatten() {
1532            let scalar = match arg {
1533                ResultData::List(items) if items.len() == 1 => &items[0],
1534                other => other,
1535            };
1536            if let ResultData::Error(e) = scalar {
1537                return Err(e.clone());
1538            }
1539            if Self::is_empty_scalar_operand(arg) {
1540                return Err("#VALUE!".to_string());
1541            }
1542        }
1543        let mut first_err = None;
1544        let xs_raw = self.positional_numbers(x_arg, &mut first_err);
1545        let ys_raw = self.positional_numbers(y_arg, &mut first_err);
1546        if xs_raw.len() != ys_raw.len() {
1547            return Err("#N/A".to_string());
1548        }
1549        if let Some(e) = first_err {
1550            return Err(e);
1551        }
1552        Self::pair_and_filter(xs_raw, ys_raw)
1553    }
1554
1555    fn flatten_strict_inner(
1556        &self,
1557        arg: &ResultData,
1558        blanks: BlankPolicy,
1559        coerce_text: bool,
1560        out: &mut Vec<f64>,
1561    ) -> Result<(), String> {
1562        match arg {
1563            ResultData::List(items) => {
1564                for item in items {
1565                    self.flatten_strict_inner(item, blanks, coerce_text, out)?;
1566                }
1567                Ok(())
1568            }
1569            ResultData::Error(e) => Err(e.clone()),
1570            ResultData::Float(f) => {
1571                out.push(*f);
1572                Ok(())
1573            }
1574            ResultData::Integer(i) => {
1575                out.push(*i as f64);
1576                Ok(())
1577            }
1578            ResultData::None => match blanks {
1579                BlankPolicy::Zero => {
1580                    out.push(0.0);
1581                    Ok(())
1582                }
1583                BlankPolicy::Skip => Ok(()),
1584                BlankPolicy::Reject => Err("#VALUE!".to_string()),
1585            },
1586            ResultData::String(_) if coerce_text => match self.to_f64(arg) {
1587                Some(f) => {
1588                    out.push(f);
1589                    Ok(())
1590                }
1591                None => Err("#VALUE!".to_string()),
1592            },
1593            _ => Err("#VALUE!".to_string()),
1594        }
1595    }
1596
1597    fn flatten_strict_numbers(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1598        let mut out = Vec::new();
1599        self.flatten_strict_inner(arg, BlankPolicy::Zero, true, &mut out)?;
1600        Ok(out)
1601    }
1602
1603    fn flatten_skipping_blanks(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1604        let mut out = Vec::new();
1605        if let Some(a) = arg {
1606            self.flatten_strict_inner(a, BlankPolicy::Skip, true, &mut out)?;
1607        }
1608        Ok(out)
1609    }
1610
1611    fn flatten_skipping_blanks_no_text_coercion(
1612        &self,
1613        arg: Option<&ResultData>,
1614    ) -> Result<Vec<f64>, String> {
1615        let mut out = Vec::new();
1616        if let Some(a) = arg {
1617            self.flatten_strict_inner(a, BlankPolicy::Skip, false, &mut out)?;
1618        }
1619        Ok(out)
1620    }
1621
1622    fn flatten_numbers_only(&self, arg: &ResultData) -> Result<Vec<f64>, String> {
1623        let mut out = Vec::new();
1624        self.flatten_strict_inner(arg, BlankPolicy::Reject, false, &mut out)?;
1625        Ok(out)
1626    }
1627
1628    fn aggregate_range_number(val: &ResultData) -> Option<f64> {
1629        match val {
1630            ResultData::Float(f) => Some(*f),
1631            ResultData::Integer(i) => Some(*i as f64),
1632            _ => None,
1633        }
1634    }
1635
1636    fn flatten_numbers_only_arg(&self, arg: Option<&ResultData>) -> Result<Vec<f64>, String> {
1637        match arg {
1638            Some(a) => self.flatten_numbers_only(a),
1639            None => Ok(vec![]),
1640        }
1641    }
1642
1643    fn flatten_args_stat_numbers(
1644        &self,
1645        args: &[ResultData],
1646        is_direct: &[bool],
1647    ) -> Result<Vec<f64>, String> {
1648        let mut out = Vec::new();
1649        for (i, arg) in args.iter().enumerate() {
1650            let direct = is_direct.get(i).copied().unwrap_or(false);
1651            if direct && matches!(arg, ResultData::String(_)) && self.to_f64(arg).is_none() {
1652                return Err("#VALUE!".to_string());
1653            }
1654            out.extend(self.flatten_stat_numbers(arg, direct));
1655        }
1656        Ok(out)
1657    }
1658
1659    fn flatten_stat_numbers_a(
1660        &self,
1661        arg: &ResultData,
1662        is_direct: bool,
1663    ) -> Result<Vec<f64>, String> {
1664        Ok(match arg {
1665            ResultData::Float(f) => vec![*f],
1666            ResultData::Integer(i) => vec![*i as f64],
1667            ResultData::Boolean(b) => vec![if *b { 1.0 } else { 0.0 }],
1668            ResultData::String(_) => {
1669                if is_direct {
1670                    match self.to_f64(arg) {
1671                        Some(f) => vec![f],
1672                        None => return Err("#VALUE!".to_string()),
1673                    }
1674                } else {
1675                    vec![0.0]
1676                }
1677            }
1678            ResultData::Error(e) => return Err(e.clone()),
1679            ResultData::List(list) => {
1680                let mut out = Vec::new();
1681                for v in list {
1682                    out.extend(self.flatten_stat_numbers_a(v, false)?);
1683                }
1684                out
1685            }
1686            ResultData::None => vec![],
1687            _ => vec![0.0],
1688        })
1689    }
1690
1691    fn flatten_args_stat_numbers_a(
1692        &self,
1693        args: &[ResultData],
1694        is_direct: &[bool],
1695    ) -> Result<Vec<f64>, String> {
1696        let mut out = Vec::new();
1697        for (i, arg) in args.iter().enumerate() {
1698            out.extend(
1699                self.flatten_stat_numbers_a(arg, is_direct.get(i).copied().unwrap_or(false))?,
1700            );
1701        }
1702        Ok(out)
1703    }
1704
1705    fn extract_matrix(&self, arg: &ResultData) -> Vec<Vec<f64>> {
1706        match arg {
1707            ResultData::List(list) => {
1708                let mut rows = Vec::new();
1709                for item in list {
1710                    match item {
1711                        ResultData::List(sub_list) => {
1712                            let row: Vec<f64> =
1713                                sub_list.iter().flat_map(|v| self.to_f64(v)).collect();
1714                            if !row.is_empty() {
1715                                rows.push(row);
1716                            }
1717                        }
1718                        _ => {
1719                            if let Some(f) = self.to_f64(item) {
1720                                rows.push(vec![f]);
1721                            }
1722                        }
1723                    }
1724                }
1725                rows
1726            }
1727            _ => vec![],
1728        }
1729    }
1730
1731    fn matrix_from_arg(
1732        &self,
1733        expr: &crate::core::parser::Expr,
1734        value: &ResultData,
1735    ) -> Vec<Vec<f64>> {
1736        if let ResultData::List(items) = value
1737            && items.iter().any(|i| matches!(i, ResultData::List(_)))
1738        {
1739            return self.extract_matrix(value);
1740        }
1741        fn plain(v: &ResultData) -> Option<f64> {
1742            match v {
1743                ResultData::Float(f) => Some(*f),
1744                ResultData::Integer(i) => Some(*i as f64),
1745                _ => None,
1746            }
1747        }
1748        let items: Vec<&ResultData> = match value {
1749            ResultData::List(items) => items.iter().collect(),
1750            other => vec![other],
1751        };
1752        if items.iter().any(|v| plain(v).is_none()) {
1753            return Vec::new();
1754        }
1755        let flat: Vec<f64> = items.iter().filter_map(|v| plain(v)).collect();
1756        let cols = match Self::range_bounds(expr) {
1757            Some((_, _, start_col, _, end_col)) => end_col.saturating_sub(start_col) + 1,
1758            None => flat.len().max(1),
1759        };
1760        if cols == 0 || !flat.len().is_multiple_of(cols) {
1761            return self.extract_matrix(value);
1762        }
1763        flat.chunks(cols).map(|c| c.to_vec()).collect()
1764    }
1765
1766    fn paired_sum_has_no_numbers(&self, arg: Option<&ResultData>) -> bool {
1767        let mut ignored = None;
1768        let slots = self.positional_numbers(arg, &mut ignored);
1769        slots.iter().all(|v| v.is_none())
1770    }
1771
1772    fn is_empty_scalar_operand(arg: &ResultData) -> bool {
1773        let scalar = match arg {
1774            ResultData::List(items) if items.len() == 1 => &items[0],
1775            other => other,
1776        };
1777        matches!(scalar, ResultData::None)
1778    }
1779
1780    fn first_arg_is_boolean(args: &[ResultData]) -> bool {
1781        matches!(args.first(), Some(ResultData::Boolean(_)))
1782    }
1783
1784    fn opt_f64_arg(&self, args: &[ResultData], i: usize, default: f64) -> Result<f64, EngineError> {
1785        match args.get(i) {
1786            None => Ok(default),
1787            Some(ResultData::None) => Ok(0.0),
1788            Some(ResultData::Error(e)) => Err(EngineError::EvalError(EvalError::UnknownFunction(
1789                e.clone(),
1790            ))),
1791            Some(v) => self.to_f64(v).ok_or_else(|| {
1792                EngineError::EvalError(EvalError::UnknownFunction("#VALUE!".to_string()))
1793            }),
1794        }
1795    }
1796
1797    fn opt_f64(&self, args: &[ResultData], i: usize, default: f64) -> f64 {
1798        args.get(i).and_then(|v| self.to_f64(v)).unwrap_or(default)
1799    }
1800
1801    fn average_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, usize) {
1802        match arg {
1803            ResultData::Float(f) => (*f, 1),
1804            ResultData::Integer(i) => (*i as f64, 1),
1805            ResultData::Boolean(b) => {
1806                if is_direct {
1807                    (if *b { 1.0 } else { 0.0 }, 1)
1808                } else {
1809                    (0.0, 0)
1810                }
1811            }
1812            ResultData::String(_) => {
1813                if is_direct {
1814                    if let Some(f) = self.to_f64(arg) {
1815                        (f, 1)
1816                    } else {
1817                        (0.0, 0)
1818                    }
1819                } else {
1820                    (0.0, 0)
1821                }
1822            }
1823            ResultData::List(list) => {
1824                let mut sum = 0.0;
1825                let mut count = 0;
1826                for item in list {
1827                    let (s, c) = self.average_helper(item, false);
1828                    sum += s;
1829                    count += c;
1830                }
1831                (sum, count)
1832            }
1833            _ => (0.0, 0),
1834        }
1835    }
1836
1837    fn count_helper(&self, arg: &ResultData) -> usize {
1838        match arg {
1839            ResultData::Float(_) | ResultData::Integer(_) => 1,
1840            ResultData::List(list) => {
1841                let mut count = 0;
1842                for item in list {
1843                    count += self.count_helper(item);
1844                }
1845                count
1846            }
1847            _ => 0,
1848        }
1849    }
1850
1851    fn min_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1852        match arg {
1853            ResultData::Float(f) => *f,
1854            ResultData::Integer(i) => *i as f64,
1855            ResultData::Boolean(b) => {
1856                if is_direct {
1857                    if *b { 1.0 } else { 0.0 }
1858                } else {
1859                    f64::INFINITY
1860                }
1861            }
1862            ResultData::String(_) => {
1863                if is_direct {
1864                    self.to_f64(arg).unwrap_or(f64::INFINITY)
1865                } else {
1866                    f64::INFINITY
1867                }
1868            }
1869            ResultData::List(list) => {
1870                let mut min_val = f64::INFINITY;
1871                for item in list {
1872                    min_val = min_val.min(self.min_helper(item, false));
1873                }
1874                min_val
1875            }
1876            _ => f64::INFINITY,
1877        }
1878    }
1879
1880    fn max_helper(&self, arg: &ResultData, is_direct: bool) -> f64 {
1881        match arg {
1882            ResultData::Float(f) => *f,
1883            ResultData::Integer(i) => *i as f64,
1884            ResultData::Boolean(b) => {
1885                if is_direct {
1886                    if *b { 1.0 } else { 0.0 }
1887                } else {
1888                    f64::NEG_INFINITY
1889                }
1890            }
1891            ResultData::String(_) => {
1892                if is_direct {
1893                    self.to_f64(arg).unwrap_or(f64::NEG_INFINITY)
1894                } else {
1895                    f64::NEG_INFINITY
1896                }
1897            }
1898            ResultData::List(list) => {
1899                let mut max_val = f64::NEG_INFINITY;
1900                for item in list {
1901                    max_val = max_val.max(self.max_helper(item, false));
1902                }
1903                max_val
1904            }
1905            _ => f64::NEG_INFINITY,
1906        }
1907    }
1908
1909    fn concat_helper(&self, arg: &ResultData, out: &mut String) {
1910        match arg {
1911            ResultData::List(list) => {
1912                for item in list {
1913                    self.concat_helper(item, out);
1914                }
1915            }
1916            other => {
1917                out.push_str(&other.to_string());
1918            }
1919        }
1920    }
1921
1922    fn counta_helper(&self, arg: &ResultData) -> usize {
1923        match arg {
1924            ResultData::None => 0,
1925            ResultData::List(list) => {
1926                let mut count = 0;
1927                for item in list {
1928                    count += self.counta_helper(item);
1929                }
1930                count
1931            }
1932            _ => 1,
1933        }
1934    }
1935
1936    fn product_helper(&self, arg: &ResultData, is_direct: bool) -> (f64, bool) {
1937        match arg {
1938            ResultData::Float(f) => (*f, true),
1939            ResultData::Integer(i) => (*i as f64, true),
1940            ResultData::Boolean(b) => {
1941                if is_direct {
1942                    (if *b { 1.0 } else { 0.0 }, true)
1943                } else {
1944                    (1.0, false)
1945                }
1946            }
1947            ResultData::String(_) => {
1948                if is_direct {
1949                    if let Some(f) = self.to_f64(arg) {
1950                        (f, true)
1951                    } else {
1952                        (1.0, false)
1953                    }
1954                } else {
1955                    (1.0, false)
1956                }
1957            }
1958            ResultData::List(list) => {
1959                let mut prod = 1.0;
1960                let mut has_nums = false;
1961                for item in list {
1962                    let (p, h) = self.product_helper(item, false);
1963                    if h {
1964                        prod *= p;
1965                        has_nums = true;
1966                    }
1967                }
1968                (prod, has_nums)
1969            }
1970            _ => (1.0, false),
1971        }
1972    }
1973
1974    fn to_bool_opt(&self, val: &ResultData) -> Option<bool> {
1975        match val {
1976            ResultData::Boolean(b) => Some(*b),
1977            ResultData::Integer(i) => Some(*i != 0),
1978            ResultData::Float(f) => Some(*f != 0.0),
1979            ResultData::String(s) => {
1980                let s_trim = s.trim();
1981                if s_trim.eq_ignore_ascii_case("true") {
1982                    Some(true)
1983                } else if s_trim.eq_ignore_ascii_case("false") {
1984                    Some(false)
1985                } else if let Ok(f) = s_trim.parse::<f64>() {
1986                    Some(f != 0.0)
1987                } else {
1988                    None
1989                }
1990            }
1991            ResultData::None => Some(false),
1992            _ => None,
1993        }
1994    }
1995
1996    fn to_bool(&self, val: &ResultData) -> bool {
1997        self.to_bool_opt(val).unwrap_or(false)
1998    }
1999
2000    fn range_numeric(val: &ResultData) -> Option<f64> {
2001        match val {
2002            ResultData::Integer(i) => Some(*i as f64),
2003            ResultData::Float(f) => Some(*f),
2004            _ => None,
2005        }
2006    }
2007
2008    fn exact_lookup_matches(lookup: &ResultData, candidate: &ResultData) -> bool {
2009        if matches!(candidate, ResultData::None) {
2010            return false;
2011        }
2012        let lookup_key = match lookup {
2013            ResultData::None => "0".to_string(),
2014            other => other.to_string(),
2015        };
2016        candidate.to_string() == lookup_key
2017    }
2018
2019    fn wildcard_criteria_matches(pattern: &str, text: &str) -> bool {
2020        fn rec(pat: &[char], txt: &[char]) -> bool {
2021            if pat.is_empty() {
2022                return txt.is_empty();
2023            }
2024            match pat[0] {
2025                '*' => rec(&pat[1..], txt) || (!txt.is_empty() && rec(pat, &txt[1..])),
2026                '?' => !txt.is_empty() && rec(&pat[1..], &txt[1..]),
2027                '~' if pat.len() > 1 && matches!(pat[1], '*' | '?' | '~') => {
2028                    !txt.is_empty() && pat[1] == txt[0] && rec(&pat[2..], &txt[1..])
2029                }
2030                ch => !txt.is_empty() && ch == txt[0] && rec(&pat[1..], &txt[1..]),
2031            }
2032        }
2033
2034        let pat = pattern.to_lowercase().chars().collect::<Vec<_>>();
2035        let txt = text.to_lowercase().chars().collect::<Vec<_>>();
2036        rec(&pat, &txt)
2037    }
2038
2039    fn criteria_text_eq(val: &ResultData, pattern: &str) -> bool {
2040        let text = val.to_string();
2041        if pattern.contains('*') || pattern.contains('?') {
2042            matches!(val, ResultData::String(_)) && Self::wildcard_criteria_matches(pattern, &text)
2043        } else {
2044            text.to_lowercase() == pattern.to_lowercase()
2045        }
2046    }
2047
2048    fn match_criteria(&self, val: &ResultData, criteria: &ResultData) -> bool {
2049        let crit_str = criteria.to_string();
2050        if let Some(rest) = crit_str.strip_prefix(">=") {
2051            let val_f = match Self::range_numeric(val) {
2052                Some(f) => f,
2053                None => return false,
2054            };
2055            let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2056            val_f >= crit_f
2057        } else if let Some(rest) = crit_str.strip_prefix('>') {
2058            let val_f = match Self::range_numeric(val) {
2059                Some(f) => f,
2060                None => return false,
2061            };
2062            let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2063            val_f > crit_f
2064        } else if let Some(rest) = crit_str.strip_prefix("<>") {
2065            let remainder = rest.trim();
2066            !Self::criteria_text_eq(val, remainder)
2067        } else if let Some(rest) = crit_str.strip_prefix("<=") {
2068            let val_f = match Self::range_numeric(val) {
2069                Some(f) => f,
2070                None => return false,
2071            };
2072            let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2073            val_f <= crit_f
2074        } else if let Some(rest) = crit_str.strip_prefix('<') {
2075            let val_f = match Self::range_numeric(val) {
2076                Some(f) => f,
2077                None => return false,
2078            };
2079            let crit_f = rest.trim().parse::<f64>().unwrap_or(0.0);
2080            val_f < crit_f
2081        } else if let Some(rest) = crit_str.strip_prefix('=') {
2082            let remainder = rest.trim();
2083            Self::criteria_text_eq(val, remainder)
2084        } else {
2085            Self::criteria_text_eq(val, &crit_str)
2086        }
2087    }
2088
2089    fn range_bounds(
2090        expr: &crate::core::parser::Expr,
2091    ) -> Option<(Option<String>, usize, usize, usize, usize)> {
2092        use crate::core::parser::Expr;
2093        match expr {
2094            Expr::RangeRef {
2095                sheet,
2096                start_row,
2097                start_col,
2098                end_row,
2099                end_col,
2100                ..
2101            } => Some((sheet.clone(), *start_row, *start_col, *end_row, *end_col)),
2102            Expr::CellRef {
2103                sheet, row, col, ..
2104            } => Some((sheet.clone(), *row, *col, *row, *col)),
2105            _ => None,
2106        }
2107    }
2108
2109    fn materialize_range(
2110        &self,
2111        sheet_opt: &Option<String>,
2112        start_row: usize,
2113        start_col: usize,
2114        end_row: usize,
2115        end_col: usize,
2116        context: Option<&Context>,
2117    ) -> Option<Vec<Vec<ResultData>>> {
2118        let is_self = match sheet_opt {
2119            Some(name) => name == &self.name,
2120            None => true,
2121        };
2122        let source: &Sheet = if is_self {
2123            self
2124        } else {
2125            context?.sheets.get(sheet_opt.as_ref()?)?
2126        };
2127        let actual_end_row = if end_row == usize::MAX {
2128            source.row_count().saturating_sub(1)
2129        } else {
2130            end_row
2131        };
2132        let actual_end_col = if end_col == usize::MAX {
2133            source.col_count().saturating_sub(1)
2134        } else {
2135            end_col
2136        };
2137        if actual_end_row < start_row || actual_end_col < start_col {
2138            return Some(Vec::new());
2139        }
2140        let mut grid = Vec::with_capacity(actual_end_row - start_row + 1);
2141        for r in start_row..=actual_end_row {
2142            let mut row = Vec::with_capacity(actual_end_col - start_col + 1);
2143            for c in start_col..=actual_end_col {
2144                row.push(source.get_result_data(&CellRef::new(r, c)));
2145            }
2146            grid.push(row);
2147        }
2148        Some(grid)
2149    }
2150
2151    fn evaluate_database_function(
2152        &self,
2153        func_name: &str,
2154        args: &[crate::core::parser::Expr],
2155        evaluated_args: &[ResultData],
2156        context: Option<&Context>,
2157    ) -> Result<ResultData, EngineError> {
2158        if args.len() < 3 || evaluated_args.len() < 3 {
2159            return Ok(ResultData::Error("#VALUE!".to_string()));
2160        }
2161        let (db_sheet, db_sr, db_sc, db_er, db_ec) = match Self::range_bounds(&args[0]) {
2162            Some(v) => v,
2163            None => return Ok(ResultData::Error("#VALUE!".to_string())),
2164        };
2165        let (crit_sheet, crit_sr, crit_sc, crit_er, crit_ec) = match Self::range_bounds(&args[2]) {
2166            Some(v) => v,
2167            None => return Ok(ResultData::Error("#VALUE!".to_string())),
2168        };
2169        let db = match self.materialize_range(&db_sheet, db_sr, db_sc, db_er, db_ec, context) {
2170            Some(g) => g,
2171            None => return Ok(ResultData::Error("#REF!".to_string())),
2172        };
2173        let crit = match self.materialize_range(
2174            &crit_sheet,
2175            crit_sr,
2176            crit_sc,
2177            crit_er,
2178            crit_ec,
2179            context,
2180        ) {
2181            Some(g) => g,
2182            None => return Ok(ResultData::Error("#REF!".to_string())),
2183        };
2184        if db.len() < 2 || crit.len() < 2 {
2185            return Ok(ResultData::Error("#VALUE!".to_string()));
2186        }
2187
2188        let db_headers: Vec<String> = db[0].iter().map(|v| v.to_string()).collect();
2189        let field_idx: usize = match &evaluated_args[1] {
2190            ResultData::String(s) => {
2191                match db_headers.iter().position(|h| h.eq_ignore_ascii_case(s)) {
2192                    Some(idx) => idx,
2193                    None => return Ok(ResultData::Error("#VALUE!".to_string())),
2194                }
2195            }
2196            other => match self.to_f64(other) {
2197                Some(n) if n >= 1.0 && (n as usize) <= db_headers.len() => n as usize - 1,
2198                _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2199            },
2200        };
2201
2202        let crit_headers: Vec<String> = crit[0].iter().map(|v| v.to_string()).collect();
2203        let crit_to_db: Vec<Option<usize>> = crit_headers
2204            .iter()
2205            .map(|h| db_headers.iter().position(|dh| dh.eq_ignore_ascii_case(h)))
2206            .collect();
2207
2208        let mut matched: Vec<ResultData> = Vec::new();
2209        for row in db.iter().skip(1) {
2210            let row_matches_any_criteria_row = crit.iter().skip(1).any(|crit_row| {
2211                crit_row.iter().enumerate().all(|(ci, cell)| {
2212                    if matches!(cell, ResultData::None) {
2213                        return true;
2214                    }
2215                    match crit_to_db.get(ci).copied().flatten() {
2216                        Some(db_col) => self.match_criteria(&row[db_col], cell),
2217                        None => false,
2218                    }
2219                })
2220            });
2221            if row_matches_any_criteria_row {
2222                matched.push(row[field_idx].clone());
2223            }
2224        }
2225
2226        match func_name {
2227            "DGET" => match matched.len() {
2228                0 => Ok(ResultData::Error("#VALUE!".to_string())),
2229                1 => Ok(matched.into_iter().next().unwrap()),
2230                _ => Ok(ResultData::Error("#NUM!".to_string())),
2231            },
2232            "DCOUNT" => Ok(ResultData::Float(
2233                matched
2234                    .iter()
2235                    .filter(|v| Self::range_numeric(v).is_some())
2236                    .count() as f64,
2237            )),
2238            "DCOUNTA" => Ok(ResultData::Float(
2239                matched.iter().map(|v| self.counta_helper(v)).sum::<usize>() as f64,
2240            )),
2241            _ => {
2242                let nums: Vec<f64> = matched.iter().filter_map(Self::range_numeric).collect();
2243                match func_name {
2244                    "DSUM" => Ok(ResultData::Float(nums.iter().sum())),
2245                    "DPRODUCT" => Ok(ResultData::Float(if nums.is_empty() {
2246                        0.0
2247                    } else {
2248                        nums.iter().product()
2249                    })),
2250                    "DMAX" => {
2251                        let m = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
2252                        Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2253                    }
2254                    "DMIN" => {
2255                        let m = nums.iter().cloned().fold(f64::INFINITY, f64::min);
2256                        Ok(ResultData::Float(if m.is_finite() { m } else { 0.0 }))
2257                    }
2258                    "DAVERAGE" => {
2259                        if nums.is_empty() {
2260                            Ok(ResultData::Error("#DIV/0!".to_string()))
2261                        } else {
2262                            Ok(ResultData::Float(
2263                                nums.iter().sum::<f64>() / nums.len() as f64,
2264                            ))
2265                        }
2266                    }
2267                    "DSTDEV" => match crate::core::stats::stdev_s(&nums) {
2268                        Ok(v) => Ok(ResultData::Float(v)),
2269                        Err(e) => Ok(ResultData::Error(e)),
2270                    },
2271                    "DSTDEVP" => match crate::core::stats::stdev_p(&nums) {
2272                        Ok(v) => Ok(ResultData::Float(v)),
2273                        Err(e) => Ok(ResultData::Error(e)),
2274                    },
2275                    "DVAR" => match crate::core::stats::var_s(&nums) {
2276                        Ok(v) => Ok(ResultData::Float(v)),
2277                        Err(e) => Ok(ResultData::Error(e)),
2278                    },
2279                    "DVARP" => match crate::core::stats::var_p(&nums) {
2280                        Ok(v) => Ok(ResultData::Float(v)),
2281                        Err(e) => Ok(ResultData::Error(e)),
2282                    },
2283                    _ => unreachable!(),
2284                }
2285            }
2286        }
2287    }
2288
2289    fn proper(&self, s: &str) -> String {
2290        let mut c_chars = Vec::new();
2291        let mut capitalize_next = true;
2292        for c in s.chars() {
2293            if c.is_alphabetic() {
2294                if capitalize_next {
2295                    c_chars.extend(c.to_uppercase());
2296                } else {
2297                    c_chars.extend(c.to_lowercase());
2298                }
2299                capitalize_next = false;
2300            } else {
2301                c_chars.push(c);
2302                capitalize_next = true;
2303            }
2304        }
2305        c_chars.into_iter().collect()
2306    }
2307
2308    fn get_ymd_hms(&self) -> ((i32, u32, u32), (u32, u32, u32)) {
2309        let now = web_time::SystemTime::now()
2310            .duration_since(web_time::SystemTime::UNIX_EPOCH)
2311            .unwrap_or_default()
2312            .as_secs();
2313        let secs_in_day = 86400;
2314        let days_since_epoch = (now / secs_in_day) as i32;
2315        let seconds_of_day = (now % secs_in_day) as u32;
2316
2317        let hour = seconds_of_day / 3600;
2318        let minute = (seconds_of_day % 3600) / 60;
2319        let second = seconds_of_day % 60;
2320
2321        let era = (if days_since_epoch >= -719468 {
2322            days_since_epoch + 719468
2323        } else {
2324            days_since_epoch + 719468 - 146096
2325        }) / 146097;
2326        let doe = (days_since_epoch + 719468 - era * 146097) as u32;
2327        let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
2328        let y = (yoe as i32) + era * 400;
2329        let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2330        let mp = (5 * doy + 2) / 153;
2331        let d = doy - (153 * mp + 2) / 5 + 1;
2332        let m = if mp < 10 { mp + 3 } else { mp - 9 };
2333        let year = if m <= 2 { y + 1 } else { y };
2334
2335        ((year, m, d), (hour, minute, second))
2336    }
2337
2338    fn evaluate_let(
2339        &self,
2340        args: &[crate::core::parser::Expr],
2341        context: Option<&Context>,
2342        row: Option<usize>,
2343        col: Option<usize>,
2344        deps: &mut Vec<Dependency>,
2345        scope: &LetScope<'_>,
2346    ) -> Result<ResultData, EngineError> {
2347        use crate::core::parser::Expr;
2348
2349        if args.is_empty() || args.len().is_multiple_of(2) {
2350            return Ok(ResultData::Error("#VALUE!".to_string()));
2351        }
2352        if args.len() == 1 {
2353            return self.evaluate_ast(&args[0], context, row, col, deps, scope);
2354        }
2355
2356        let name = match &args[0] {
2357            Expr::Identifier(n) => n.as_str(),
2358            _ => return Ok(ResultData::Error("#VALUE!".to_string())),
2359        };
2360        let remaining_pairs = args.len() / 2 - 1;
2361        let is_duplicate = args[2..]
2362            .iter()
2363            .step_by(2)
2364            .take(remaining_pairs)
2365            .any(|a| matches!(a, Expr::Identifier(n2) if n2.eq_ignore_ascii_case(name)));
2366        if is_duplicate {
2367            return Ok(ResultData::Error("#VALUE!".to_string()));
2368        }
2369
2370        let value = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2371        let inner_scope = LetScope::Bound {
2372            name,
2373            value: &value,
2374            parent: scope,
2375        };
2376        self.evaluate_let(&args[2..], context, row, col, deps, &inner_scope)
2377    }
2378
2379    fn extract_lambda(
2380        expr: &crate::core::parser::Expr,
2381    ) -> Option<(Vec<&str>, &crate::core::parser::Expr)> {
2382        use crate::core::parser::Expr;
2383        let Expr::FunctionCall { name, args } = expr else {
2384            return None;
2385        };
2386        if !name.eq_ignore_ascii_case("LAMBDA") || args.is_empty() {
2387            return None;
2388        }
2389        let (body, params) = args.split_last().unwrap();
2390        let param_names: Vec<&str> = params
2391            .iter()
2392            .filter_map(|p| match p {
2393                Expr::Identifier(n) => Some(n.as_str()),
2394                _ => None,
2395            })
2396            .collect();
2397        if param_names.len() != params.len() {
2398            return None;
2399        }
2400        Some((param_names, body))
2401    }
2402
2403    #[allow(clippy::too_many_arguments)]
2404    fn invoke_lambda<'v>(
2405        &self,
2406        params: &[&str],
2407        values: &'v [ResultData],
2408        body: &crate::core::parser::Expr,
2409        context: Option<&Context>,
2410        row: Option<usize>,
2411        col: Option<usize>,
2412        deps: &mut Vec<Dependency>,
2413        scope: &LetScope<'v>,
2414    ) -> Result<ResultData, EngineError> {
2415        match (params.split_first(), values.split_first()) {
2416            (Some((&pname, prest)), Some((vfirst, vrest))) => {
2417                let inner_scope = LetScope::Bound {
2418                    name: pname,
2419                    value: vfirst,
2420                    parent: scope,
2421                };
2422                self.invoke_lambda(prest, vrest, body, context, row, col, deps, &inner_scope)
2423            }
2424            _ => self.evaluate_ast(body, context, row, col, deps, scope),
2425        }
2426    }
2427
2428    fn eval_as_array(
2429        &self,
2430        expr: &crate::core::parser::Expr,
2431        context: Option<&Context>,
2432        row: Option<usize>,
2433        col: Option<usize>,
2434        deps: &mut Vec<Dependency>,
2435        scope: &LetScope<'_>,
2436    ) -> Result<Vec<ResultData>, EngineError> {
2437        Ok(
2438            match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2439                ResultData::List(items) => Self::flatten_row_major(items).0,
2440                other => vec![other],
2441            },
2442        )
2443    }
2444
2445    fn flatten_row_major(items: Vec<ResultData>) -> (Vec<ResultData>, Option<usize>) {
2446        if !items.is_empty() && items.iter().all(|v| matches!(v, ResultData::List(_))) {
2447            let cols = match &items[0] {
2448                ResultData::List(inner) => inner.len().max(1),
2449                _ => 1,
2450            };
2451            let flat = items
2452                .into_iter()
2453                .flat_map(|v| match v {
2454                    ResultData::List(inner) => inner,
2455                    other => vec![other],
2456                })
2457                .collect();
2458            (flat, Some(cols))
2459        } else {
2460            (items, None)
2461        }
2462    }
2463
2464    fn array_shape(
2465        &self,
2466        expr: &crate::core::parser::Expr,
2467        context: Option<&Context>,
2468        row: Option<usize>,
2469        col: Option<usize>,
2470        deps: &mut Vec<Dependency>,
2471        scope: &LetScope<'_>,
2472    ) -> Result<(Vec<ResultData>, usize), EngineError> {
2473        use crate::core::parser::Expr;
2474        let items = match self.evaluate_ast(expr, context, row, col, deps, scope)? {
2475            ResultData::List(items) => items,
2476            other => vec![other],
2477        };
2478        let (flat, nested_cols) = Self::flatten_row_major(items);
2479        if let Some(cols) = nested_cols {
2480            return Ok((flat, cols));
2481        }
2482        let num_cols = match expr {
2483            Expr::RangeRef {
2484                start_col, end_col, ..
2485            } => (end_col - start_col + 1).max(1),
2486            Expr::CellRef { .. } => 1,
2487            Expr::FunctionCall { name, args } => self
2488                .function_call_cols(name, args, context, row, col, deps, scope)
2489                .unwrap_or_else(|| flat.len().max(1)),
2490            _ => flat.len().max(1),
2491        };
2492        Ok((flat, num_cols))
2493    }
2494
2495    #[allow(clippy::too_many_arguments)]
2496    fn function_call_cols(
2497        &self,
2498        name: &str,
2499        args: &[crate::core::parser::Expr],
2500        context: Option<&Context>,
2501        row: Option<usize>,
2502        col: Option<usize>,
2503        deps: &mut Vec<Dependency>,
2504        scope: &LetScope<'_>,
2505    ) -> Option<usize> {
2506        let mut upper = name.to_ascii_uppercase();
2507        if let Some(rest) = upper.strip_prefix("_XLFN.") {
2508            upper = rest.to_string();
2509        }
2510        if let Some(rest) = upper.strip_prefix("_XLWS.") {
2511            upper = rest.to_string();
2512        }
2513        match upper.as_str() {
2514            "TRANSPOSE" => {
2515                let (flat, cols) = self
2516                    .array_shape(args.first()?, context, row, col, deps, scope)
2517                    .ok()?;
2518                Some((flat.len().checked_div(cols).unwrap_or(0)).max(1))
2519            }
2520            "HSTACK" => {
2521                let mut total = 0usize;
2522                for a in args {
2523                    total += self.array_shape(a, context, row, col, deps, scope).ok()?.1;
2524                }
2525                Some(total)
2526            }
2527            "VSTACK" => {
2528                let mut max_cols = 0usize;
2529                for a in args {
2530                    max_cols =
2531                        max_cols.max(self.array_shape(a, context, row, col, deps, scope).ok()?.1);
2532                }
2533                Some(max_cols)
2534            }
2535            "CHOOSEROWS" => Some(
2536                self.array_shape(args.first()?, context, row, col, deps, scope)
2537                    .ok()?
2538                    .1,
2539            ),
2540            "CHOOSECOLS" => Some(args.len().saturating_sub(1).max(1)),
2541            "DROP" | "TAKE" => {
2542                let (_, cols) = self
2543                    .array_shape(args.first()?, context, row, col, deps, scope)
2544                    .ok()?;
2545                let is_take = upper == "TAKE";
2546                match args.get(2) {
2547                    Some(e) => {
2548                        let n = self
2549                            .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2550                            .unwrap_or(0.0) as isize;
2551                        let (s, e2) = Self::drop_take_bounds(cols as isize, n, is_take);
2552                        Some((e2 - s).max(0) as usize)
2553                    }
2554                    None => Some(if is_take { cols } else { 0 }),
2555                }
2556            }
2557            "EXPAND" => {
2558                let (_, cols) = self
2559                    .array_shape(args.first()?, context, row, col, deps, scope)
2560                    .ok()?;
2561                match args.get(2) {
2562                    Some(e) => Some(
2563                        self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2564                            .unwrap_or(cols as f64) as usize,
2565                    ),
2566                    None => Some(cols),
2567                }
2568            }
2569            "TOCOL" => Some(1),
2570            "WRAPROWS" => {
2571                let n = self
2572                    .to_f64(
2573                        &self
2574                            .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2575                            .ok()?,
2576                    )
2577                    .unwrap_or(1.0)
2578                    .max(1.0) as usize;
2579                Some(n)
2580            }
2581            "WRAPCOLS" => {
2582                let (flat, _) = self
2583                    .array_shape(args.first()?, context, row, col, deps, scope)
2584                    .ok()?;
2585                let wrap = self
2586                    .to_f64(
2587                        &self
2588                            .evaluate_ast(args.get(1)?, context, row, col, deps, scope)
2589                            .ok()?,
2590                    )
2591                    .unwrap_or(1.0)
2592                    .max(1.0) as usize;
2593                Some(flat.len().div_ceil(wrap).max(1))
2594            }
2595            "UNIQUE" | "SORT" | "SORTBY" | "FILTER" | "TRIMRANGE" => Some(
2596                self.array_shape(args.first()?, context, row, col, deps, scope)
2597                    .ok()?
2598                    .1,
2599            ),
2600            "SEQUENCE" => match args.get(1) {
2601                Some(e) => Some(
2602                    self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2603                        .unwrap_or(1.0)
2604                        .max(1.0) as usize,
2605                ),
2606                None => Some(1),
2607            },
2608            "MUNIT" => {
2609                let n = self
2610                    .to_f64(
2611                        &self
2612                            .evaluate_ast(args.first()?, context, row, col, deps, scope)
2613                            .ok()?,
2614                    )
2615                    .unwrap_or(1.0)
2616                    .max(1.0) as usize;
2617                Some(n)
2618            }
2619            "MAKEARRAY" => match args.get(1) {
2620                Some(e) => Some(
2621                    self.to_f64(&self.evaluate_ast(e, context, row, col, deps, scope).ok()?)
2622                        .unwrap_or(1.0)
2623                        .max(1.0) as usize,
2624                ),
2625                None => Some(1),
2626            },
2627            _ => None,
2628        }
2629    }
2630
2631    fn drop_take_bounds(total: isize, n: isize, is_take: bool) -> (isize, isize) {
2632        let n = n.clamp(-total, total);
2633        if is_take {
2634            if n >= 0 { (0, n) } else { (total + n, total) }
2635        } else if n >= 0 {
2636            (n, total)
2637        } else {
2638            (0, total + n)
2639        }
2640    }
2641
2642    #[allow(clippy::too_many_arguments)]
2643    fn evaluate_lambda_function(
2644        &self,
2645        func_name: &str,
2646        args: &[crate::core::parser::Expr],
2647        context: Option<&Context>,
2648        row: Option<usize>,
2649        col: Option<usize>,
2650        deps: &mut Vec<Dependency>,
2651        scope: &LetScope<'_>,
2652    ) -> Result<ResultData, EngineError> {
2653        use crate::core::parser::Expr;
2654
2655        match func_name {
2656            "MAP" => {
2657                if args.len() < 2 {
2658                    return Ok(ResultData::Error("#VALUE!".to_string()));
2659                }
2660                let (lambda_expr, array_exprs) = args.split_last().unwrap();
2661                let Some((params, body)) = Self::extract_lambda(lambda_expr) else {
2662                    return Ok(ResultData::Error("#VALUE!".to_string()));
2663                };
2664                if params.len() != array_exprs.len() {
2665                    return Ok(ResultData::Error("#VALUE!".to_string()));
2666                }
2667                let arrays: Vec<Vec<ResultData>> = array_exprs
2668                    .iter()
2669                    .map(|e| self.eval_as_array(e, context, row, col, deps, scope))
2670                    .collect::<Result<_, _>>()?;
2671                let len = arrays.iter().map(|a| a.len()).max().unwrap_or(0);
2672                let mut results = Vec::with_capacity(len);
2673                for i in 0..len {
2674                    let values: Vec<ResultData> = arrays
2675                        .iter()
2676                        .map(|a| a.get(i).cloned().unwrap_or(ResultData::None))
2677                        .collect();
2678                    results.push(
2679                        self.invoke_lambda(&params, &values, body, context, row, col, deps, scope)?,
2680                    );
2681                }
2682                Ok(ResultData::List(results))
2683            }
2684            "BYROW" | "BYCOL" => {
2685                if args.len() != 2 {
2686                    return Ok(ResultData::Error("#VALUE!".to_string()));
2687                }
2688                let Some((params, body)) = Self::extract_lambda(&args[1]) else {
2689                    return Ok(ResultData::Error("#VALUE!".to_string()));
2690                };
2691                if params.len() != 1 {
2692                    return Ok(ResultData::Error("#VALUE!".to_string()));
2693                }
2694                let num_cols = match &args[0] {
2695                    Expr::RangeRef {
2696                        start_col, end_col, ..
2697                    } => (end_col - start_col + 1).max(1),
2698                    _ => 1,
2699                };
2700                let flat = self.eval_as_array(&args[0], context, row, col, deps, scope)?;
2701                let num_rows = if num_cols == 0 {
2702                    0
2703                } else {
2704                    flat.len().div_ceil(num_cols)
2705                };
2706                let mut results = Vec::new();
2707                if func_name == "BYROW" {
2708                    for r in 0..num_rows {
2709                        let row_vals: Vec<ResultData> = (0..num_cols)
2710                            .filter_map(|c| flat.get(r * num_cols + c).cloned())
2711                            .collect();
2712                        let arg = vec![ResultData::List(row_vals)];
2713                        results.push(
2714                            self.invoke_lambda(
2715                                &params, &arg, body, context, row, col, deps, scope,
2716                            )?,
2717                        );
2718                    }
2719                } else {
2720                    for c in 0..num_cols {
2721                        let col_vals: Vec<ResultData> = (0..num_rows)
2722                            .filter_map(|r| flat.get(r * num_cols + c).cloned())
2723                            .collect();
2724                        let arg = vec![ResultData::List(col_vals)];
2725                        results.push(
2726                            self.invoke_lambda(
2727                                &params, &arg, body, context, row, col, deps, scope,
2728                            )?,
2729                        );
2730                    }
2731                }
2732                Ok(ResultData::List(results))
2733            }
2734            "REDUCE" | "SCAN" => {
2735                if args.len() != 2 && args.len() != 3 {
2736                    return Ok(ResultData::Error("#VALUE!".to_string()));
2737                }
2738                let lambda_idx = args.len() - 1;
2739                let array_idx = args.len() - 2;
2740                let Some((params, body)) = Self::extract_lambda(&args[lambda_idx]) else {
2741                    return Ok(ResultData::Error("#VALUE!".to_string()));
2742                };
2743                if params.len() != 2 {
2744                    return Ok(ResultData::Error("#VALUE!".to_string()));
2745                }
2746                let array = self.eval_as_array(&args[array_idx], context, row, col, deps, scope)?;
2747                let (mut acc, rest, mut history): (ResultData, &[ResultData], Vec<ResultData>) =
2748                    if args.len() == 3 {
2749                        let init = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
2750                        (init, &array[..], Vec::new())
2751                    } else {
2752                        match array.split_first() {
2753                            Some((first, rest)) => (first.clone(), rest, vec![first.clone()]),
2754                            None => return Ok(ResultData::Error("#VALUE!".to_string())),
2755                        }
2756                    };
2757                for item in rest {
2758                    let call_args = [acc.clone(), item.clone()];
2759                    acc = self
2760                        .invoke_lambda(&params, &call_args, body, context, row, col, deps, scope)?;
2761                    history.push(acc.clone());
2762                }
2763                if func_name == "REDUCE" {
2764                    Ok(acc)
2765                } else {
2766                    Ok(ResultData::List(history))
2767                }
2768            }
2769            "MAKEARRAY" => {
2770                if args.len() != 3 {
2771                    return Ok(ResultData::Error("#VALUE!".to_string()));
2772                }
2773                let Some((params, body)) = Self::extract_lambda(&args[2]) else {
2774                    return Ok(ResultData::Error("#VALUE!".to_string()));
2775                };
2776                if params.len() != 2 {
2777                    return Ok(ResultData::Error("#VALUE!".to_string()));
2778                }
2779                let rows_val = self.evaluate_ast(&args[0], context, row, col, deps, scope)?;
2780                let cols_val = self.evaluate_ast(&args[1], context, row, col, deps, scope)?;
2781                let num_rows = self.to_f64(&rows_val).unwrap_or(0.0).max(0.0) as usize;
2782                let num_cols = self.to_f64(&cols_val).unwrap_or(0.0).max(0.0) as usize;
2783                let mut results = Vec::with_capacity(num_rows * num_cols);
2784                for r in 1..=num_rows {
2785                    for c in 1..=num_cols {
2786                        let call_args = [ResultData::Float(r as f64), ResultData::Float(c as f64)];
2787                        results.push(self.invoke_lambda(
2788                            &params, &call_args, body, context, row, col, deps, scope,
2789                        )?);
2790                    }
2791                }
2792                Ok(ResultData::List(results))
2793            }
2794            _ => unreachable!(),
2795        }
2796    }
2797
2798    fn parse_a1_reference(text: &str) -> Option<(Option<String>, usize, usize, usize, usize)> {
2799        let text = text.trim();
2800        let (sheet_part, ref_part) = match text.rfind('!') {
2801            Some(idx) => (Some(&text[..idx]), &text[idx + 1..]),
2802            None => (None, text),
2803        };
2804        let sheet = sheet_part.map(|s| s.trim().trim_matches('\'').to_string());
2805
2806        fn parse_cell(s: &str) -> Option<(usize, usize)> {
2807            let s = s.replace('$', "");
2808            let col_end = s.find(|c: char| c.is_ascii_digit())?;
2809            let (col_str, row_str) = s.split_at(col_end);
2810            if col_str.is_empty() || row_str.is_empty() {
2811                return None;
2812            }
2813            let mut col = 0usize;
2814            for ch in col_str.chars() {
2815                if !ch.is_ascii_alphabetic() {
2816                    return None;
2817                }
2818                col = col * 26 + (ch.to_ascii_uppercase() as usize - 'A' as usize + 1);
2819            }
2820            let row: usize = row_str.parse().ok()?;
2821            if row == 0 || col == 0 {
2822                return None;
2823            }
2824            Some((row - 1, col - 1))
2825        }
2826
2827        if let Some((start, end)) = ref_part.split_once(':') {
2828            let (r1, c1) = parse_cell(start)?;
2829            let (r2, c2) = parse_cell(end)?;
2830            Some((sheet, r1.min(r2), c1.min(c2), r1.max(r2), c1.max(c2)))
2831        } else {
2832            let (r, c) = parse_cell(ref_part)?;
2833            Some((sheet, r, c, r, c))
2834        }
2835    }
2836
2837    fn read_cell_with_deps(
2838        &self,
2839        sheet_opt: &Option<String>,
2840        r: usize,
2841        c: usize,
2842        context: Option<&Context>,
2843        deps: &mut Vec<Dependency>,
2844    ) -> ResultData {
2845        let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
2846        if is_self {
2847            deps.push(Dependency::Local(CellRef::new(r, c)));
2848            self.get_result_data(&CellRef::new(r, c))
2849        } else if let Some(ctx) = context {
2850            let name = sheet_opt.clone().unwrap();
2851            deps.push(Dependency::Remote {
2852                sheet: name.clone(),
2853                cell: CellRef::new(r, c),
2854            });
2855            ctx.sheets
2856                .get(&name)
2857                .map(|s| s.get_result_data(&CellRef::new(r, c)))
2858                .unwrap_or(ResultData::None)
2859        } else {
2860            ResultData::None
2861        }
2862    }
2863
2864    #[allow(clippy::too_many_arguments)]
2865    fn evaluate_range_info_function(
2866        &self,
2867        func_name: &str,
2868        args: &[crate::core::parser::Expr],
2869        context: Option<&Context>,
2870        row: Option<usize>,
2871        col: Option<usize>,
2872        deps: &mut Vec<Dependency>,
2873        scope: &LetScope<'_>,
2874    ) -> Result<ResultData, EngineError> {
2875        use crate::core::parser::Expr;
2876
2877        match func_name {
2878            "ROW" => match args.first() {
2879                Some(arg) => match Self::range_bounds(arg) {
2880                    Some((_, start_row, _, end_row, _)) if end_row > start_row => {
2881                        Ok(ResultData::List(
2882                            (start_row..=end_row)
2883                                .map(|r| ResultData::Float((r + 1) as f64))
2884                                .collect(),
2885                        ))
2886                    }
2887                    Some((_, start_row, _, _, _)) => Ok(ResultData::Float((start_row + 1) as f64)),
2888                    None => Ok(ResultData::Error("#VALUE!".to_string())),
2889                },
2890                None => match row {
2891                    Some(r) => Ok(ResultData::Float((r + 1) as f64)),
2892                    None => Ok(ResultData::Error("#VALUE!".to_string())),
2893                },
2894            },
2895            "COLUMN" => match args.first() {
2896                Some(arg) => match Self::range_bounds(arg) {
2897                    Some((_, _, start_col, _, end_col)) if end_col > start_col => {
2898                        Ok(ResultData::List(
2899                            (start_col..=end_col)
2900                                .map(|c| ResultData::Float((c + 1) as f64))
2901                                .collect(),
2902                        ))
2903                    }
2904                    Some((_, _, start_col, _, _)) => Ok(ResultData::Float((start_col + 1) as f64)),
2905                    None => Ok(ResultData::Error("#VALUE!".to_string())),
2906                },
2907                None => match col {
2908                    Some(c) => Ok(ResultData::Float((c + 1) as f64)),
2909                    None => Ok(ResultData::Error("#VALUE!".to_string())),
2910                },
2911            },
2912            "ROWS" => {
2913                let Some(arg) = args.first() else {
2914                    return Ok(ResultData::Error("#VALUE!".to_string()));
2915                };
2916                let Some((sheet_opt, start_row, _, end_row, _)) = Self::range_bounds(arg) else {
2917                    return Ok(ResultData::Error("#VALUE!".to_string()));
2918                };
2919                let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
2920                let actual_end_row = if end_row == usize::MAX {
2921                    if is_self {
2922                        self.row_count().saturating_sub(1)
2923                    } else {
2924                        context
2925                            .and_then(|ctx| sheet_opt.as_ref().and_then(|n| ctx.sheets.get(n)))
2926                            .map(|s| s.row_count().saturating_sub(1))
2927                            .unwrap_or(0)
2928                    }
2929                } else {
2930                    end_row
2931                };
2932                Ok(ResultData::Float(
2933                    (actual_end_row.saturating_sub(start_row) + 1) as f64,
2934                ))
2935            }
2936            "COLUMNS" => {
2937                let Some(arg) = args.first() else {
2938                    return Ok(ResultData::Error("#VALUE!".to_string()));
2939                };
2940                match Self::range_bounds(arg) {
2941                    Some((_, _, start_col, _, end_col)) => Ok(ResultData::Float(
2942                        (end_col.saturating_sub(start_col) + 1) as f64,
2943                    )),
2944                    None => Ok(ResultData::Error("#VALUE!".to_string())),
2945                }
2946            }
2947            "AREAS" => {
2948                if args.is_empty() {
2949                    Ok(ResultData::Error("#VALUE!".to_string()))
2950                } else {
2951                    Ok(ResultData::Float(1.0))
2952                }
2953            }
2954            "ISREF" => Ok(ResultData::Boolean(matches!(
2955                args.first(),
2956                Some(Expr::CellRef { .. } | Expr::RangeRef { .. } | Expr::StructuredRef { .. })
2957            ))),
2958            "FORMULATEXT" | "ISFORMULA" => {
2959                let Some(arg) = args.first() else {
2960                    return Ok(ResultData::Error("#VALUE!".to_string()));
2961                };
2962                let Some((sheet_opt, r, c, _, _)) = Self::range_bounds(arg) else {
2963                    return Ok(ResultData::Error("#VALUE!".to_string()));
2964                };
2965                let is_self = sheet_opt.as_deref().is_none_or(|n| n == self.name);
2966                let src = if is_self {
2967                    deps.push(Dependency::Local(CellRef::new(r, c)));
2968                    self.get_src_str(&CellRef::new(r, c))
2969                } else if let Some(ctx) = context {
2970                    let name = sheet_opt.unwrap();
2971                    deps.push(Dependency::Remote {
2972                        sheet: name.clone(),
2973                        cell: CellRef::new(r, c),
2974                    });
2975                    ctx.sheets
2976                        .get(&name)
2977                        .map(|s| s.get_src_str(&CellRef::new(r, c)))
2978                        .unwrap_or_default()
2979                } else {
2980                    String::new()
2981                };
2982                let is_formula = src.starts_with('=');
2983                if func_name == "ISFORMULA" {
2984                    Ok(ResultData::Boolean(is_formula))
2985                } else if is_formula {
2986                    Ok(ResultData::String(src))
2987                } else {
2988                    Ok(ResultData::Error("#N/A".to_string()))
2989                }
2990            }
2991            "SHEETS" => Ok(ResultData::Float(
2992                context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
2993            )),
2994            "SHEET" => {
2995                let sheet_name = match args.first() {
2996                    None => Some(self.name.clone()),
2997                    Some(arg) => match Self::range_bounds(arg) {
2998                        Some((sheet_opt, ..)) => {
2999                            Some(sheet_opt.unwrap_or_else(|| self.name.clone()))
3000                        }
3001                        None => self
3002                            .evaluate_ast(arg, context, row, col, deps, scope)
3003                            .ok()
3004                            .map(|v| v.to_string()),
3005                    },
3006                };
3007
3008                match sheet_name {
3009                    Some(name) => {
3010                        let ordinal = context
3011                            .and_then(|c| {
3012                                c.sheet_order
3013                                    .iter()
3014                                    .position(|n| n.eq_ignore_ascii_case(&name))
3015                            })
3016                            .map(|i| i + 1)
3017                            .unwrap_or(1);
3018                        Ok(ResultData::Float(ordinal as f64))
3019                    }
3020                    None => Ok(ResultData::Error("#N/A".to_string())),
3021                }
3022            }
3023            "CELL" => {
3024                if args.is_empty() {
3025                    return Ok(ResultData::Error("#VALUE!".to_string()));
3026                }
3027                let info_type = self
3028                    .evaluate_ast(&args[0], context, row, col, deps, scope)?
3029                    .to_string()
3030                    .to_lowercase();
3031                let bounds = args.get(1).and_then(Self::range_bounds);
3032                match info_type.as_str() {
3033                    "row" => match bounds.map(|b| b.1).or(row) {
3034                        Some(r) => Ok(ResultData::Float((r + 1) as f64)),
3035                        None => Ok(ResultData::Error("#VALUE!".to_string())),
3036                    },
3037                    "col" => match bounds {
3038                        Some((_, _, c, _, _)) => Ok(ResultData::Float((c + 1) as f64)),
3039                        None => Ok(ResultData::Error("#VALUE!".to_string())),
3040                    },
3041                    "address" => match bounds {
3042                        Some((_, r, c, _, _)) => Ok(ResultData::String(format!(
3043                            "${}${}",
3044                            crate::core::parser::col_idx_to_letters(c),
3045                            r + 1
3046                        ))),
3047                        None => Ok(ResultData::Error("#VALUE!".to_string())),
3048                    },
3049                    "contents" => match bounds {
3050                        Some((sheet_opt, r, c, _, _)) => {
3051                            Ok(self.read_cell_with_deps(&sheet_opt, r, c, context, deps))
3052                        }
3053                        None => Ok(ResultData::Error("#VALUE!".to_string())),
3054                    },
3055                    _ => Ok(ResultData::Error("#VALUE!".to_string())),
3056                }
3057            }
3058            "INFO" => {
3059                if args.is_empty() {
3060                    return Ok(ResultData::Error("#VALUE!".to_string()));
3061                }
3062                let info_type = self
3063                    .evaluate_ast(&args[0], context, row, col, deps, scope)?
3064                    .to_string()
3065                    .to_lowercase();
3066                match info_type.as_str() {
3067                    "numfile" => Ok(ResultData::Float(
3068                        context.map(|c| c.sheets.len() + 1).unwrap_or(1) as f64,
3069                    )),
3070                    "release" => Ok(ResultData::String("16.0".to_string())),
3071                    "system" => Ok(ResultData::String(
3072                        if cfg!(target_os = "macos") {
3073                            "mac"
3074                        } else {
3075                            "pcdos"
3076                        }
3077                        .to_string(),
3078                    )),
3079                    _ => Ok(ResultData::Error("#VALUE!".to_string())),
3080                }
3081            }
3082            "INDIRECT" => {
3083                if args.is_empty() {
3084                    return Ok(ResultData::Error("#VALUE!".to_string()));
3085                }
3086                let text = self
3087                    .evaluate_ast(&args[0], context, row, col, deps, scope)?
3088                    .to_string();
3089                let a1_style = match args.get(1) {
3090                    Some(a) => self.to_bool(&self.evaluate_ast(a, context, row, col, deps, scope)?),
3091                    None => true,
3092                };
3093                if !a1_style {
3094                    return Ok(ResultData::Error("#VALUE!".to_string()));
3095                }
3096                match Self::parse_a1_reference(&text) {
3097                    Some((sheet_opt, start_row, start_col, end_row, end_col)) => {
3098                        if start_row == end_row && start_col == end_col {
3099                            Ok(self.read_cell_with_deps(
3100                                &sheet_opt, start_row, start_col, context, deps,
3101                            ))
3102                        } else {
3103                            match self.materialize_range(
3104                                &sheet_opt, start_row, start_col, end_row, end_col, context,
3105                            ) {
3106                                Some(grid) => {
3107                                    Ok(ResultData::List(grid.into_iter().flatten().collect()))
3108                                }
3109                                None => Ok(ResultData::Error("#REF!".to_string())),
3110                            }
3111                        }
3112                    }
3113                    None => Ok(ResultData::Error("#REF!".to_string())),
3114                }
3115            }
3116            "OFFSET" => {
3117                if args.len() < 3 {
3118                    return Ok(ResultData::Error("#VALUE!".to_string()));
3119                }
3120                let Some((sheet_opt, base_row, base_col, base_end_row, base_end_col)) =
3121                    Self::range_bounds(&args[0])
3122                else {
3123                    return Ok(ResultData::Error("#VALUE!".to_string()));
3124                };
3125                let row_offset = self
3126                    .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3127                    .unwrap_or(0.0) as isize;
3128                let col_offset = self
3129                    .to_f64(&self.evaluate_ast(&args[2], context, row, col, deps, scope)?)
3130                    .unwrap_or(0.0) as isize;
3131                let base_height = (base_end_row.saturating_sub(base_row) + 1) as isize;
3132                let base_width = (base_end_col.saturating_sub(base_col) + 1) as isize;
3133                let height = match args.get(3) {
3134                    Some(a) => self
3135                        .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3136                        .unwrap_or(base_height as f64) as isize,
3137                    None => base_height,
3138                };
3139                let width = match args.get(4) {
3140                    Some(a) => self
3141                        .to_f64(&self.evaluate_ast(a, context, row, col, deps, scope)?)
3142                        .unwrap_or(base_width as f64) as isize,
3143                    None => base_width,
3144                };
3145                let new_row = base_row as isize + row_offset;
3146                let new_col = base_col as isize + col_offset;
3147                if new_row < 0 || new_col < 0 || height <= 0 || width <= 0 {
3148                    return Ok(ResultData::Error("#REF!".to_string()));
3149                }
3150                let (start_row, start_col) = (new_row as usize, new_col as usize);
3151                let (end_row, end_col) = (
3152                    start_row + (height - 1) as usize,
3153                    start_col + (width - 1) as usize,
3154                );
3155                if start_row == end_row && start_col == end_col {
3156                    Ok(self.read_cell_with_deps(&sheet_opt, start_row, start_col, context, deps))
3157                } else {
3158                    match self.materialize_range(
3159                        &sheet_opt, start_row, start_col, end_row, end_col, context,
3160                    ) {
3161                        Some(grid) => Ok(ResultData::List(grid.into_iter().flatten().collect())),
3162                        None => Ok(ResultData::Error("#REF!".to_string())),
3163                    }
3164                }
3165            }
3166            _ => unreachable!(),
3167        }
3168    }
3169
3170    fn evaluate_getpivotdata(
3171        &self,
3172        args: &[crate::core::parser::Expr],
3173        context: Option<&Context>,
3174        row: Option<usize>,
3175        col: Option<usize>,
3176        deps: &mut Vec<Dependency>,
3177        scope: &LetScope<'_>,
3178    ) -> Result<ResultData, EngineError> {
3179        if args.len() < 2 || !(args.len() - 2).is_multiple_of(2) {
3180            return Ok(ResultData::Error("#VALUE!".to_string()));
3181        }
3182
3183        let data_field = self
3184            .evaluate_ast(&args[0], context, row, col, deps, scope)?
3185            .to_string();
3186
3187        let (sheet_opt, target_row, target_col, _, _) = match Self::range_bounds(&args[1]) {
3188            Some(bounds) => bounds,
3189            None => return Ok(ResultData::Error("#REF!".to_string())),
3190        };
3191        self.read_cell_with_deps(&sheet_opt, target_row, target_col, context, deps);
3192
3193        let sheet_id = match &sheet_opt {
3194            None => self.id,
3195            Some(name) if name == &self.name => self.id,
3196            Some(name) => match context.and_then(|c| c.sheets.get(name)) {
3197                Some(s) => s.id,
3198                None => return Ok(ResultData::Error("#REF!".to_string())),
3199            },
3200        };
3201
3202        let pivot_tables = context.map(|c| c.pivot_tables).unwrap_or(&[]);
3203        let pivot = match pivot_tables.iter().find(|p| {
3204            p.dest_sheet_id == sheet_id
3205                && p.last_output_end_row
3206                    .is_some_and(|end| target_row >= p.dest_row && target_row <= end)
3207                && p.last_output_end_col
3208                    .is_some_and(|end| target_col >= p.dest_col && target_col <= end)
3209        }) {
3210            Some(p) => p,
3211            None => return Ok(ResultData::Error("#REF!".to_string())),
3212        };
3213
3214        let mut criteria: Vec<(String, String)> = Vec::new();
3215        let mut i = 2;
3216        while i < args.len() {
3217            let field = self
3218                .evaluate_ast(&args[i], context, row, col, deps, scope)?
3219                .to_string();
3220            let item = self
3221                .evaluate_ast(&args[i + 1], context, row, col, deps, scope)?
3222                .to_string();
3223            criteria.push((field, item));
3224            i += 2;
3225        }
3226
3227        let mut sheet_refs: Vec<&Sheet> = context
3228            .map(|c| c.sheets.values().copied().collect())
3229            .unwrap_or_default();
3230        sheet_refs.push(self);
3231
3232        match crate::core::pivot::getpivotdata(&sheet_refs, pivot, &data_field, &criteria) {
3233            Ok(v) => Ok(v),
3234            Err(e) => Ok(ResultData::Error(e)),
3235        }
3236    }
3237
3238    #[allow(clippy::too_many_arguments)]
3239    fn evaluate_array_reshape_function(
3240        &self,
3241        func_name: &str,
3242        args: &[crate::core::parser::Expr],
3243        context: Option<&Context>,
3244        row: Option<usize>,
3245        col: Option<usize>,
3246        deps: &mut Vec<Dependency>,
3247        scope: &LetScope<'_>,
3248    ) -> Result<ResultData, EngineError> {
3249        match func_name {
3250            "TRANSPOSE" => {
3251                let Some(arg) = args.first() else {
3252                    return Ok(ResultData::Error("#VALUE!".to_string()));
3253                };
3254                let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3255                let rows = flat.len().checked_div(cols).unwrap_or(0);
3256                let mut result = Vec::with_capacity(flat.len());
3257                for c in 0..cols {
3258                    for r in 0..rows {
3259                        result.push(flat[r * cols + c].clone());
3260                    }
3261                }
3262                Ok(ResultData::List(result))
3263            }
3264            "HSTACK" | "VSTACK" => {
3265                if args.is_empty() {
3266                    return Ok(ResultData::Error("#VALUE!".to_string()));
3267                }
3268                let mut shapes = Vec::with_capacity(args.len());
3269                for a in args {
3270                    shapes.push(self.array_shape(a, context, row, col, deps, scope)?);
3271                }
3272                let mut result = Vec::new();
3273                if func_name == "HSTACK" {
3274                    let max_rows = shapes
3275                        .iter()
3276                        .map(|(f, c)| if *c == 0 { 0 } else { f.len() / c })
3277                        .max()
3278                        .unwrap_or(0);
3279                    for r in 0..max_rows {
3280                        for (flat, cols) in &shapes {
3281                            let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3282                            for c in 0..*cols {
3283                                result.push(if r < rows {
3284                                    flat[r * cols + c].clone()
3285                                } else {
3286                                    ResultData::Error("#N/A".to_string())
3287                                });
3288                            }
3289                        }
3290                    }
3291                } else {
3292                    let max_cols = shapes.iter().map(|(_, c)| *c).max().unwrap_or(0);
3293                    for (flat, cols) in &shapes {
3294                        let rows = if *cols == 0 { 0 } else { flat.len() / cols };
3295                        for r in 0..rows {
3296                            for c in 0..max_cols {
3297                                result.push(if c < *cols {
3298                                    flat[r * cols + c].clone()
3299                                } else {
3300                                    ResultData::Error("#N/A".to_string())
3301                                });
3302                            }
3303                        }
3304                    }
3305                }
3306                Ok(ResultData::List(result))
3307            }
3308            "CHOOSEROWS" | "CHOOSECOLS" => {
3309                if args.len() < 2 {
3310                    return Ok(ResultData::Error("#VALUE!".to_string()));
3311                }
3312                let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3313                let rows = flat.len().checked_div(cols).unwrap_or(0);
3314                let total = if func_name == "CHOOSEROWS" {
3315                    rows
3316                } else {
3317                    cols
3318                } as isize;
3319                let mut indices = Vec::with_capacity(args.len() - 1);
3320                for idx_expr in &args[1..] {
3321                    let n = self
3322                        .to_f64(&self.evaluate_ast(idx_expr, context, row, col, deps, scope)?)
3323                        .unwrap_or(0.0) as isize;
3324                    let real_idx = if n < 0 { total + n } else { n - 1 };
3325                    if real_idx < 0 || real_idx >= total {
3326                        return Ok(ResultData::Error("#VALUE!".to_string()));
3327                    }
3328                    indices.push(real_idx as usize);
3329                }
3330                let mut result = Vec::new();
3331                if func_name == "CHOOSEROWS" {
3332                    for r in indices {
3333                        for c in 0..cols {
3334                            result.push(flat[r * cols + c].clone());
3335                        }
3336                    }
3337                } else {
3338                    for r in 0..rows {
3339                        for &c in &indices {
3340                            result.push(flat[r * cols + c].clone());
3341                        }
3342                    }
3343                }
3344                Ok(ResultData::List(result))
3345            }
3346            "DROP" | "TAKE" => {
3347                if args.len() < 2 {
3348                    return Ok(ResultData::Error("#VALUE!".to_string()));
3349                }
3350                let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3351                let num_rows = flat.len().checked_div(cols).unwrap_or(0) as isize;
3352                let is_take = func_name == "TAKE";
3353                let rows_n = self
3354                    .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3355                    .unwrap_or(0.0) as isize;
3356                let cols_n = match args.get(2) {
3357                    Some(e) => self
3358                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3359                        .unwrap_or(0.0) as isize,
3360                    None => {
3361                        if is_take {
3362                            cols as isize
3363                        } else {
3364                            0
3365                        }
3366                    }
3367                };
3368                let (row_start, row_end) = Self::drop_take_bounds(num_rows, rows_n, is_take);
3369                let (col_start, col_end) = Self::drop_take_bounds(cols as isize, cols_n, is_take);
3370                if row_start >= row_end || col_start >= col_end {
3371                    return Ok(ResultData::Error("#CALC!".to_string()));
3372                }
3373                let mut result = Vec::new();
3374                for r in row_start..row_end {
3375                    for c in col_start..col_end {
3376                        result.push(flat[(r as usize) * cols + (c as usize)].clone());
3377                    }
3378                }
3379                Ok(ResultData::List(result))
3380            }
3381            "EXPAND" => {
3382                if args.len() < 2 {
3383                    return Ok(ResultData::Error("#VALUE!".to_string()));
3384                }
3385                let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3386                let orig_rows = flat.len().checked_div(cols).unwrap_or(0);
3387                let new_rows = self
3388                    .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3389                    .unwrap_or(orig_rows as f64) as usize;
3390                let new_cols = match args.get(2) {
3391                    Some(e) => self
3392                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3393                        .unwrap_or(cols as f64) as usize,
3394                    None => cols,
3395                };
3396                let pad = match args.get(3) {
3397                    Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3398                    None => ResultData::Error("#N/A".to_string()),
3399                };
3400                if new_rows < orig_rows || new_cols < cols {
3401                    return Ok(ResultData::Error("#VALUE!".to_string()));
3402                }
3403                let mut result = Vec::with_capacity(new_rows * new_cols);
3404                for r in 0..new_rows {
3405                    for c in 0..new_cols {
3406                        result.push(if r < orig_rows && c < cols {
3407                            flat[r * cols + c].clone()
3408                        } else {
3409                            pad.clone()
3410                        });
3411                    }
3412                }
3413                Ok(ResultData::List(result))
3414            }
3415            "TOCOL" | "TOROW" => {
3416                let Some(arg) = args.first() else {
3417                    return Ok(ResultData::Error("#VALUE!".to_string()));
3418                };
3419                let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3420                let rows = flat.len().checked_div(cols).unwrap_or(0);
3421                let ignore = match args.get(1) {
3422                    Some(e) => self
3423                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3424                        .unwrap_or(0.0) as i64,
3425                    None => 0,
3426                };
3427                let scan_by_col = match args.get(2) {
3428                    Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3429                    None => false,
3430                };
3431                let ordered: Vec<ResultData> = if scan_by_col {
3432                    let mut v = Vec::with_capacity(flat.len());
3433                    for c in 0..cols {
3434                        for r in 0..rows {
3435                            v.push(flat[r * cols + c].clone());
3436                        }
3437                    }
3438                    v
3439                } else {
3440                    flat
3441                };
3442                let filtered: Vec<ResultData> = ordered
3443                    .into_iter()
3444                    .filter(|v| match ignore {
3445                        1 => !matches!(v, ResultData::None),
3446                        2 => !matches!(v, ResultData::Error(_)),
3447                        3 => !matches!(v, ResultData::None | ResultData::Error(_)),
3448                        _ => true,
3449                    })
3450                    .collect();
3451                Ok(ResultData::List(filtered))
3452            }
3453            "WRAPROWS" | "WRAPCOLS" => {
3454                if args.len() < 2 {
3455                    return Ok(ResultData::Error("#VALUE!".to_string()));
3456                }
3457                let (flat, _cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3458                let wrap = self
3459                    .to_f64(&self.evaluate_ast(&args[1], context, row, col, deps, scope)?)
3460                    .unwrap_or(1.0)
3461                    .max(1.0) as usize;
3462                let pad = match args.get(2) {
3463                    Some(e) => self.evaluate_ast(e, context, row, col, deps, scope)?,
3464                    None => ResultData::Error("#N/A".to_string()),
3465                };
3466                if func_name == "WRAPROWS" {
3467                    let mut result = flat;
3468                    let rem = result.len() % wrap;
3469                    if rem != 0 {
3470                        result.extend(std::iter::repeat_n(pad, wrap - rem));
3471                    }
3472                    Ok(ResultData::List(result))
3473                } else {
3474                    let num_result_cols = flat.len().div_ceil(wrap).max(1);
3475                    let total = wrap * num_result_cols;
3476                    let mut result = Vec::with_capacity(total);
3477                    for i in 0..total {
3478                        let col = i / wrap;
3479                        let r = i % wrap;
3480                        let target = r * num_result_cols + col;
3481                        while result.len() <= target {
3482                            result.push(pad.clone());
3483                        }
3484                        if i < flat.len() {
3485                            result[target] = flat[i].clone();
3486                        }
3487                    }
3488                    Ok(ResultData::List(result))
3489                }
3490            }
3491            "UNIQUE" => {
3492                let Some(arg) = args.first() else {
3493                    return Ok(ResultData::Error("#VALUE!".to_string()));
3494                };
3495                let (flat, _cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3496                let exactly_once = match args.get(2) {
3497                    Some(e) => self.to_bool(&self.evaluate_ast(e, context, row, col, deps, scope)?),
3498                    None => false,
3499                };
3500                let mut seen: Vec<(String, ResultData, usize)> = Vec::new();
3501                for v in &flat {
3502                    let key = match v {
3503                        ResultData::None => "blank:".to_string(),
3504                        ResultData::Boolean(b) => format!("bool:{b}"),
3505                        ResultData::Integer(i) => format!("num:{}", *i as f64),
3506                        ResultData::Float(f) => format!("num:{f}"),
3507                        ResultData::String(s) => format!("str:{s}"),
3508                        ResultData::Error(e) => format!("err:{e}"),
3509                        ResultData::List(_) | ResultData::Dict(_) => format!("other:{v}"),
3510                    };
3511                    match seen.iter_mut().find(|(k, ..)| k == &key) {
3512                        Some(entry) => entry.2 += 1,
3513                        None => seen.push((key, v.clone(), 1)),
3514                    }
3515                }
3516                let result: Vec<ResultData> = seen
3517                    .into_iter()
3518                    .filter(|(_, _, count)| !exactly_once || *count == 1)
3519                    .map(|(_, v, _)| v)
3520                    .collect();
3521                Ok(ResultData::List(result))
3522            }
3523            "SORT" => {
3524                let Some(arg) = args.first() else {
3525                    return Ok(ResultData::Error("#VALUE!".to_string()));
3526                };
3527                let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3528                let rows = flat.len().checked_div(cols).unwrap_or(0);
3529                let sort_index = match args.get(1) {
3530                    Some(e) => self
3531                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3532                        .unwrap_or(1.0) as usize,
3533                    None => 1,
3534                };
3535                let sort_order = match args.get(2) {
3536                    Some(e) => self
3537                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3538                        .unwrap_or(1.0),
3539                    None => 1.0,
3540                };
3541                let col_idx = sort_index.saturating_sub(1).min(cols.saturating_sub(1));
3542                let mut row_indices: Vec<usize> = (0..rows).collect();
3543                row_indices.sort_by(|&a, &b| {
3544                    Self::sort_compare_blanks_last(
3545                        &flat[a * cols + col_idx],
3546                        &flat[b * cols + col_idx],
3547                        sort_order,
3548                    )
3549                });
3550                let mut result = Vec::with_capacity(flat.len());
3551                for r in row_indices {
3552                    for c in 0..cols {
3553                        result.push(flat[r * cols + c].clone());
3554                    }
3555                }
3556                Ok(ResultData::List(result))
3557            }
3558            "SORTBY" => {
3559                if args.len() < 2 {
3560                    return Ok(ResultData::Error("#VALUE!".to_string()));
3561                }
3562                let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3563                let rows = flat.len().checked_div(cols).unwrap_or(0);
3564                let by = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3565                let order = match args.get(2) {
3566                    Some(e) => self
3567                        .to_f64(&self.evaluate_ast(e, context, row, col, deps, scope)?)
3568                        .unwrap_or(1.0),
3569                    None => 1.0,
3570                };
3571                let mut row_indices: Vec<usize> = (0..rows).collect();
3572                row_indices.sort_by(|&a, &b| {
3573                    let va = by.get(a).cloned().unwrap_or(ResultData::None);
3574                    let vb = by.get(b).cloned().unwrap_or(ResultData::None);
3575                    Self::sort_compare_blanks_last(&va, &vb, order)
3576                });
3577                let mut result = Vec::with_capacity(flat.len());
3578                for r in row_indices {
3579                    for c in 0..cols {
3580                        result.push(flat[r * cols + c].clone());
3581                    }
3582                }
3583                Ok(ResultData::List(result))
3584            }
3585            "FILTER" => {
3586                if args.len() < 2 {
3587                    return Ok(ResultData::Error("#VALUE!".to_string()));
3588                }
3589                let (flat, cols) = self.array_shape(&args[0], context, row, col, deps, scope)?;
3590                let rows = flat.len().checked_div(cols).unwrap_or(0);
3591                let include = self.eval_as_array(&args[1], context, row, col, deps, scope)?;
3592                let mut result = Vec::new();
3593                for r in 0..rows {
3594                    let keep = include.get(r).map(|v| self.to_bool(v)).unwrap_or(false);
3595                    if keep {
3596                        for c in 0..cols {
3597                            result.push(flat[r * cols + c].clone());
3598                        }
3599                    }
3600                }
3601                if result.is_empty() {
3602                    match args.get(2) {
3603                        Some(e) => Ok(self.evaluate_ast(e, context, row, col, deps, scope)?),
3604                        None => Ok(ResultData::Error("#CALC!".to_string())),
3605                    }
3606                } else {
3607                    Ok(ResultData::List(result))
3608                }
3609            }
3610            "TRIMRANGE" => {
3611                let Some(arg) = args.first() else {
3612                    return Ok(ResultData::Error("#VALUE!".to_string()));
3613                };
3614                let (flat, cols) = self.array_shape(arg, context, row, col, deps, scope)?;
3615                let rows = flat.len().checked_div(cols).unwrap_or(0);
3616                let is_blank = |v: &ResultData| {
3617                    matches!(v, ResultData::None)
3618                        || matches!(v, ResultData::String(s) if s.is_empty())
3619                };
3620                let row_blank = |r: usize| (0..cols).all(|c| is_blank(&flat[r * cols + c]));
3621                let col_blank = |c: usize| (0..rows).all(|r| is_blank(&flat[r * cols + c]));
3622                let mut r_start = 0;
3623                while r_start < rows && row_blank(r_start) {
3624                    r_start += 1;
3625                }
3626                let mut r_end = rows;
3627                while r_end > r_start && row_blank(r_end - 1) {
3628                    r_end -= 1;
3629                }
3630                let mut c_start = 0;
3631                while c_start < cols && col_blank(c_start) {
3632                    c_start += 1;
3633                }
3634                let mut c_end = cols;
3635                while c_end > c_start && col_blank(c_end - 1) {
3636                    c_end -= 1;
3637                }
3638                let mut result = Vec::new();
3639                for r in r_start..r_end {
3640                    for c in c_start..c_end {
3641                        result.push(flat[r * cols + c].clone());
3642                    }
3643                }
3644                Ok(ResultData::List(result))
3645            }
3646            _ => unreachable!(),
3647        }
3648    }
3649
3650    /// The raw text typed into a cell. Returns `None` if ref is OOB
3651    pub fn get_src(&self, cell: &CellRef) -> Option<&String> {
3652        let col = self.columns.get(cell.col);
3653        if let Some(col) = col {
3654            col.src.get(cell.row)
3655        } else {
3656            None
3657        }
3658    }
3659
3660    /// [`Sheet::get_src`] but returns empty string on OOB.
3661    pub fn get_src_str(&self, cell: &CellRef) -> String {
3662        let col = self.columns.get(cell.col);
3663        if let Some(col) = col {
3664            col.src.get(cell.row).cloned().unwrap_or("".to_string())
3665        } else {
3666            "".to_string()
3667        }
3668    }
3669
3670    /// [`Sheet::get_src`] as a borrowed `&str` for read-only.
3671    pub fn get_src_str_ref(&self, cell: &CellRef) -> Option<&str> {
3672        let col = self.columns.get(cell.col)?;
3673        col.src.get(cell.row).map(|s| s.as_str())
3674    }
3675
3676    /// See [`get_word_boundaries_from_str`].
3677    pub fn get_word_boundaries(&self, cell: &CellRef, char_offset: usize) -> (usize, usize) {
3678        let text = self.get_src_str(cell);
3679        get_word_boundaries_from_str(&text, char_offset)
3680    }
3681}