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