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