1use super::super::utils::collapse_if_scalar;
23use super::lookup_utils::{cmp_for_lookup, equals_maybe_wildcard, value_to_f64_lenient};
24use crate::args::{ArgSchema, CoercionPolicy, ShapeKind};
25use crate::function::Function; use crate::traits::{ArgumentHandle, FunctionContext};
27use formualizer_common::{ArgKind, ExcelError, ExcelErrorKind, LiteralValue};
28use formualizer_macros::func_caps;
29use std::collections::HashMap;
30
31pub fn super_wildcard_match(pattern: &str, text: &str) -> bool {
34 #[derive(Clone, Copy, Debug)]
38 enum Token<'a> {
39 AnySeq,
40 AnyChar,
41 Lit(&'a str),
42 }
43 let mut tokens: Vec<Token> = Vec::new();
44 let mut i = 0;
45 let bytes = pattern.as_bytes();
46 let mut lit_start = 0;
47 while i < bytes.len() {
48 match bytes[i] {
49 b'~' => {
50 if i + 1 < bytes.len() {
52 if lit_start < i {
54 tokens.push(Token::Lit(&pattern[lit_start..i]));
55 }
56 tokens.push(Token::Lit(&pattern[i + 1..i + 2]));
57 i += 2;
58 lit_start = i;
59 } else {
60 i += 1;
62 }
63 }
64 b'*' => {
65 if lit_start < i {
66 tokens.push(Token::Lit(&pattern[lit_start..i]));
67 }
68 tokens.push(Token::AnySeq);
69 i += 1;
70 lit_start = i;
71 }
72 b'?' => {
73 if lit_start < i {
74 tokens.push(Token::Lit(&pattern[lit_start..i]));
75 }
76 tokens.push(Token::AnyChar);
77 i += 1;
78 lit_start = i;
79 }
80 _ => i += 1,
81 }
82 }
83 if lit_start < bytes.len() {
84 tokens.push(Token::Lit(&pattern[lit_start..]));
85 }
86 let mut compact: Vec<Token> = Vec::new();
88 for t in tokens {
89 match t {
90 Token::AnySeq => {
91 if !matches!(compact.last(), Some(Token::AnySeq)) {
92 compact.push(t);
93 }
94 }
95 _ => compact.push(t),
96 }
97 }
98 fn match_tokens<'a>(tokens: &[Token<'a>], text: &str) -> bool {
100 fn eq_icase(a: &str, b: &str) -> bool {
101 a.eq_ignore_ascii_case(b)
102 }
103 let mut ti = 0;
105 let tb = tokens;
106 let mut backtrack: Vec<(usize, usize)> = Vec::new(); let text_bytes = text.as_bytes();
109 let mut si = 0; loop {
111 if ti == tb.len() {
112 if si == text_bytes.len() {
114 return true;
115 }
116 } else {
118 match tb[ti] {
119 Token::AnySeq => {
120 ti += 1;
122 backtrack.push((ti - 1, si + 1));
123 continue;
124 }
125 Token::AnyChar => {
126 if si < text_bytes.len() {
127 ti += 1;
128 si += 1;
129 continue;
130 }
131 }
132 Token::Lit(l) => {
133 let l_len = l.len();
134 if si + l_len <= text_bytes.len() && eq_icase(&text[si..si + l_len], l) {
135 ti += 1;
136 si += l_len;
137 continue;
138 }
139 }
140 }
141 }
142 if let Some((tok_star, new_si)) = backtrack.pop() {
144 if new_si <= text_bytes.len() {
145 ti = tok_star + 1;
146 si = new_si;
147 continue;
148 } else {
149 continue;
150 }
151 }
152 return false;
153 }
154 }
155 match_tokens(&compact, text)
156}
157
158#[derive(Debug)]
161pub struct XLookupFn;
162
163impl Function for XLookupFn {
164 func_caps!(PURE, LOOKUP);
165 fn name(&self) -> &'static str {
166 "XLOOKUP"
167 }
168 fn min_args(&self) -> usize {
169 3
170 }
171 fn variadic(&self) -> bool {
172 true
173 }
174 fn arg_schema(&self) -> &'static [ArgSchema] {
175 use once_cell::sync::Lazy;
176 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
177 vec![
178 ArgSchema {
180 kinds: smallvec::smallvec![ArgKind::Any],
181 required: true,
182 by_ref: false,
183 shape: ShapeKind::Scalar,
184 coercion: CoercionPolicy::None,
185 max: None,
186 repeating: None,
187 default: None,
188 },
189 ArgSchema {
191 kinds: smallvec::smallvec![ArgKind::Range],
192 required: true,
193 by_ref: true,
194 shape: ShapeKind::Range,
195 coercion: CoercionPolicy::None,
196 max: None,
197 repeating: None,
198 default: None,
199 },
200 ArgSchema {
202 kinds: smallvec::smallvec![ArgKind::Range],
203 required: true,
204 by_ref: true,
205 shape: ShapeKind::Range,
206 coercion: CoercionPolicy::None,
207 max: None,
208 repeating: None,
209 default: None,
210 },
211 ArgSchema {
213 kinds: smallvec::smallvec![ArgKind::Any],
214 required: false,
215 by_ref: false,
216 shape: ShapeKind::Scalar,
217 coercion: CoercionPolicy::None,
218 max: None,
219 repeating: None,
220 default: None,
221 },
222 ArgSchema {
224 kinds: smallvec::smallvec![ArgKind::Number],
225 required: false,
226 by_ref: false,
227 shape: ShapeKind::Scalar,
228 coercion: CoercionPolicy::NumberLenientText,
229 max: None,
230 repeating: None,
231 default: Some(LiteralValue::Int(0)),
232 },
233 ArgSchema {
235 kinds: smallvec::smallvec![ArgKind::Number],
236 required: false,
237 by_ref: false,
238 shape: ShapeKind::Scalar,
239 coercion: CoercionPolicy::NumberLenientText,
240 max: None,
241 repeating: None,
242 default: Some(LiteralValue::Int(1)),
243 },
244 ]
245 });
246 &SCHEMA
247 }
248 fn eval<'a, 'b, 'c>(
249 &self,
250 args: &'c [ArgumentHandle<'a, 'b>],
251 _ctx: &dyn FunctionContext<'b>,
252 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
253 if args.len() < 3 {
254 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
255 ExcelError::new(ExcelErrorKind::Value),
256 )));
257 }
258 let lookup_value = args[0].value()?.into_literal();
259 if let LiteralValue::Error(ref e) = lookup_value {
260 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
261 e.clone(),
262 )));
263 }
264 let lookup_view = match args[1].range_view() {
265 Ok(v) => v,
266 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
267 };
268 let ret_view = match args[2].range_view() {
269 Ok(v) => v,
270 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
271 };
272
273 let (lookup_rows, lookup_cols) = lookup_view.dims();
274 let (ret_rows, ret_cols) = ret_view.dims();
275
276 let vertical = if lookup_cols == 1 {
281 true
282 } else if lookup_rows == 1 {
283 false
284 } else if lookup_rows == 0 && lookup_cols == 0 {
285 if ret_cols == 1 {
286 true
287 } else if ret_rows == 1 {
288 false
289 } else {
290 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
291 ExcelError::new(ExcelErrorKind::Value),
292 )));
293 }
294 } else {
295 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
296 ExcelError::new(ExcelErrorKind::Value),
297 )));
298 };
299
300 let lookup_len = {
301 let raw = if vertical { lookup_rows } else { lookup_cols };
302 if raw == 0 {
303 if vertical { ret_rows } else { ret_cols }
304 } else {
305 raw
306 }
307 };
308
309 if lookup_len == 0 {
310 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
311 ExcelError::new(ExcelErrorKind::Na),
312 )));
313 }
314
315 let match_mode = if args.len() >= 5 {
316 match args[4].value()?.into_literal() {
317 LiteralValue::Int(i) => i,
318 LiteralValue::Number(n) => n as i64,
319 _ => 0,
320 }
321 } else {
322 0
323 };
324 let search_mode = if args.len() >= 6 {
325 match args[5].value()?.into_literal() {
326 LiteralValue::Int(i) => i,
327 LiteralValue::Number(n) => n as i64,
328 _ => 1,
329 }
330 } else {
331 1
332 };
333
334 let wildcard = match_mode == 2;
335
336 let mut found: Option<usize> = None;
337 let needle = lookup_value;
338 if match_mode == 0 || wildcard {
339 if search_mode == 1 && lookup_rows > 0 && lookup_cols > 0 {
340 found =
341 super::lookup_utils::find_exact_index_in_view(&lookup_view, &needle, wildcard)?;
342 } else if search_mode == -1 {
343 for i in (0..lookup_len).rev() {
344 let cand = if vertical {
345 lookup_view.get_cell(i, 0)
346 } else {
347 lookup_view.get_cell(0, i)
348 };
349 if equals_maybe_wildcard(&needle, &cand, wildcard) {
350 found = Some(i);
351 break;
352 }
353 }
354 } else {
355 for i in 0..lookup_len {
358 let cand = if vertical {
359 lookup_view.get_cell(i, 0)
360 } else {
361 lookup_view.get_cell(0, i)
362 };
363 if equals_maybe_wildcard(&needle, &cand, wildcard) {
364 found = Some(i);
365 break;
366 }
367 }
368 }
369 } else if match_mode == -1 || match_mode == 1 {
370 let needle_num = value_to_f64_lenient(&needle);
371 let mut best_idx: Option<usize> = None;
372 let mut best_val: f64 = if match_mode == -1 {
373 f64::NEG_INFINITY
374 } else {
375 f64::INFINITY
376 };
377
378 let mut prev: Option<LiteralValue> = None;
379 for i in 0..lookup_len {
380 let cand = if vertical {
381 lookup_view.get_cell(i, 0)
382 } else {
383 lookup_view.get_cell(0, i)
384 };
385
386 if let Some(p) = prev.as_ref() {
387 let sorted_ok = cmp_for_lookup(p, &cand).is_some_and(|o| o <= 0);
388 if !sorted_ok {
389 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
390 ExcelError::new(ExcelErrorKind::Na),
391 )));
392 }
393 }
394 prev = Some(cand.clone());
395
396 if cmp_for_lookup(&cand, &needle).is_some_and(|o| o == 0) {
397 found = Some(i);
398 break;
399 }
400
401 if let (Some(nn), Some(vv)) = (needle_num, value_to_f64_lenient(&cand)) {
402 if match_mode == -1 {
403 if vv <= nn && vv > best_val {
404 best_val = vv;
405 best_idx = Some(i);
406 }
407 } else if vv >= nn && vv < best_val {
408 best_val = vv;
409 best_idx = Some(i);
410 }
411 }
412 }
413
414 if found.is_none() {
415 found = best_idx;
416 }
417 } else {
418 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
419 ExcelError::new(ExcelErrorKind::Value),
420 )));
421 }
422
423 if let Some(idx) = found {
424 let (ret_rows, ret_cols) = ret_view.dims();
425 if ret_rows == 0 || ret_cols == 0 {
426 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Empty));
427 }
428
429 if vertical {
430 if ret_cols == 1 {
431 return Ok(crate::traits::CalcValue::Scalar(ret_view.get_cell(idx, 0)));
432 }
433 let mut row_out: Vec<LiteralValue> = Vec::with_capacity(ret_cols);
434 for c in 0..ret_cols {
435 row_out.push(ret_view.get_cell(idx, c));
436 }
437 return Ok(crate::traits::CalcValue::Range(
438 crate::engine::range_view::RangeView::from_owned_rows(
439 vec![row_out],
440 _ctx.date_system(),
441 ),
442 ));
443 }
444
445 if ret_rows == 1 {
447 return Ok(crate::traits::CalcValue::Scalar(ret_view.get_cell(0, idx)));
448 }
449
450 let mut col_out: Vec<Vec<LiteralValue>> = Vec::with_capacity(ret_rows);
451 for r in 0..ret_rows {
452 col_out.push(vec![ret_view.get_cell(r, idx)]);
453 }
454 return Ok(crate::traits::CalcValue::Range(
455 crate::engine::range_view::RangeView::from_owned_rows(col_out, _ctx.date_system()),
456 ));
457 }
458
459 if args.len() >= 4 {
460 return args[3].value();
461 }
462 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
463 ExcelError::new(ExcelErrorKind::Na),
464 )))
465 }
466}
467
468#[derive(Debug)]
471pub struct XMatchFn;
472impl Function for XMatchFn {
473 func_caps!(PURE, LOOKUP);
474 fn name(&self) -> &'static str {
475 "XMATCH"
476 }
477 fn min_args(&self) -> usize {
478 2
479 }
480 fn variadic(&self) -> bool {
481 true
482 }
483 fn arg_schema(&self) -> &'static [ArgSchema] {
484 use once_cell::sync::Lazy;
485 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
486 vec![
487 ArgSchema {
489 kinds: smallvec::smallvec![ArgKind::Any],
490 required: true,
491 by_ref: false,
492 shape: ShapeKind::Scalar,
493 coercion: CoercionPolicy::None,
494 max: None,
495 repeating: None,
496 default: None,
497 },
498 ArgSchema {
500 kinds: smallvec::smallvec![ArgKind::Range],
501 required: true,
502 by_ref: true,
503 shape: ShapeKind::Range,
504 coercion: CoercionPolicy::None,
505 max: None,
506 repeating: None,
507 default: None,
508 },
509 ArgSchema {
512 kinds: smallvec::smallvec![ArgKind::Number],
513 required: false,
514 by_ref: false,
515 shape: ShapeKind::Scalar,
516 coercion: CoercionPolicy::NumberLenientText,
517 max: None,
518 repeating: None,
519 default: Some(LiteralValue::Int(0)),
520 },
521 ArgSchema {
524 kinds: smallvec::smallvec![ArgKind::Number],
525 required: false,
526 by_ref: false,
527 shape: ShapeKind::Scalar,
528 coercion: CoercionPolicy::NumberLenientText,
529 max: None,
530 repeating: None,
531 default: Some(LiteralValue::Int(1)),
532 },
533 ]
534 });
535 &SCHEMA
536 }
537 fn eval<'a, 'b, 'c>(
538 &self,
539 args: &'c [ArgumentHandle<'a, 'b>],
540 _ctx: &dyn FunctionContext<'b>,
541 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
542 if args.len() < 2 {
543 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
544 ExcelError::new(ExcelErrorKind::Value),
545 )));
546 }
547 let lookup_value = args[0].value()?.into_literal();
548 if let LiteralValue::Error(ref e) = lookup_value {
549 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
550 e.clone(),
551 )));
552 }
553 let lookup_view = match args[1].range_view() {
554 Ok(v) => v,
555 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
556 };
557
558 let (lookup_rows, lookup_cols) = lookup_view.dims();
559
560 let vertical = if lookup_cols == 1 {
562 true
563 } else if lookup_rows == 1 {
564 false
565 } else if lookup_rows == 0 || lookup_cols == 0 {
566 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
567 ExcelError::new(ExcelErrorKind::Na),
568 )));
569 } else {
570 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
571 ExcelError::new(ExcelErrorKind::Value),
572 )));
573 };
574
575 let lookup_len = if vertical { lookup_rows } else { lookup_cols };
576
577 if lookup_len == 0 {
578 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
579 ExcelError::new(ExcelErrorKind::Na),
580 )));
581 }
582
583 let match_mode = if args.len() >= 3 {
584 match args[2].value()?.into_literal() {
585 LiteralValue::Int(i) => i,
586 LiteralValue::Number(n) => n as i64,
587 _ => 0,
588 }
589 } else {
590 0
591 };
592 let search_mode = if args.len() >= 4 {
593 match args[3].value()?.into_literal() {
594 LiteralValue::Int(i) => i,
595 LiteralValue::Number(n) => n as i64,
596 _ => 1,
597 }
598 } else {
599 1
600 };
601
602 let wildcard = match_mode == 2;
603 let needle = lookup_value;
604
605 let mut found: Option<usize> = None;
606
607 if match_mode == 0 || wildcard {
608 if search_mode == 1 || search_mode == 2 {
610 if lookup_rows > 0 && lookup_cols > 0 {
612 found = super::lookup_utils::find_exact_index_in_view(
613 &lookup_view,
614 &needle,
615 wildcard,
616 )?;
617 }
618 } else if search_mode == -1 || search_mode == -2 {
619 for i in (0..lookup_len).rev() {
621 let cand = if vertical {
622 lookup_view.get_cell(i, 0)
623 } else {
624 lookup_view.get_cell(0, i)
625 };
626 if equals_maybe_wildcard(&needle, &cand, wildcard) {
627 found = Some(i);
628 break;
629 }
630 }
631 } else {
632 for i in 0..lookup_len {
634 let cand = if vertical {
635 lookup_view.get_cell(i, 0)
636 } else {
637 lookup_view.get_cell(0, i)
638 };
639 if equals_maybe_wildcard(&needle, &cand, wildcard) {
640 found = Some(i);
641 break;
642 }
643 }
644 }
645 } else if match_mode == -1 || match_mode == 1 {
646 let needle_num = value_to_f64_lenient(&needle);
648 let mut best_idx: Option<usize> = None;
649 let mut best_val: f64 = if match_mode == -1 {
650 f64::NEG_INFINITY
651 } else {
652 f64::INFINITY
653 };
654
655 let use_reverse = search_mode == -1 || search_mode == -2;
657 let indices: Box<dyn Iterator<Item = usize>> = if use_reverse {
658 Box::new((0..lookup_len).rev())
659 } else {
660 Box::new(0..lookup_len)
661 };
662
663 if (search_mode == 2 || search_mode == -2) && match_mode != 0 {
666 let ascending = search_mode == 2;
667 let mut prev: Option<LiteralValue> = None;
668 for i in 0..lookup_len {
669 let cand = if vertical {
670 lookup_view.get_cell(i, 0)
671 } else {
672 lookup_view.get_cell(0, i)
673 };
674 if let Some(p) = prev.as_ref() {
675 let sorted_ok = if ascending {
676 cmp_for_lookup(p, &cand).is_some_and(|o| o <= 0)
677 } else {
678 cmp_for_lookup(p, &cand).is_some_and(|o| o >= 0)
679 };
680 if !sorted_ok {
681 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
682 ExcelError::new(ExcelErrorKind::Na),
683 )));
684 }
685 }
686 prev = Some(cand);
687 }
688 }
689
690 for i in indices {
691 let cand = if vertical {
692 lookup_view.get_cell(i, 0)
693 } else {
694 lookup_view.get_cell(0, i)
695 };
696
697 if cmp_for_lookup(&cand, &needle).is_some_and(|o| o == 0) {
698 found = Some(i);
699 break;
700 }
701
702 if let (Some(nn), Some(vv)) = (needle_num, value_to_f64_lenient(&cand)) {
703 if match_mode == -1 {
704 if vv <= nn && vv > best_val {
706 best_val = vv;
707 best_idx = Some(i);
708 }
709 } else {
710 if vv >= nn && vv < best_val {
712 best_val = vv;
713 best_idx = Some(i);
714 }
715 }
716 }
717 }
718
719 if found.is_none() {
720 found = best_idx;
721 }
722 } else {
723 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
724 ExcelError::new(ExcelErrorKind::Value),
725 )));
726 }
727
728 match found {
729 Some(idx) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
730 (idx + 1) as i64,
731 ))),
732 None => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
733 ExcelError::new(ExcelErrorKind::Na),
734 ))),
735 }
736 }
737}
738
739#[derive(Debug)]
742pub struct SortFn;
743impl Function for SortFn {
744 func_caps!(PURE);
745 fn name(&self) -> &'static str {
746 "SORT"
747 }
748 fn min_args(&self) -> usize {
749 1
750 }
751 fn variadic(&self) -> bool {
752 true
753 }
754 fn arg_schema(&self) -> &'static [ArgSchema] {
755 use once_cell::sync::Lazy;
756 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
757 vec![
758 ArgSchema {
760 kinds: smallvec::smallvec![ArgKind::Range],
761 required: true,
762 by_ref: true,
763 shape: ShapeKind::Range,
764 coercion: CoercionPolicy::None,
765 max: None,
766 repeating: None,
767 default: None,
768 },
769 ArgSchema {
771 kinds: smallvec::smallvec![ArgKind::Number],
772 required: false,
773 by_ref: false,
774 shape: ShapeKind::Scalar,
775 coercion: CoercionPolicy::NumberLenientText,
776 max: None,
777 repeating: None,
778 default: Some(LiteralValue::Int(1)),
779 },
780 ArgSchema {
782 kinds: smallvec::smallvec![ArgKind::Number],
783 required: false,
784 by_ref: false,
785 shape: ShapeKind::Scalar,
786 coercion: CoercionPolicy::NumberLenientText,
787 max: None,
788 repeating: None,
789 default: Some(LiteralValue::Int(1)),
790 },
791 ArgSchema {
793 kinds: smallvec::smallvec![ArgKind::Logical],
794 required: false,
795 by_ref: false,
796 shape: ShapeKind::Scalar,
797 coercion: CoercionPolicy::Logical,
798 max: None,
799 repeating: None,
800 default: Some(LiteralValue::Boolean(false)),
801 },
802 ]
803 });
804 &SCHEMA
805 }
806 fn eval<'a, 'b, 'c>(
807 &self,
808 args: &'c [ArgumentHandle<'a, 'b>],
809 _ctx: &dyn FunctionContext<'b>,
810 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
811 let view = match args[0].range_view() {
812 Ok(v) => v,
813 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
814 };
815 let (rows, cols) = view.dims();
816 if rows == 0 || cols == 0 {
817 return Ok(crate::traits::CalcValue::Range(
818 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
819 ));
820 }
821
822 let sort_index = if args.len() >= 2 {
823 match args[1].value()?.into_literal() {
824 LiteralValue::Int(i) => i,
825 LiteralValue::Number(n) => n as i64,
826 _ => 1,
827 }
828 } else {
829 1
830 };
831
832 let sort_order = if args.len() >= 3 {
833 match args[2].value()?.into_literal() {
834 LiteralValue::Int(i) => i,
835 LiteralValue::Number(n) => n as i64,
836 _ => 1,
837 }
838 } else {
839 1
840 };
841
842 let by_col = if args.len() >= 4 {
843 matches!(args[3].value()?.into_literal(), LiteralValue::Boolean(true))
844 } else {
845 false
846 };
847
848 let ascending = sort_order >= 0;
849
850 if by_col {
851 let sort_row_idx = (sort_index - 1).max(0) as usize;
853 if sort_row_idx >= rows {
854 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
855 ExcelError::new(ExcelErrorKind::Value),
856 )));
857 }
858
859 let mut columns: Vec<(usize, Vec<LiteralValue>)> = Vec::with_capacity(cols);
861 for c in 0..cols {
862 let mut col_vals: Vec<LiteralValue> = Vec::with_capacity(rows);
863 for r in 0..rows {
864 col_vals.push(view.get_cell(r, c));
865 }
866 columns.push((c, col_vals));
867 }
868
869 columns.sort_by(|a, b| {
871 let val_a = &a.1[sort_row_idx];
872 let val_b = &b.1[sort_row_idx];
873 let cmp = cmp_for_lookup(val_a, val_b).unwrap_or(0);
874 if ascending { cmp.cmp(&0) } else { 0.cmp(&cmp) }
875 });
876
877 let mut out: Vec<Vec<LiteralValue>> = vec![Vec::with_capacity(cols); rows];
879 for (_orig_idx, col_vals) in columns {
880 for (r, val) in col_vals.into_iter().enumerate() {
881 out[r].push(val);
882 }
883 }
884
885 Ok(collapse_if_scalar(out, _ctx.date_system()))
886 } else {
887 let sort_col_idx = (sort_index - 1).max(0) as usize;
889 if sort_col_idx >= cols {
890 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
891 ExcelError::new(ExcelErrorKind::Value),
892 )));
893 }
894
895 let mut row_data: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
897 for r in 0..rows {
898 let mut row_vals: Vec<LiteralValue> = Vec::with_capacity(cols);
899 for c in 0..cols {
900 row_vals.push(view.get_cell(r, c));
901 }
902 row_data.push(row_vals);
903 }
904
905 row_data.sort_by(|a, b| {
907 let val_a = &a[sort_col_idx];
908 let val_b = &b[sort_col_idx];
909 let cmp = cmp_for_lookup(val_a, val_b).unwrap_or(0);
910 if ascending { cmp.cmp(&0) } else { 0.cmp(&cmp) }
911 });
912
913 Ok(collapse_if_scalar(row_data, _ctx.date_system()))
914 }
915 }
916}
917
918#[derive(Debug)]
921pub struct SortByFn;
922impl Function for SortByFn {
923 func_caps!(PURE);
924 fn name(&self) -> &'static str {
925 "SORTBY"
926 }
927 fn min_args(&self) -> usize {
928 2
929 }
930 fn variadic(&self) -> bool {
931 true
932 }
933 fn arg_schema(&self) -> &'static [ArgSchema] {
934 use once_cell::sync::Lazy;
935 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
936 vec![
937 ArgSchema {
939 kinds: smallvec::smallvec![ArgKind::Range],
940 required: true,
941 by_ref: true,
942 shape: ShapeKind::Range,
943 coercion: CoercionPolicy::None,
944 max: None,
945 repeating: None,
946 default: None,
947 },
948 ArgSchema {
950 kinds: smallvec::smallvec![ArgKind::Range],
951 required: true,
952 by_ref: true,
953 shape: ShapeKind::Range,
954 coercion: CoercionPolicy::None,
955 max: None,
956 repeating: None,
957 default: None,
958 },
959 ArgSchema {
961 kinds: smallvec::smallvec![ArgKind::Number],
962 required: false,
963 by_ref: false,
964 shape: ShapeKind::Scalar,
965 coercion: CoercionPolicy::NumberLenientText,
966 max: None,
967 repeating: None,
968 default: Some(LiteralValue::Int(1)),
969 },
970 ]
972 });
973 &SCHEMA
974 }
975 fn eval<'a, 'b, 'c>(
976 &self,
977 args: &'c [ArgumentHandle<'a, 'b>],
978 _ctx: &dyn FunctionContext<'b>,
979 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
980 if args.len() < 2 {
981 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
982 ExcelError::new(ExcelErrorKind::Value),
983 )));
984 }
985
986 let view = match args[0].range_view() {
987 Ok(v) => v,
988 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
989 };
990 let (rows, cols) = view.dims();
991 if rows == 0 || cols == 0 {
992 return Ok(crate::traits::CalcValue::Range(
993 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
994 ));
995 }
996
997 let mut sort_criteria: Vec<(Vec<LiteralValue>, bool)> = Vec::new();
1000 let mut arg_idx = 1;
1001
1002 while arg_idx < args.len() {
1003 let by_view = match args[arg_idx].range_view() {
1005 Ok(v) => v,
1006 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1007 };
1008 let (by_rows, by_cols) = by_view.dims();
1009
1010 let by_values: Vec<LiteralValue> = if by_cols == 1 {
1012 if by_rows != rows {
1013 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1014 ExcelError::new(ExcelErrorKind::Value),
1015 )));
1016 }
1017 (0..by_rows).map(|r| by_view.get_cell(r, 0)).collect()
1018 } else if by_rows == 1 {
1019 if by_cols != rows {
1020 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1021 ExcelError::new(ExcelErrorKind::Value),
1022 )));
1023 }
1024 (0..by_cols).map(|c| by_view.get_cell(0, c)).collect()
1025 } else {
1026 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1027 ExcelError::new(ExcelErrorKind::Value),
1028 )));
1029 };
1030
1031 arg_idx += 1;
1032
1033 let ascending = if arg_idx < args.len() {
1035 match args[arg_idx].value() {
1038 Ok(v) => {
1039 let lit = v.into_literal();
1040 match lit {
1041 LiteralValue::Int(i) => {
1042 arg_idx += 1;
1043 i >= 0
1044 }
1045 LiteralValue::Number(n) => {
1046 arg_idx += 1;
1047 n >= 0.0
1048 }
1049 _ => true, }
1051 }
1052 Err(_) => true,
1053 }
1054 } else {
1055 true
1056 };
1057
1058 sort_criteria.push((by_values, ascending));
1059 }
1060
1061 if sort_criteria.is_empty() {
1062 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1063 ExcelError::new(ExcelErrorKind::Value),
1064 )));
1065 }
1066
1067 let mut indexed_rows: Vec<(usize, Vec<LiteralValue>)> = Vec::with_capacity(rows);
1069 for r in 0..rows {
1070 let mut row_vals: Vec<LiteralValue> = Vec::with_capacity(cols);
1071 for c in 0..cols {
1072 row_vals.push(view.get_cell(r, c));
1073 }
1074 indexed_rows.push((r, row_vals));
1075 }
1076
1077 indexed_rows.sort_by(|a, b| {
1079 for (by_values, ascending) in &sort_criteria {
1080 let val_a = &by_values[a.0];
1081 let val_b = &by_values[b.0];
1082 let cmp = cmp_for_lookup(val_a, val_b).unwrap_or(0);
1083 if cmp != 0 {
1084 return if *ascending { cmp.cmp(&0) } else { 0.cmp(&cmp) };
1085 }
1086 }
1087 std::cmp::Ordering::Equal
1088 });
1089
1090 let out: Vec<Vec<LiteralValue>> = indexed_rows.into_iter().map(|(_, row)| row).collect();
1092
1093 Ok(collapse_if_scalar(out, _ctx.date_system()))
1094 }
1095}
1096
1097#[derive(Debug)]
1100pub struct RandArrayFn;
1101impl Function for RandArrayFn {
1102 fn caps(&self) -> crate::function::FnCaps {
1104 crate::function::FnCaps::empty()
1105 }
1106 fn name(&self) -> &'static str {
1107 "RANDARRAY"
1108 }
1109 fn min_args(&self) -> usize {
1110 0
1111 }
1112 fn variadic(&self) -> bool {
1113 true
1114 }
1115 fn arg_schema(&self) -> &'static [ArgSchema] {
1116 use once_cell::sync::Lazy;
1117 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
1118 vec![
1119 ArgSchema {
1121 kinds: smallvec::smallvec![ArgKind::Number],
1122 required: false,
1123 by_ref: false,
1124 shape: ShapeKind::Scalar,
1125 coercion: CoercionPolicy::NumberLenientText,
1126 max: None,
1127 repeating: None,
1128 default: Some(LiteralValue::Int(1)),
1129 },
1130 ArgSchema {
1132 kinds: smallvec::smallvec![ArgKind::Number],
1133 required: false,
1134 by_ref: false,
1135 shape: ShapeKind::Scalar,
1136 coercion: CoercionPolicy::NumberLenientText,
1137 max: None,
1138 repeating: None,
1139 default: Some(LiteralValue::Int(1)),
1140 },
1141 ArgSchema {
1143 kinds: smallvec::smallvec![ArgKind::Number],
1144 required: false,
1145 by_ref: false,
1146 shape: ShapeKind::Scalar,
1147 coercion: CoercionPolicy::NumberLenientText,
1148 max: None,
1149 repeating: None,
1150 default: Some(LiteralValue::Int(0)),
1151 },
1152 ArgSchema {
1154 kinds: smallvec::smallvec![ArgKind::Number],
1155 required: false,
1156 by_ref: false,
1157 shape: ShapeKind::Scalar,
1158 coercion: CoercionPolicy::NumberLenientText,
1159 max: None,
1160 repeating: None,
1161 default: Some(LiteralValue::Int(1)),
1162 },
1163 ArgSchema {
1165 kinds: smallvec::smallvec![ArgKind::Logical],
1166 required: false,
1167 by_ref: false,
1168 shape: ShapeKind::Scalar,
1169 coercion: CoercionPolicy::Logical,
1170 max: None,
1171 repeating: None,
1172 default: Some(LiteralValue::Boolean(false)),
1173 },
1174 ]
1175 });
1176 &SCHEMA
1177 }
1178 fn eval<'a, 'b, 'c>(
1179 &self,
1180 args: &'c [ArgumentHandle<'a, 'b>],
1181 _ctx: &dyn FunctionContext<'b>,
1182 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1183 use rand::Rng;
1184
1185 let num = |a: &ArgumentHandle| -> Result<f64, ExcelError> {
1187 Ok(match a.value()?.into_literal() {
1188 LiteralValue::Int(i) => i as f64,
1189 LiteralValue::Number(n) => n,
1190 LiteralValue::Error(e) => return Err(e),
1191 _other => {
1192 return Err(ExcelError::new(ExcelErrorKind::Value));
1193 }
1194 })
1195 };
1196
1197 let rows = if !args.is_empty() {
1198 num(&args[0])? as i64
1199 } else {
1200 1
1201 };
1202 let cols = if args.len() >= 2 {
1203 num(&args[1])? as i64
1204 } else {
1205 1
1206 };
1207 let min_val = if args.len() >= 3 { num(&args[2])? } else { 0.0 };
1208 let max_val = if args.len() >= 4 { num(&args[3])? } else { 1.0 };
1209 let whole_number = if args.len() >= 5 {
1210 matches!(args[4].value()?.into_literal(), LiteralValue::Boolean(true))
1211 } else {
1212 false
1213 };
1214
1215 if rows <= 0 || cols <= 0 {
1217 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1218 ExcelError::new(ExcelErrorKind::Value),
1219 )));
1220 }
1221
1222 if whole_number && min_val > max_val {
1224 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1225 ExcelError::new(ExcelErrorKind::Value),
1226 )));
1227 }
1228
1229 let mut rng = rand::thread_rng();
1230 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows as usize);
1231
1232 for _r in 0..rows {
1233 let mut row_vec: Vec<LiteralValue> = Vec::with_capacity(cols as usize);
1234 for _c in 0..cols {
1235 let value = if whole_number {
1236 let min_int = min_val.ceil() as i64;
1238 let max_int = max_val.floor() as i64;
1239 if min_int > max_int {
1240 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1241 ExcelError::new(ExcelErrorKind::Value),
1242 )));
1243 }
1244 let rand_int = rng.gen_range(min_int..=max_int);
1245 LiteralValue::Int(rand_int)
1246 } else {
1247 let rand_float = rng.r#gen::<f64>() * (max_val - min_val) + min_val;
1249 LiteralValue::Number(rand_float)
1250 };
1251 row_vec.push(value);
1252 }
1253 out.push(row_vec);
1254 }
1255
1256 Ok(collapse_if_scalar(out, _ctx.date_system()))
1257 }
1258}
1259
1260#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1264enum GroupAggregation {
1265 Sum,
1266 Average,
1267 Count,
1268 CountA,
1269 Max,
1270 Min,
1271 Product,
1272 StDev,
1273 StDevP,
1274 Var,
1275 VarP,
1276 Median,
1277}
1278
1279impl GroupAggregation {
1280 fn from_literal(val: &LiteralValue) -> Option<Self> {
1281 match val {
1282 LiteralValue::Text(s) => Self::from_str(s),
1283 LiteralValue::Int(n) => Self::from_num(*n as i32),
1284 LiteralValue::Number(n) => Self::from_num(*n as i32),
1285 _ => None,
1286 }
1287 }
1288
1289 fn from_str(s: &str) -> Option<Self> {
1290 let upper = s.to_ascii_uppercase();
1291 match upper.as_str() {
1292 "SUM" => Some(Self::Sum),
1293 "AVERAGE" | "AVG" => Some(Self::Average),
1294 "COUNT" => Some(Self::Count),
1295 "COUNTA" => Some(Self::CountA),
1296 "MAX" => Some(Self::Max),
1297 "MIN" => Some(Self::Min),
1298 "PRODUCT" => Some(Self::Product),
1299 "STDEV" | "STDEV.S" => Some(Self::StDev),
1300 "STDEVP" | "STDEV.P" => Some(Self::StDevP),
1301 "VAR" | "VAR.S" => Some(Self::Var),
1302 "VARP" | "VAR.P" => Some(Self::VarP),
1303 "MEDIAN" => Some(Self::Median),
1304 _ => None,
1305 }
1306 }
1307
1308 fn from_num(n: i32) -> Option<Self> {
1309 match n {
1311 1 => Some(Self::Average),
1312 2 => Some(Self::Count),
1313 3 => Some(Self::CountA),
1314 4 => Some(Self::Max),
1315 5 => Some(Self::Min),
1316 6 => Some(Self::Product),
1317 7 => Some(Self::StDev),
1318 8 => Some(Self::StDevP),
1319 9 => Some(Self::Sum),
1320 10 => Some(Self::Var),
1321 11 => Some(Self::VarP),
1322 12 => Some(Self::Median),
1323 _ => None,
1324 }
1325 }
1326
1327 fn apply(&self, values: &[f64]) -> f64 {
1328 if values.is_empty() {
1329 return match self {
1330 Self::Count | Self::CountA => 0.0,
1331 Self::Sum | Self::Product => 0.0,
1332 _ => f64::NAN,
1333 };
1334 }
1335
1336 match self {
1337 Self::Sum => values.iter().sum(),
1338 Self::Average => values.iter().sum::<f64>() / values.len() as f64,
1339 Self::Count | Self::CountA => values.len() as f64,
1340 Self::Max => values.iter().copied().fold(f64::NEG_INFINITY, f64::max),
1341 Self::Min => values.iter().copied().fold(f64::INFINITY, f64::min),
1342 Self::Product => values.iter().product(),
1343 Self::StDev => {
1344 if values.len() < 2 {
1345 return f64::NAN;
1346 }
1347 let mean = values.iter().sum::<f64>() / values.len() as f64;
1348 let variance = values.iter().map(|v| (v - mean).powi(2)).sum::<f64>()
1349 / (values.len() - 1) as f64;
1350 variance.sqrt()
1351 }
1352 Self::StDevP => {
1353 let mean = values.iter().sum::<f64>() / values.len() as f64;
1354 let variance =
1355 values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / values.len() as f64;
1356 variance.sqrt()
1357 }
1358 Self::Var => {
1359 if values.len() < 2 {
1360 return f64::NAN;
1361 }
1362 let mean = values.iter().sum::<f64>() / values.len() as f64;
1363 values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64
1364 }
1365 Self::VarP => {
1366 let mean = values.iter().sum::<f64>() / values.len() as f64;
1367 values.iter().map(|v| (v - mean).powi(2)).sum::<f64>() / values.len() as f64
1368 }
1369 Self::Median => {
1370 let mut sorted = values.to_vec();
1371 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1372 let mid = sorted.len() / 2;
1373 if sorted.len().is_multiple_of(2) {
1374 (sorted[mid - 1] + sorted[mid]) / 2.0
1375 } else {
1376 sorted[mid]
1377 }
1378 }
1379 }
1380 }
1381}
1382
1383fn literal_to_group_key(v: &LiteralValue) -> String {
1385 match v {
1386 LiteralValue::Text(s) => s.clone(),
1387 LiteralValue::Int(i) => i.to_string(),
1388 LiteralValue::Number(n) => format!("{:.10}", n),
1389 LiteralValue::Boolean(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
1390 LiteralValue::Empty => String::new(),
1391 LiteralValue::Error(e) => format!("#{:?}", e.kind),
1392 LiteralValue::Array(_) => "[Array]".to_string(),
1393 LiteralValue::Date(d) => d.to_string(),
1394 LiteralValue::DateTime(dt) => dt.to_string(),
1395 LiteralValue::Time(t) => t.to_string(),
1396 LiteralValue::Duration(d) => format!("{:?}", d),
1397 LiteralValue::Pending => "[Pending]".to_string(),
1398 }
1399}
1400
1401fn literal_to_num_opt(v: &LiteralValue) -> Option<f64> {
1403 match v {
1404 LiteralValue::Number(n) => Some(*n),
1405 LiteralValue::Int(i) => Some(*i as f64),
1406 LiteralValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
1407 _ => None,
1408 }
1409}
1410
1411#[derive(Debug)]
1412pub struct GroupByFn;
1413
1414impl Function for GroupByFn {
1415 func_caps!(PURE);
1416 fn name(&self) -> &'static str {
1417 "GROUPBY"
1418 }
1419 fn min_args(&self) -> usize {
1420 3
1421 }
1422 fn variadic(&self) -> bool {
1423 true
1424 }
1425 fn arg_schema(&self) -> &'static [ArgSchema] {
1426 use once_cell::sync::Lazy;
1427 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
1428 vec![
1429 ArgSchema {
1431 kinds: smallvec::smallvec![ArgKind::Range],
1432 required: true,
1433 by_ref: true,
1434 shape: ShapeKind::Range,
1435 coercion: CoercionPolicy::None,
1436 max: None,
1437 repeating: None,
1438 default: None,
1439 },
1440 ArgSchema {
1442 kinds: smallvec::smallvec![ArgKind::Range],
1443 required: true,
1444 by_ref: true,
1445 shape: ShapeKind::Range,
1446 coercion: CoercionPolicy::None,
1447 max: None,
1448 repeating: None,
1449 default: None,
1450 },
1451 ArgSchema {
1453 kinds: smallvec::smallvec![ArgKind::Any],
1454 required: true,
1455 by_ref: false,
1456 shape: ShapeKind::Scalar,
1457 coercion: CoercionPolicy::None,
1458 max: None,
1459 repeating: None,
1460 default: None,
1461 },
1462 ArgSchema {
1464 kinds: smallvec::smallvec![ArgKind::Number],
1465 required: false,
1466 by_ref: false,
1467 shape: ShapeKind::Scalar,
1468 coercion: CoercionPolicy::NumberLenientText,
1469 max: None,
1470 repeating: None,
1471 default: Some(LiteralValue::Int(1)),
1472 },
1473 ArgSchema {
1475 kinds: smallvec::smallvec![ArgKind::Number],
1476 required: false,
1477 by_ref: false,
1478 shape: ShapeKind::Scalar,
1479 coercion: CoercionPolicy::NumberLenientText,
1480 max: None,
1481 repeating: None,
1482 default: Some(LiteralValue::Int(0)),
1483 },
1484 ArgSchema {
1486 kinds: smallvec::smallvec![ArgKind::Number],
1487 required: false,
1488 by_ref: false,
1489 shape: ShapeKind::Scalar,
1490 coercion: CoercionPolicy::NumberLenientText,
1491 max: None,
1492 repeating: None,
1493 default: Some(LiteralValue::Int(0)),
1494 },
1495 ]
1496 });
1497 &SCHEMA
1498 }
1499
1500 fn eval<'a, 'b, 'c>(
1501 &self,
1502 args: &'c [ArgumentHandle<'a, 'b>],
1503 _ctx: &dyn FunctionContext<'b>,
1504 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1505 if args.len() < 3 {
1506 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1507 ExcelError::new(ExcelErrorKind::Value),
1508 )));
1509 }
1510
1511 let row_fields_view = match args[0].range_view() {
1513 Ok(v) => v,
1514 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1515 };
1516 let values_view = match args[1].range_view() {
1517 Ok(v) => v,
1518 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1519 };
1520
1521 let agg_val = args[2].value()?.into_literal();
1523 let aggregation = match GroupAggregation::from_literal(&agg_val) {
1524 Some(a) => a,
1525 None => {
1526 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1527 ExcelError::new(ExcelErrorKind::Value)
1528 .with_message("Invalid aggregation function"),
1529 )));
1530 }
1531 };
1532
1533 let field_headers = if args.len() >= 4 {
1535 match args[3].value()?.into_literal() {
1536 LiteralValue::Int(i) => i as i32,
1537 LiteralValue::Number(n) => n as i32,
1538 _ => 1,
1539 }
1540 } else {
1541 1
1542 };
1543
1544 let total_depth = if args.len() >= 5 {
1545 match args[4].value()?.into_literal() {
1546 LiteralValue::Int(i) => i as i32,
1547 LiteralValue::Number(n) => n as i32,
1548 _ => 0,
1549 }
1550 } else {
1551 0
1552 };
1553
1554 let sort_order = if args.len() >= 6 {
1555 match args[5].value()?.into_literal() {
1556 LiteralValue::Int(i) => i as i32,
1557 LiteralValue::Number(n) => n as i32,
1558 _ => 0,
1559 }
1560 } else {
1561 0
1562 };
1563
1564 let (rf_rows, rf_cols) = row_fields_view.dims();
1565 let (val_rows, val_cols) = values_view.dims();
1566
1567 let has_headers = field_headers == 1 || field_headers == 3;
1569 let data_start_row = if has_headers { 1 } else { 0 };
1570
1571 if rf_rows != val_rows {
1573 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1574 ExcelError::new(ExcelErrorKind::Value)
1575 .with_message("Row fields and values must have same number of rows"),
1576 )));
1577 }
1578
1579 if rf_rows <= data_start_row {
1580 return Ok(crate::traits::CalcValue::Range(
1582 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
1583 ));
1584 }
1585
1586 let mut groups: std::collections::HashMap<String, Vec<Vec<f64>>> = HashMap::new();
1589 let mut group_order: Vec<String> = Vec::new();
1590
1591 for r in data_start_row..rf_rows {
1592 let mut key_parts: Vec<String> = Vec::with_capacity(rf_cols);
1594 for c in 0..rf_cols {
1595 key_parts.push(literal_to_group_key(&row_fields_view.get_cell(r, c)));
1596 }
1597 let key = key_parts.join("\x00"); let mut row_values: Vec<Option<f64>> = Vec::with_capacity(val_cols);
1601 for c in 0..val_cols {
1602 row_values.push(literal_to_num_opt(&values_view.get_cell(r, c)));
1603 }
1604
1605 if !groups.contains_key(&key) {
1607 group_order.push(key.clone());
1608 groups.insert(key.clone(), vec![Vec::new(); val_cols]);
1609 }
1610
1611 let group_vals = groups.get_mut(&key).unwrap();
1612 for (c, val) in row_values.iter().enumerate() {
1613 if let Some(v) = val {
1614 group_vals[c].push(*v);
1615 }
1616 }
1617 }
1618
1619 if sort_order != 0 {
1621 group_order.sort_by(|a, b| if sort_order > 0 { a.cmp(b) } else { b.cmp(a) });
1622 }
1623
1624 let mut output: Vec<Vec<LiteralValue>> = Vec::new();
1626
1627 let generate_headers = field_headers == 2 || field_headers == 3;
1629 if generate_headers {
1630 let mut header_row: Vec<LiteralValue> = Vec::new();
1631 for c in 0..rf_cols {
1633 if has_headers {
1634 header_row.push(row_fields_view.get_cell(0, c));
1635 } else {
1636 header_row.push(LiteralValue::Text(format!("Field{}", c + 1)));
1637 }
1638 }
1639 for c in 0..val_cols {
1641 if has_headers {
1642 header_row.push(values_view.get_cell(0, c));
1643 } else {
1644 header_row.push(LiteralValue::Text(format!("Value{}", c + 1)));
1645 }
1646 }
1647 output.push(header_row);
1648 }
1649
1650 for key in &group_order {
1652 let mut row: Vec<LiteralValue> = Vec::new();
1653
1654 let key_parts: Vec<&str> = key.split('\x00').collect();
1656 for part in &key_parts {
1657 row.push(LiteralValue::Text(part.to_string()));
1658 }
1659
1660 let group_vals = groups.get(key).unwrap();
1662 for col_vals in group_vals {
1663 let result = aggregation.apply(col_vals);
1664 if result.is_nan() {
1665 row.push(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)));
1666 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
1667 row.push(LiteralValue::Int(result as i64));
1668 } else {
1669 row.push(LiteralValue::Number(result));
1670 }
1671 }
1672 output.push(row);
1673 }
1674
1675 if total_depth >= 1 {
1677 let mut total_row: Vec<LiteralValue> = Vec::new();
1678 total_row.push(LiteralValue::Text("Grand Total".to_string()));
1680 for _ in 1..rf_cols {
1681 total_row.push(LiteralValue::Empty);
1682 }
1683
1684 for c in 0..val_cols {
1686 let mut all_vals: Vec<f64> = Vec::new();
1687 for group_vals in groups.values() {
1688 all_vals.extend(&group_vals[c]);
1689 }
1690 let result = aggregation.apply(&all_vals);
1691 if result.is_nan() {
1692 total_row.push(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)));
1693 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
1694 total_row.push(LiteralValue::Int(result as i64));
1695 } else {
1696 total_row.push(LiteralValue::Number(result));
1697 }
1698 }
1699 output.push(total_row);
1700 }
1701
1702 if output.is_empty() {
1703 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1704 ExcelError::new(ExcelErrorKind::Calc),
1705 )));
1706 }
1707
1708 Ok(collapse_if_scalar(output, _ctx.date_system()))
1709 }
1710}
1711
1712#[derive(Debug)]
1715pub struct PivotByFn;
1716
1717impl Function for PivotByFn {
1718 func_caps!(PURE);
1719 fn name(&self) -> &'static str {
1720 "PIVOTBY"
1721 }
1722 fn min_args(&self) -> usize {
1723 4
1724 }
1725 fn variadic(&self) -> bool {
1726 true
1727 }
1728 fn arg_schema(&self) -> &'static [ArgSchema] {
1729 use once_cell::sync::Lazy;
1730 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
1731 vec![
1732 ArgSchema {
1734 kinds: smallvec::smallvec![ArgKind::Range],
1735 required: true,
1736 by_ref: true,
1737 shape: ShapeKind::Range,
1738 coercion: CoercionPolicy::None,
1739 max: None,
1740 repeating: None,
1741 default: None,
1742 },
1743 ArgSchema {
1745 kinds: smallvec::smallvec![ArgKind::Range],
1746 required: true,
1747 by_ref: true,
1748 shape: ShapeKind::Range,
1749 coercion: CoercionPolicy::None,
1750 max: None,
1751 repeating: None,
1752 default: None,
1753 },
1754 ArgSchema {
1756 kinds: smallvec::smallvec![ArgKind::Range],
1757 required: true,
1758 by_ref: true,
1759 shape: ShapeKind::Range,
1760 coercion: CoercionPolicy::None,
1761 max: None,
1762 repeating: None,
1763 default: None,
1764 },
1765 ArgSchema {
1767 kinds: smallvec::smallvec![ArgKind::Any],
1768 required: true,
1769 by_ref: false,
1770 shape: ShapeKind::Scalar,
1771 coercion: CoercionPolicy::None,
1772 max: None,
1773 repeating: None,
1774 default: None,
1775 },
1776 ArgSchema {
1778 kinds: smallvec::smallvec![ArgKind::Number],
1779 required: false,
1780 by_ref: false,
1781 shape: ShapeKind::Scalar,
1782 coercion: CoercionPolicy::NumberLenientText,
1783 max: None,
1784 repeating: None,
1785 default: Some(LiteralValue::Int(1)),
1786 },
1787 ArgSchema {
1789 kinds: smallvec::smallvec![ArgKind::Number],
1790 required: false,
1791 by_ref: false,
1792 shape: ShapeKind::Scalar,
1793 coercion: CoercionPolicy::NumberLenientText,
1794 max: None,
1795 repeating: None,
1796 default: Some(LiteralValue::Int(0)),
1797 },
1798 ArgSchema {
1800 kinds: smallvec::smallvec![ArgKind::Number],
1801 required: false,
1802 by_ref: false,
1803 shape: ShapeKind::Scalar,
1804 coercion: CoercionPolicy::NumberLenientText,
1805 max: None,
1806 repeating: None,
1807 default: Some(LiteralValue::Int(0)),
1808 },
1809 ArgSchema {
1811 kinds: smallvec::smallvec![ArgKind::Number],
1812 required: false,
1813 by_ref: false,
1814 shape: ShapeKind::Scalar,
1815 coercion: CoercionPolicy::NumberLenientText,
1816 max: None,
1817 repeating: None,
1818 default: Some(LiteralValue::Int(0)),
1819 },
1820 ArgSchema {
1822 kinds: smallvec::smallvec![ArgKind::Number],
1823 required: false,
1824 by_ref: false,
1825 shape: ShapeKind::Scalar,
1826 coercion: CoercionPolicy::NumberLenientText,
1827 max: None,
1828 repeating: None,
1829 default: Some(LiteralValue::Int(0)),
1830 },
1831 ]
1832 });
1833 &SCHEMA
1834 }
1835
1836 fn eval<'a, 'b, 'c>(
1837 &self,
1838 args: &'c [ArgumentHandle<'a, 'b>],
1839 _ctx: &dyn FunctionContext<'b>,
1840 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1841 if args.len() < 4 {
1842 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1843 ExcelError::new(ExcelErrorKind::Value),
1844 )));
1845 }
1846
1847 let row_fields_view = match args[0].range_view() {
1849 Ok(v) => v,
1850 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1851 };
1852 let col_fields_view = match args[1].range_view() {
1853 Ok(v) => v,
1854 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1855 };
1856 let values_view = match args[2].range_view() {
1857 Ok(v) => v,
1858 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1859 };
1860
1861 let agg_val = args[3].value()?.into_literal();
1863 let aggregation = match GroupAggregation::from_literal(&agg_val) {
1864 Some(a) => a,
1865 None => {
1866 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1867 ExcelError::new(ExcelErrorKind::Value)
1868 .with_message("Invalid aggregation function"),
1869 )));
1870 }
1871 };
1872
1873 let field_headers = if args.len() >= 5 {
1875 match args[4].value()?.into_literal() {
1876 LiteralValue::Int(i) => i as i32,
1877 LiteralValue::Number(n) => n as i32,
1878 _ => 1,
1879 }
1880 } else {
1881 1
1882 };
1883
1884 let row_total_depth = if args.len() >= 6 {
1885 match args[5].value()?.into_literal() {
1886 LiteralValue::Int(i) => i as i32,
1887 LiteralValue::Number(n) => n as i32,
1888 _ => 0,
1889 }
1890 } else {
1891 0
1892 };
1893
1894 let row_sort_order = if args.len() >= 7 {
1895 match args[6].value()?.into_literal() {
1896 LiteralValue::Int(i) => i as i32,
1897 LiteralValue::Number(n) => n as i32,
1898 _ => 0,
1899 }
1900 } else {
1901 0
1902 };
1903
1904 let col_total_depth = if args.len() >= 8 {
1905 match args[7].value()?.into_literal() {
1906 LiteralValue::Int(i) => i as i32,
1907 LiteralValue::Number(n) => n as i32,
1908 _ => 0,
1909 }
1910 } else {
1911 0
1912 };
1913
1914 let col_sort_order = if args.len() >= 9 {
1915 match args[8].value()?.into_literal() {
1916 LiteralValue::Int(i) => i as i32,
1917 LiteralValue::Number(n) => n as i32,
1918 _ => 0,
1919 }
1920 } else {
1921 0
1922 };
1923
1924 let (rf_rows, rf_cols) = row_fields_view.dims();
1925 let (cf_rows, _cf_cols) = col_fields_view.dims();
1926 let (val_rows, _val_cols) = values_view.dims();
1927
1928 if rf_rows != cf_rows || rf_rows != val_rows {
1930 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1931 ExcelError::new(ExcelErrorKind::Value)
1932 .with_message("All ranges must have same number of rows"),
1933 )));
1934 }
1935
1936 let has_headers = field_headers == 1 || field_headers == 3;
1937 let data_start_row = if has_headers { 1 } else { 0 };
1938
1939 if rf_rows <= data_start_row {
1940 return Ok(crate::traits::CalcValue::Range(
1941 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
1942 ));
1943 }
1944
1945 let mut row_keys: Vec<String> = Vec::new();
1947 let mut col_keys: Vec<String> = Vec::new();
1948 let mut row_key_set: std::collections::HashSet<String> = std::collections::HashSet::new();
1949 let mut col_key_set: std::collections::HashSet<String> = std::collections::HashSet::new();
1950
1951 let mut pivot_data: HashMap<(String, String), Vec<f64>> = HashMap::new();
1953
1954 for r in data_start_row..rf_rows {
1955 let mut row_key_parts: Vec<String> = Vec::with_capacity(rf_cols);
1957 for c in 0..rf_cols {
1958 row_key_parts.push(literal_to_group_key(&row_fields_view.get_cell(r, c)));
1959 }
1960 let row_key = row_key_parts.join("\x00");
1961
1962 let col_key = literal_to_group_key(&col_fields_view.get_cell(r, 0));
1965
1966 let val = literal_to_num_opt(&values_view.get_cell(r, 0));
1968
1969 if !row_key_set.contains(&row_key) {
1971 row_key_set.insert(row_key.clone());
1972 row_keys.push(row_key.clone());
1973 }
1974 if !col_key_set.contains(&col_key) {
1975 col_key_set.insert(col_key.clone());
1976 col_keys.push(col_key.clone());
1977 }
1978
1979 let entry = pivot_data.entry((row_key, col_key)).or_default();
1981 if let Some(v) = val {
1982 entry.push(v);
1983 }
1984 }
1985
1986 if row_sort_order != 0 {
1988 row_keys.sort_by(|a, b| {
1989 if row_sort_order > 0 {
1990 a.cmp(b)
1991 } else {
1992 b.cmp(a)
1993 }
1994 });
1995 }
1996 if col_sort_order != 0 {
1997 col_keys.sort_by(|a, b| {
1998 if col_sort_order > 0 {
1999 a.cmp(b)
2000 } else {
2001 b.cmp(a)
2002 }
2003 });
2004 }
2005
2006 let mut output: Vec<Vec<LiteralValue>> = Vec::new();
2008
2009 let generate_headers = field_headers == 2 || field_headers == 3;
2011 if generate_headers || has_headers {
2012 let mut header_row: Vec<LiteralValue> = Vec::new();
2013 for _ in 0..rf_cols {
2015 header_row.push(LiteralValue::Empty);
2016 }
2017 for col_key in &col_keys {
2019 let parts: Vec<&str> = col_key.split('\x00').collect();
2021 header_row.push(LiteralValue::Text(parts.join(" ")));
2022 }
2023 if col_total_depth >= 1 {
2025 header_row.push(LiteralValue::Text("Total".to_string()));
2026 }
2027 output.push(header_row);
2028 }
2029
2030 for row_key in &row_keys {
2032 let mut row: Vec<LiteralValue> = Vec::new();
2033
2034 let row_parts: Vec<&str> = row_key.split('\x00').collect();
2036 for part in &row_parts {
2037 row.push(LiteralValue::Text(part.to_string()));
2038 }
2039
2040 let mut row_total_vals: Vec<f64> = Vec::new();
2042 for col_key in &col_keys {
2043 let key = (row_key.clone(), col_key.clone());
2044 let vals = pivot_data.get(&key).map(|v| v.as_slice()).unwrap_or(&[]);
2045 let result = aggregation.apply(vals);
2046
2047 row_total_vals.extend(vals);
2049
2050 if result.is_nan() || vals.is_empty() {
2051 row.push(LiteralValue::Empty);
2052 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
2053 row.push(LiteralValue::Int(result as i64));
2054 } else {
2055 row.push(LiteralValue::Number(result));
2056 }
2057 }
2058
2059 if col_total_depth >= 1 {
2061 let result = aggregation.apply(&row_total_vals);
2062 if result.is_nan() {
2063 row.push(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)));
2064 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
2065 row.push(LiteralValue::Int(result as i64));
2066 } else {
2067 row.push(LiteralValue::Number(result));
2068 }
2069 }
2070
2071 output.push(row);
2072 }
2073
2074 if row_total_depth >= 1 {
2076 let mut total_row: Vec<LiteralValue> = Vec::new();
2077 total_row.push(LiteralValue::Text("Total".to_string()));
2078 for _ in 1..rf_cols {
2079 total_row.push(LiteralValue::Empty);
2080 }
2081
2082 let mut grand_total_vals: Vec<f64> = Vec::new();
2083 for col_key in &col_keys {
2084 let mut col_vals: Vec<f64> = Vec::new();
2085 for row_key in &row_keys {
2086 let key = (row_key.clone(), col_key.clone());
2087 if let Some(vals) = pivot_data.get(&key) {
2088 col_vals.extend(vals);
2089 }
2090 }
2091 grand_total_vals.extend(&col_vals);
2092 let result = aggregation.apply(&col_vals);
2093 if result.is_nan() {
2094 total_row.push(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)));
2095 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
2096 total_row.push(LiteralValue::Int(result as i64));
2097 } else {
2098 total_row.push(LiteralValue::Number(result));
2099 }
2100 }
2101
2102 if col_total_depth >= 1 {
2104 let result = aggregation.apply(&grand_total_vals);
2105 if result.is_nan() {
2106 total_row.push(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Na)));
2107 } else if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
2108 total_row.push(LiteralValue::Int(result as i64));
2109 } else {
2110 total_row.push(LiteralValue::Number(result));
2111 }
2112 }
2113
2114 output.push(total_row);
2115 }
2116
2117 if output.is_empty() {
2118 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2119 ExcelError::new(ExcelErrorKind::Calc),
2120 )));
2121 }
2122
2123 Ok(collapse_if_scalar(output, _ctx.date_system()))
2124 }
2125}
2126
2127#[derive(Debug)]
2130pub struct FilterFn;
2131impl Function for FilterFn {
2132 func_caps!(PURE);
2133 fn name(&self) -> &'static str {
2134 "FILTER"
2135 }
2136 fn min_args(&self) -> usize {
2137 2
2138 }
2139 fn variadic(&self) -> bool {
2140 true
2141 }
2142 fn arg_schema(&self) -> &'static [ArgSchema] {
2143 use once_cell::sync::Lazy;
2144 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2145 vec![
2146 ArgSchema {
2148 kinds: smallvec::smallvec![ArgKind::Range],
2149 required: true,
2150 by_ref: true,
2151 shape: ShapeKind::Range,
2152 coercion: CoercionPolicy::None,
2153 max: None,
2154 repeating: None,
2155 default: None,
2156 },
2157 ArgSchema {
2159 kinds: smallvec::smallvec![ArgKind::Range],
2160 required: true,
2161 by_ref: true,
2162 shape: ShapeKind::Range,
2163 coercion: CoercionPolicy::None,
2164 max: None,
2165 repeating: None,
2166 default: None,
2167 },
2168 ArgSchema {
2170 kinds: smallvec::smallvec![ArgKind::Any],
2171 required: false,
2172 by_ref: false,
2173 shape: ShapeKind::Scalar,
2174 coercion: CoercionPolicy::None,
2175 max: None,
2176 repeating: None,
2177 default: None,
2178 },
2179 ]
2180 });
2181 &SCHEMA
2182 }
2183 fn eval<'a, 'b, 'c>(
2184 &self,
2185 args: &'c [ArgumentHandle<'a, 'b>],
2186 _ctx: &dyn FunctionContext<'b>,
2187 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2188 if args.len() < 2 {
2189 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2190 ExcelError::new(ExcelErrorKind::Value),
2191 )));
2192 }
2193 let array_view = args[0].range_view()?;
2194 let include_view = args[1].range_view()?;
2195
2196 let (array_rows, array_cols) = array_view.dims();
2197 if array_rows == 0 || array_cols == 0 {
2198 return Ok(crate::traits::CalcValue::Range(
2199 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2200 ));
2201 }
2202
2203 let (include_rows, include_cols) = include_view.dims();
2204 if include_rows != array_rows && include_rows != 1 {
2205 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2206 ExcelError::new(ExcelErrorKind::Value),
2207 )));
2208 }
2209
2210 let mut result: Vec<Vec<LiteralValue>> = Vec::new();
2211 for r in 0..array_rows {
2212 let include_r = if include_rows == array_rows { r } else { 0 };
2213 let mut include = false;
2214 for c in 0..include_cols {
2215 if include_view.get_cell(include_r, c).is_truthy() {
2216 include = true;
2217 break;
2218 }
2219 }
2220
2221 if include {
2222 let mut row_out: Vec<LiteralValue> = Vec::with_capacity(array_cols);
2223 for c in 0..array_cols {
2224 row_out.push(array_view.get_cell(r, c));
2225 }
2226 result.push(row_out);
2227 }
2228 }
2229
2230 if result.is_empty() {
2231 if args.len() >= 3 {
2232 return args[2].value();
2233 }
2234 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2235 ExcelError::new(ExcelErrorKind::Calc),
2236 )));
2237 }
2238
2239 Ok(crate::traits::CalcValue::Range(
2240 crate::engine::range_view::RangeView::from_owned_rows(result, _ctx.date_system()),
2241 ))
2242 }
2243}
2244
2245#[derive(Debug)]
2248pub struct UniqueFn;
2249impl Function for UniqueFn {
2250 func_caps!(PURE);
2251 fn name(&self) -> &'static str {
2252 "UNIQUE"
2253 }
2254 fn min_args(&self) -> usize {
2255 1
2256 }
2257 fn variadic(&self) -> bool {
2258 true
2259 }
2260 fn arg_schema(&self) -> &'static [ArgSchema] {
2261 use once_cell::sync::Lazy;
2262 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2263 vec![
2264 ArgSchema {
2265 kinds: smallvec::smallvec![ArgKind::Range],
2266 required: true,
2267 by_ref: true,
2268 shape: ShapeKind::Range,
2269 coercion: CoercionPolicy::None,
2270 max: None,
2271 repeating: None,
2272 default: None,
2273 },
2274 ArgSchema {
2275 kinds: smallvec::smallvec![ArgKind::Logical],
2276 required: false,
2277 by_ref: false,
2278 shape: ShapeKind::Scalar,
2279 coercion: CoercionPolicy::Logical,
2280 max: None,
2281 repeating: None,
2282 default: Some(LiteralValue::Boolean(false)),
2283 },
2284 ArgSchema {
2285 kinds: smallvec::smallvec![ArgKind::Logical],
2286 required: false,
2287 by_ref: false,
2288 shape: ShapeKind::Scalar,
2289 coercion: CoercionPolicy::Logical,
2290 max: None,
2291 repeating: None,
2292 default: Some(LiteralValue::Boolean(false)),
2293 },
2294 ]
2295 });
2296 &SCHEMA
2297 }
2298 fn eval<'a, 'b, 'c>(
2299 &self,
2300 args: &'c [ArgumentHandle<'a, 'b>],
2301 _ctx: &dyn FunctionContext<'b>,
2302 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2303 let view = match args[0].range_view() {
2304 Ok(v) => v,
2305 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2306 };
2307 let (rows, cols) = view.dims();
2308 if rows == 0 || cols == 0 {
2309 return Ok(crate::traits::CalcValue::Range(
2310 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2311 ));
2312 }
2313
2314 let by_col = if args.len() >= 2 {
2315 matches!(args[1].value()?.into_literal(), LiteralValue::Boolean(true))
2316 } else {
2317 false
2318 };
2319 let exactly_once = if args.len() >= 3 {
2320 matches!(args[2].value()?.into_literal(), LiteralValue::Boolean(true))
2321 } else {
2322 false
2323 };
2324
2325 if by_col {
2326 #[derive(Hash, Eq, PartialEq, Clone)]
2327 struct ColKey(Vec<LiteralValue>);
2328
2329 let mut order: Vec<ColKey> = Vec::new();
2330 let mut counts: HashMap<ColKey, usize> = HashMap::new();
2331
2332 for c in 0..cols {
2333 let mut col_vals: Vec<LiteralValue> = Vec::with_capacity(rows);
2334 for r in 0..rows {
2335 col_vals.push(view.get_cell(r, c));
2336 }
2337 let key = ColKey(col_vals);
2338 if !counts.contains_key(&key) {
2339 order.push(key.clone());
2340 }
2341 *counts.entry(key).or_insert(0) += 1;
2342 }
2343
2344 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
2345 for k in order {
2346 if !exactly_once || counts.get(&k) == Some(&1) {
2347 out.push(k.0);
2348 }
2349 }
2350 return Ok(collapse_if_scalar(out, _ctx.date_system()));
2351 }
2352
2353 #[derive(Hash, Eq, PartialEq, Clone)]
2354 struct RowKey(Vec<LiteralValue>);
2355
2356 let mut order: Vec<RowKey> = Vec::new();
2357 let mut counts: HashMap<RowKey, usize> = HashMap::new();
2358 for r in 0..rows {
2359 let mut row_vals: Vec<LiteralValue> = Vec::with_capacity(cols);
2360 for c in 0..cols {
2361 row_vals.push(view.get_cell(r, c));
2362 }
2363 let key = RowKey(row_vals);
2364 if !counts.contains_key(&key) {
2365 order.push(key.clone());
2366 }
2367 *counts.entry(key).or_insert(0) += 1;
2368 }
2369
2370 let mut out: Vec<Vec<LiteralValue>> = Vec::new();
2371 for k in order {
2372 if !exactly_once || counts.get(&k) == Some(&1) {
2373 out.push(k.0);
2374 }
2375 }
2376 Ok(collapse_if_scalar(out, _ctx.date_system()))
2377 }
2378}
2379
2380#[derive(Debug)]
2383pub struct SequenceFn;
2384impl Function for SequenceFn {
2385 func_caps!(PURE);
2386 fn name(&self) -> &'static str {
2387 "SEQUENCE"
2388 }
2389 fn min_args(&self) -> usize {
2390 1
2391 }
2392 fn variadic(&self) -> bool {
2393 true
2394 }
2395 fn arg_schema(&self) -> &'static [ArgSchema] {
2396 use once_cell::sync::Lazy;
2397 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2398 vec![
2399 ArgSchema {
2401 kinds: smallvec::smallvec![ArgKind::Number],
2402 required: true,
2403 by_ref: false,
2404 shape: ShapeKind::Scalar,
2405 coercion: CoercionPolicy::NumberLenientText,
2406 max: None,
2407 repeating: None,
2408 default: None,
2409 },
2410 ArgSchema {
2412 kinds: smallvec::smallvec![ArgKind::Number],
2413 required: false,
2414 by_ref: false,
2415 shape: ShapeKind::Scalar,
2416 coercion: CoercionPolicy::NumberLenientText,
2417 max: None,
2418 repeating: None,
2419 default: Some(LiteralValue::Int(1)),
2420 },
2421 ArgSchema {
2423 kinds: smallvec::smallvec![ArgKind::Number],
2424 required: false,
2425 by_ref: false,
2426 shape: ShapeKind::Scalar,
2427 coercion: CoercionPolicy::NumberLenientText,
2428 max: None,
2429 repeating: None,
2430 default: Some(LiteralValue::Int(1)),
2431 },
2432 ArgSchema {
2434 kinds: smallvec::smallvec![ArgKind::Number],
2435 required: false,
2436 by_ref: false,
2437 shape: ShapeKind::Scalar,
2438 coercion: CoercionPolicy::NumberLenientText,
2439 max: None,
2440 repeating: None,
2441 default: Some(LiteralValue::Int(1)),
2442 },
2443 ]
2444 });
2445 &SCHEMA
2446 }
2447 fn eval<'a, 'b, 'c>(
2448 &self,
2449 args: &'c [ArgumentHandle<'a, 'b>],
2450 _ctx: &dyn FunctionContext<'b>,
2451 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2452 let num = |a: &ArgumentHandle| -> Result<f64, ExcelError> {
2454 Ok(match a.value()?.into_literal() {
2455 LiteralValue::Int(i) => i as f64,
2456 LiteralValue::Number(n) => n,
2457 _other => {
2458 return Err(ExcelError::new(ExcelErrorKind::Value));
2459 }
2460 })
2461 };
2462 let rows_f = num(&args[0])?;
2463 let rows = rows_f as i64;
2464 let cols = if args.len() >= 2 {
2465 num(&args[1])? as i64
2466 } else {
2467 1
2468 };
2469 let start = if args.len() >= 3 { num(&args[2])? } else { 1.0 };
2470 let step = if args.len() >= 4 { num(&args[3])? } else { 1.0 };
2471 if rows <= 0 || cols <= 0 {
2472 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2473 ExcelError::new(ExcelErrorKind::Value),
2474 )));
2475 }
2476 let total = rows.saturating_mul(cols);
2477 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows as usize);
2479 let mut current = start;
2480 for _r in 0..rows {
2481 let mut row_vec: Vec<LiteralValue> = Vec::with_capacity(cols as usize);
2482 for _c in 0..cols {
2483 if (current.fract().abs() < 1e-12) && current.abs() < (i64::MAX as f64) {
2485 row_vec.push(LiteralValue::Int(current as i64));
2486 } else {
2487 row_vec.push(LiteralValue::Number(current));
2488 }
2489 current += step;
2490 }
2491 out.push(row_vec);
2492 }
2493
2494 Ok(collapse_if_scalar(out, _ctx.date_system()))
2495 }
2496}
2497
2498#[derive(Debug)]
2501pub struct TransposeFn;
2502impl Function for TransposeFn {
2503 func_caps!(PURE);
2504 fn name(&self) -> &'static str {
2505 "TRANSPOSE"
2506 }
2507 fn min_args(&self) -> usize {
2508 1
2509 }
2510 fn variadic(&self) -> bool {
2511 false
2512 }
2513 fn arg_schema(&self) -> &'static [ArgSchema] {
2514 use once_cell::sync::Lazy;
2515 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2516 vec![ArgSchema {
2517 kinds: smallvec::smallvec![ArgKind::Range],
2518 required: true,
2519 by_ref: true,
2520 shape: ShapeKind::Range,
2521 coercion: CoercionPolicy::None,
2522 max: None,
2523 repeating: None,
2524 default: None,
2525 }]
2526 });
2527 &SCHEMA
2528 }
2529 fn eval<'a, 'b, 'c>(
2530 &self,
2531 args: &'c [ArgumentHandle<'a, 'b>],
2532 _ctx: &dyn FunctionContext<'b>,
2533 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2534 let view = match args[0].range_view() {
2535 Ok(v) => v,
2536 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2537 };
2538 let (rows, cols) = view.dims();
2539 if rows == 0 || cols == 0 {
2540 return Ok(crate::traits::CalcValue::Range(
2541 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2542 ));
2543 }
2544
2545 let mut out: Vec<Vec<LiteralValue>> = vec![Vec::with_capacity(rows); cols];
2546 for (c, col) in out.iter_mut().enumerate().take(cols) {
2547 for r in 0..rows {
2548 col.push(view.get_cell(r, c));
2549 }
2550 }
2551 Ok(collapse_if_scalar(out, _ctx.date_system()))
2552 }
2553}
2554
2555#[derive(Debug)]
2558pub struct TakeFn;
2559impl Function for TakeFn {
2560 func_caps!(PURE);
2561 fn name(&self) -> &'static str {
2562 "TAKE"
2563 }
2564 fn min_args(&self) -> usize {
2565 2
2566 }
2567 fn variadic(&self) -> bool {
2568 true
2569 }
2570 fn arg_schema(&self) -> &'static [ArgSchema] {
2571 use once_cell::sync::Lazy;
2572 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2573 vec![
2574 ArgSchema {
2575 kinds: smallvec::smallvec![ArgKind::Range],
2576 required: true,
2577 by_ref: true,
2578 shape: ShapeKind::Range,
2579 coercion: CoercionPolicy::None,
2580 max: None,
2581 repeating: None,
2582 default: None,
2583 },
2584 ArgSchema {
2585 kinds: smallvec::smallvec![ArgKind::Number],
2586 required: true,
2587 by_ref: false,
2588 shape: ShapeKind::Scalar,
2589 coercion: CoercionPolicy::NumberLenientText,
2590 max: None,
2591 repeating: None,
2592 default: None,
2593 },
2594 ArgSchema {
2595 kinds: smallvec::smallvec![ArgKind::Number],
2596 required: false,
2597 by_ref: false,
2598 shape: ShapeKind::Scalar,
2599 coercion: CoercionPolicy::NumberLenientText,
2600 max: None,
2601 repeating: None,
2602 default: None,
2603 },
2604 ]
2605 });
2606 &SCHEMA
2607 }
2608 fn eval<'a, 'b, 'c>(
2609 &self,
2610 args: &'c [ArgumentHandle<'a, 'b>],
2611 _ctx: &dyn FunctionContext<'b>,
2612 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2613 let view = match args[0].range_view() {
2614 Ok(v) => v,
2615 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2616 };
2617 let (rows, cols) = view.dims();
2618 if rows == 0 || cols == 0 {
2619 return Ok(crate::traits::CalcValue::Range(
2620 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2621 ));
2622 }
2623
2624 let height = rows as i64;
2625 let width = cols as i64;
2626
2627 let num = |a: &ArgumentHandle| -> Result<i64, ExcelError> {
2628 Ok(match a.value()?.into_literal() {
2629 LiteralValue::Int(i) => i,
2630 LiteralValue::Number(n) => n as i64,
2631 _ => 0,
2632 })
2633 };
2634 let take_rows = num(&args[1])?;
2635 let take_cols = if args.len() >= 3 {
2636 Some(num(&args[2])?)
2637 } else {
2638 None
2639 };
2640
2641 if take_rows.abs() > height {
2642 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2643 ExcelError::new(ExcelErrorKind::Value),
2644 )));
2645 }
2646
2647 let (row_start, row_end) = if take_rows >= 0 {
2648 (0usize, take_rows as usize)
2649 } else {
2650 ((height + take_rows) as usize, height as usize)
2651 };
2652
2653 let (col_start, col_end) = if let Some(tc) = take_cols {
2654 if tc.abs() > width {
2655 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2656 ExcelError::new(ExcelErrorKind::Value),
2657 )));
2658 }
2659 if tc >= 0 {
2660 (0usize, tc as usize)
2661 } else {
2662 ((width + tc) as usize, width as usize)
2663 }
2664 } else {
2665 (0usize, width as usize)
2666 };
2667
2668 if row_start >= row_end || col_start >= col_end {
2669 return Ok(crate::traits::CalcValue::Range(
2670 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2671 ));
2672 }
2673
2674 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(row_end - row_start);
2675 for r in row_start..row_end {
2676 let mut row_out: Vec<LiteralValue> = Vec::with_capacity(col_end - col_start);
2677 for c in col_start..col_end {
2678 row_out.push(view.get_cell(r, c));
2679 }
2680 out.push(row_out);
2681 }
2682
2683 Ok(collapse_if_scalar(out, _ctx.date_system()))
2684 }
2685}
2686
2687#[derive(Debug)]
2690pub struct DropFn;
2691impl Function for DropFn {
2692 func_caps!(PURE);
2693 fn name(&self) -> &'static str {
2694 "DROP"
2695 }
2696 fn min_args(&self) -> usize {
2697 2
2698 }
2699 fn variadic(&self) -> bool {
2700 true
2701 }
2702 fn arg_schema(&self) -> &'static [ArgSchema] {
2703 use once_cell::sync::Lazy;
2704 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
2705 vec![
2706 ArgSchema {
2707 kinds: smallvec::smallvec![ArgKind::Range],
2708 required: true,
2709 by_ref: true,
2710 shape: ShapeKind::Range,
2711 coercion: CoercionPolicy::None,
2712 max: None,
2713 repeating: None,
2714 default: None,
2715 },
2716 ArgSchema {
2717 kinds: smallvec::smallvec![ArgKind::Number],
2718 required: true,
2719 by_ref: false,
2720 shape: ShapeKind::Scalar,
2721 coercion: CoercionPolicy::NumberLenientText,
2722 max: None,
2723 repeating: None,
2724 default: None,
2725 },
2726 ArgSchema {
2727 kinds: smallvec::smallvec![ArgKind::Number],
2728 required: false,
2729 by_ref: false,
2730 shape: ShapeKind::Scalar,
2731 coercion: CoercionPolicy::NumberLenientText,
2732 max: None,
2733 repeating: None,
2734 default: None,
2735 },
2736 ]
2737 });
2738 &SCHEMA
2739 }
2740 fn eval<'a, 'b, 'c>(
2741 &self,
2742 args: &'c [ArgumentHandle<'a, 'b>],
2743 _ctx: &dyn FunctionContext<'b>,
2744 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2745 let view = match args[0].range_view() {
2746 Ok(v) => v,
2747 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2748 };
2749 let (rows, cols) = view.dims();
2750 if rows == 0 || cols == 0 {
2751 return Ok(crate::traits::CalcValue::Range(
2752 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2753 ));
2754 }
2755
2756 let height = rows as i64;
2757 let width = cols as i64;
2758
2759 let num = |a: &ArgumentHandle| -> Result<i64, ExcelError> {
2760 Ok(match a.value()?.into_literal() {
2761 LiteralValue::Int(i) => i,
2762 LiteralValue::Number(n) => n as i64,
2763 _ => 0,
2764 })
2765 };
2766 let drop_rows = num(&args[1])?;
2767 let drop_cols = if args.len() >= 3 {
2768 Some(num(&args[2])?)
2769 } else {
2770 None
2771 };
2772
2773 let (row_start, row_end) = if drop_rows >= 0 {
2774 ((drop_rows as usize).min(height as usize), height as usize)
2775 } else {
2776 (0usize, (height + drop_rows).max(0) as usize)
2777 };
2778
2779 let (col_start, col_end) = if let Some(dc) = drop_cols {
2780 if dc >= 0 {
2781 ((dc as usize).min(width as usize), width as usize)
2782 } else {
2783 (0usize, (width + dc).max(0) as usize)
2784 }
2785 } else {
2786 (0usize, width as usize)
2787 };
2788
2789 if row_start >= row_end || col_start >= col_end {
2790 return Ok(crate::traits::CalcValue::Range(
2791 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
2792 ));
2793 }
2794
2795 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(row_end - row_start);
2796 for r in row_start..row_end {
2797 let mut row_out: Vec<LiteralValue> = Vec::with_capacity(col_end - col_start);
2798 for c in col_start..col_end {
2799 row_out.push(view.get_cell(r, c));
2800 }
2801 out.push(row_out);
2802 }
2803
2804 Ok(collapse_if_scalar(out, _ctx.date_system()))
2805 }
2806}
2807
2808pub fn register_builtins() {
2809 use crate::function_registry::register_function;
2810 use std::sync::Arc;
2811 register_function(Arc::new(XLookupFn));
2812 register_function(Arc::new(FilterFn));
2813 register_function(Arc::new(UniqueFn));
2814 register_function(Arc::new(SequenceFn));
2815 register_function(Arc::new(TransposeFn));
2816 register_function(Arc::new(TakeFn));
2817 register_function(Arc::new(DropFn));
2818 register_function(Arc::new(XMatchFn));
2819 register_function(Arc::new(SortFn));
2820 register_function(Arc::new(SortByFn));
2821 register_function(Arc::new(RandArrayFn));
2822 register_function(Arc::new(GroupByFn));
2823 register_function(Arc::new(PivotByFn));
2824}
2825
2826#[cfg(test)]
2829mod tests {
2830 use super::*;
2831 use crate::test_workbook::TestWorkbook;
2832 use crate::traits::ArgumentHandle;
2833 use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
2834 use std::sync::Arc;
2835
2836 #[test]
2837 fn test_all_dynamic_functions_registered() {
2838 crate::builtins::load_builtins();
2840
2841 let functions = [
2842 "XLOOKUP",
2843 "FILTER",
2844 "UNIQUE",
2845 "SEQUENCE",
2846 "TRANSPOSE",
2847 "TAKE",
2848 "DROP",
2849 "XMATCH",
2850 "SORT",
2851 "SORTBY",
2852 "RANDARRAY",
2853 "GROUPBY",
2854 "PIVOTBY",
2855 ];
2856
2857 for name in &functions {
2858 let result = crate::function_registry::get("", name);
2859 assert!(result.is_some(), "Function {} should be registered", name);
2860 }
2861 }
2862
2863 fn lit(v: LiteralValue) -> ASTNode {
2864 ASTNode::new(ASTNodeType::Literal(v), None)
2865 }
2866
2867 fn range(r: &str, sr: u32, sc: u32, er: u32, ec: u32) -> ASTNode {
2868 ASTNode::new(
2869 ASTNodeType::Reference {
2870 original: r.into(),
2871 reference: ReferenceType::range(None, Some(sr), Some(sc), Some(er), Some(ec)),
2872 },
2873 None,
2874 )
2875 }
2876
2877 #[test]
2878 fn xlookup_basic_exact_and_if_not_found() {
2879 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
2880 let wb = wb
2881 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("a".into()))
2882 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("b".into()))
2883 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(10))
2884 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(20));
2885 let ctx = wb.interpreter();
2886 let lookup_range = range("A1:A2", 1, 1, 2, 1);
2887 let return_range = range("B1:B2", 1, 2, 2, 2);
2888 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
2889 let key_b = lit(LiteralValue::Text("b".into()));
2890 let args = vec![
2891 ArgumentHandle::new(&key_b, &ctx),
2892 ArgumentHandle::new(&lookup_range, &ctx),
2893 ArgumentHandle::new(&return_range, &ctx),
2894 ];
2895 let v = f
2896 .dispatch(&args, &ctx.function_context(None))
2897 .unwrap()
2898 .into_literal();
2899 assert_eq!(v, LiteralValue::Number(20.0));
2900 let key_missing = lit(LiteralValue::Text("z".into()));
2901 let if_nf = lit(LiteralValue::Text("NF".into()));
2902 let args_nf = vec![
2903 ArgumentHandle::new(&key_missing, &ctx),
2904 ArgumentHandle::new(&lookup_range, &ctx),
2905 ArgumentHandle::new(&return_range, &ctx),
2906 ArgumentHandle::new(&if_nf, &ctx),
2907 ];
2908 let v_nf = f
2909 .dispatch(&args_nf, &ctx.function_context(None))
2910 .unwrap()
2911 .into_literal();
2912 assert_eq!(v_nf, LiteralValue::Text("NF".into()));
2913 }
2914
2915 #[test]
2916 fn xlookup_match_modes_next_smaller_larger() {
2917 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
2918 let wb = wb
2919 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(10))
2920 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(20))
2921 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(30))
2922 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(1))
2923 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(2))
2924 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(3));
2925 let ctx = wb.interpreter();
2926 let lookup_range = range("A1:A3", 1, 1, 3, 1);
2927 let return_range = range("B1:B3", 1, 2, 3, 2);
2928 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
2929 let needle_25 = lit(LiteralValue::Int(25));
2930 let mm_next_smaller = lit(LiteralValue::Int(-1));
2931 let nf_text = lit(LiteralValue::Text("NF".into()));
2932 let args_smaller = vec![
2933 ArgumentHandle::new(&needle_25, &ctx),
2934 ArgumentHandle::new(&lookup_range, &ctx),
2935 ArgumentHandle::new(&return_range, &ctx),
2936 ArgumentHandle::new(&nf_text, &ctx),
2937 ArgumentHandle::new(&mm_next_smaller, &ctx),
2938 ];
2939 let v_smaller = f
2940 .dispatch(&args_smaller, &ctx.function_context(None))
2941 .unwrap()
2942 .into_literal();
2943 assert_eq!(v_smaller, LiteralValue::Number(2.0));
2944 let mm_next_larger = lit(LiteralValue::Int(1));
2945 let nf_text2 = lit(LiteralValue::Text("NF".into()));
2946 let args_larger = vec![
2947 ArgumentHandle::new(&needle_25, &ctx),
2948 ArgumentHandle::new(&lookup_range, &ctx),
2949 ArgumentHandle::new(&return_range, &ctx),
2950 ArgumentHandle::new(&nf_text2, &ctx),
2951 ArgumentHandle::new(&mm_next_larger, &ctx),
2952 ];
2953 let v_larger = f
2954 .dispatch(&args_larger, &ctx.function_context(None))
2955 .unwrap()
2956 .into_literal();
2957 assert_eq!(v_larger, LiteralValue::Number(3.0));
2958 }
2959
2960 #[test]
2961 fn xlookup_wildcard_and_not_found_default_na() {
2962 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
2963 let wb = wb
2964 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("Alpha".into()))
2965 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("Beta".into()))
2966 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("Gamma".into()))
2967 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(100))
2968 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(200))
2969 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(300));
2970 let ctx = wb.interpreter();
2971 let lookup_range = range("A1:A3", 1, 1, 3, 1);
2972 let return_range = range("B1:B3", 1, 2, 3, 2);
2973 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
2974 let pattern = lit(LiteralValue::Text("*et*".into()));
2976 let match_mode_wild = lit(LiteralValue::Int(2));
2977 let nf_binding = lit(LiteralValue::Text("NF".into()));
2978 let args_wild = vec![
2979 ArgumentHandle::new(&pattern, &ctx),
2980 ArgumentHandle::new(&lookup_range, &ctx),
2981 ArgumentHandle::new(&return_range, &ctx),
2982 ArgumentHandle::new(&nf_binding, &ctx),
2983 ArgumentHandle::new(&match_mode_wild, &ctx),
2984 ];
2985 let v_wild = f
2986 .dispatch(&args_wild, &ctx.function_context(None))
2987 .unwrap()
2988 .into_literal();
2989 assert_eq!(v_wild, LiteralValue::Number(200.0));
2990 let pattern_lit_star = lit(LiteralValue::Text("~*eta".into()));
2992 let args_lit = vec![
2993 ArgumentHandle::new(&pattern_lit_star, &ctx),
2994 ArgumentHandle::new(&lookup_range, &ctx),
2995 ArgumentHandle::new(&return_range, &ctx),
2996 ArgumentHandle::new(&nf_binding, &ctx),
2997 ArgumentHandle::new(&match_mode_wild, &ctx),
2998 ];
2999 let v_lit = f
3000 .dispatch(&args_lit, &ctx.function_context(None))
3001 .unwrap()
3002 .into_literal();
3003 match v_lit {
3004 LiteralValue::Text(s) => assert_eq!(s, "NF"),
3005 other => panic!("expected NF text got {other:?}"),
3006 }
3007 let missing = lit(LiteralValue::Text("Zeta".into()));
3009 let args_nf = vec![
3010 ArgumentHandle::new(&missing, &ctx),
3011 ArgumentHandle::new(&lookup_range, &ctx),
3012 ArgumentHandle::new(&return_range, &ctx),
3013 ];
3014 let v_nf = f
3015 .dispatch(&args_nf, &ctx.function_context(None))
3016 .unwrap()
3017 .into_literal();
3018 match v_nf {
3019 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Na),
3020 other => panic!("expected #N/A got {other:?}"),
3021 }
3022 }
3023
3024 #[test]
3025 fn xlookup_reverse_search_mode_picks_last() {
3026 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
3027 let wb = wb
3028 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3029 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2))
3030 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(1))
3031 .with_cell_a1("Sheet1", "B1", LiteralValue::Text("First".into()))
3032 .with_cell_a1("Sheet1", "B2", LiteralValue::Text("Mid".into()))
3033 .with_cell_a1("Sheet1", "B3", LiteralValue::Text("Last".into()));
3034 let ctx = wb.interpreter();
3035 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3036 let return_range = range("B1:B3", 1, 2, 3, 2);
3037 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
3038 let needle_one = lit(LiteralValue::Int(1));
3039 let search_rev = lit(LiteralValue::Int(-1));
3040 let nf_binding2 = lit(LiteralValue::Text("NF".into()));
3041 let match_mode_zero = lit(LiteralValue::Int(0));
3042 let args_rev = vec![
3043 ArgumentHandle::new(&needle_one, &ctx),
3044 ArgumentHandle::new(&lookup_range, &ctx),
3045 ArgumentHandle::new(&return_range, &ctx),
3046 ArgumentHandle::new(&nf_binding2, &ctx),
3047 ArgumentHandle::new(&match_mode_zero, &ctx),
3048 ArgumentHandle::new(&search_rev, &ctx),
3049 ];
3050 let v_rev = f
3051 .dispatch(&args_rev, &ctx.function_context(None))
3052 .unwrap()
3053 .into_literal();
3054 assert_eq!(v_rev, LiteralValue::Text("Last".into()));
3055 }
3056
3057 #[test]
3058 fn xlookup_horizontal_returns_column_vector_for_matrix_return() {
3059 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
3060 let wb = wb
3061 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(10))
3062 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(20))
3063 .with_cell_a1("Sheet1", "C1", LiteralValue::Int(30))
3064 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(1))
3065 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(2))
3066 .with_cell_a1("Sheet1", "C2", LiteralValue::Int(3))
3067 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(4))
3068 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(5))
3069 .with_cell_a1("Sheet1", "C3", LiteralValue::Int(6));
3070 let ctx = wb.interpreter();
3071 let lookup_range = range("A1:C1", 1, 1, 1, 3);
3072 let return_range = range("A2:C3", 2, 1, 3, 3);
3073 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
3074 let needle = lit(LiteralValue::Int(20));
3075 let args = vec![
3076 ArgumentHandle::new(&needle, &ctx),
3077 ArgumentHandle::new(&lookup_range, &ctx),
3078 ArgumentHandle::new(&return_range, &ctx),
3079 ];
3080 let v = f
3081 .dispatch(&args, &ctx.function_context(None))
3082 .unwrap()
3083 .into_literal();
3084 match v {
3085 LiteralValue::Array(a) => {
3086 assert_eq!(
3087 a,
3088 vec![
3089 vec![LiteralValue::Number(2.0)],
3090 vec![LiteralValue::Number(5.0)]
3091 ]
3092 );
3093 }
3094 other => panic!("expected array got {other:?}"),
3095 }
3096 }
3097
3098 #[test]
3099 fn xlookup_vertical_returns_row_vector_for_matrix_return() {
3100 let wb = TestWorkbook::new().with_function(Arc::new(XLookupFn));
3101 let wb = wb
3102 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(10))
3103 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(20))
3104 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(30))
3105 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(101))
3106 .with_cell_a1("Sheet1", "C1", LiteralValue::Int(102))
3107 .with_cell_a1("Sheet1", "D1", LiteralValue::Int(103))
3108 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(201))
3109 .with_cell_a1("Sheet1", "C2", LiteralValue::Int(202))
3110 .with_cell_a1("Sheet1", "D2", LiteralValue::Int(203))
3111 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(301))
3112 .with_cell_a1("Sheet1", "C3", LiteralValue::Int(302))
3113 .with_cell_a1("Sheet1", "D3", LiteralValue::Int(303));
3114 let ctx = wb.interpreter();
3115 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3116 let return_range = range("B1:D3", 1, 2, 3, 4);
3117 let f = ctx.context.get_function("", "XLOOKUP").unwrap();
3118 let needle = lit(LiteralValue::Int(20));
3119 let args = vec![
3120 ArgumentHandle::new(&needle, &ctx),
3121 ArgumentHandle::new(&lookup_range, &ctx),
3122 ArgumentHandle::new(&return_range, &ctx),
3123 ];
3124 let v = f
3125 .dispatch(&args, &ctx.function_context(None))
3126 .unwrap()
3127 .into_literal();
3128 match v {
3129 LiteralValue::Array(a) => {
3130 assert_eq!(
3131 a,
3132 vec![vec![
3133 LiteralValue::Number(201.0),
3134 LiteralValue::Number(202.0),
3135 LiteralValue::Number(203.0)
3136 ]]
3137 );
3138 }
3139 other => panic!("expected array got {other:?}"),
3140 }
3141 }
3142
3143 #[test]
3144 fn filter_basic_and_if_empty() {
3145 let wb = TestWorkbook::new().with_function(Arc::new(FilterFn));
3146 let wb = wb
3147 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3148 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2))
3149 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(10))
3150 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(20))
3151 .with_cell_a1("Sheet1", "C1", LiteralValue::Boolean(true))
3152 .with_cell_a1("Sheet1", "C2", LiteralValue::Boolean(false));
3153 let ctx = wb.interpreter();
3154 let array_range = range("A1:B2", 1, 1, 2, 2);
3155 let include_range = range("C1:C2", 1, 3, 2, 3);
3156 let f = ctx.context.get_function("", "FILTER").unwrap();
3157 let args = vec![
3158 ArgumentHandle::new(&array_range, &ctx),
3159 ArgumentHandle::new(&include_range, &ctx),
3160 ];
3161 let v = f
3162 .dispatch(&args, &ctx.function_context(None))
3163 .unwrap()
3164 .into_literal();
3165 match v {
3166 LiteralValue::Array(a) => {
3167 assert_eq!(a.len(), 1);
3168 assert_eq!(
3169 a[0],
3170 vec![LiteralValue::Number(1.0), LiteralValue::Number(10.0)]
3171 );
3172 }
3173 other => panic!("expected array got {other:?}"),
3174 }
3175 let wb2 = wb
3176 .with_cell_a1("Sheet1", "C1", LiteralValue::Boolean(false))
3177 .with_cell_a1("Sheet1", "C2", LiteralValue::Boolean(false));
3178 let ctx2 = wb2.interpreter();
3179 let f2 = ctx2.context.get_function("", "FILTER").unwrap();
3180 let empty_text = lit(LiteralValue::Text("EMPTY".into()));
3181 let args_empty = vec![
3182 ArgumentHandle::new(&array_range, &ctx2),
3183 ArgumentHandle::new(&include_range, &ctx2),
3184 ArgumentHandle::new(&empty_text, &ctx2),
3185 ];
3186 let v_empty = f2
3187 .dispatch(&args_empty, &ctx2.function_context(None))
3188 .unwrap()
3189 .into_literal();
3190 assert_eq!(v_empty, LiteralValue::Text("EMPTY".into()));
3191 }
3192
3193 #[test]
3194 fn unique_basic_and_exactly_once() {
3195 let wb = TestWorkbook::new().with_function(Arc::new(UniqueFn));
3196 let wb = wb
3197 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3198 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(1))
3199 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(2))
3200 .with_cell_a1("Sheet1", "A4", LiteralValue::Int(3));
3201 let ctx = wb.interpreter();
3202 let range = range("A1:A4", 1, 1, 4, 1);
3203 let f = ctx.context.get_function("", "UNIQUE").unwrap();
3204 let args = vec![ArgumentHandle::new(&range, &ctx)];
3205 let v = f
3206 .dispatch(&args, &ctx.function_context(None))
3207 .unwrap()
3208 .into_literal();
3209 match v {
3210 LiteralValue::Array(a) => {
3211 assert_eq!(a.len(), 3);
3212 assert_eq!(a[0][0], LiteralValue::Number(1.0));
3213 }
3214 _ => panic!("expected array"),
3215 }
3216 }
3217
3218 #[test]
3219 fn sequence_basic_rows_cols_step() {
3220 let wb = TestWorkbook::new().with_function(Arc::new(SequenceFn));
3221 let ctx = wb.interpreter();
3222 let f = ctx.context.get_function("", "SEQUENCE").unwrap();
3223 let rows = lit(LiteralValue::Int(2));
3224 let cols = lit(LiteralValue::Int(3));
3225 let start = lit(LiteralValue::Int(5));
3226 let step = lit(LiteralValue::Int(2));
3227 let args = vec![
3228 ArgumentHandle::new(&rows, &ctx),
3229 ArgumentHandle::new(&cols, &ctx),
3230 ArgumentHandle::new(&start, &ctx),
3231 ArgumentHandle::new(&step, &ctx),
3232 ];
3233 let v = f
3234 .dispatch(&args, &ctx.function_context(None))
3235 .unwrap()
3236 .into_literal();
3237 match v {
3238 LiteralValue::Array(a) => {
3239 assert_eq!(a.len(), 2);
3240 assert_eq!(a[0][0], LiteralValue::Number(5.0));
3241 }
3242 other => panic!("expected array got {other:?}"),
3243 }
3244 }
3245
3246 #[test]
3247 fn transpose_basic() {
3248 let wb = TestWorkbook::new().with_function(Arc::new(TransposeFn));
3249 let wb = wb
3250 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3251 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2))
3252 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(10))
3253 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(20));
3254 let ctx = wb.interpreter();
3255 let arr = range("A1:B2", 1, 1, 2, 2);
3256 let f = ctx.context.get_function("", "TRANSPOSE").unwrap();
3257 let args = vec![ArgumentHandle::new(&arr, &ctx)];
3258 let v = f
3259 .dispatch(&args, &ctx.function_context(None))
3260 .unwrap()
3261 .into_literal();
3262 match v {
3263 LiteralValue::Array(a) => {
3264 assert_eq!(a.len(), 2);
3265 assert_eq!(
3266 a[0],
3267 vec![LiteralValue::Number(1.0), LiteralValue::Number(2.0)]
3268 );
3269 }
3270 other => panic!("expected array got {other:?}"),
3271 }
3272 }
3273
3274 #[test]
3275 fn take_basic() {
3276 let wb = TestWorkbook::new().with_function(Arc::new(TakeFn));
3277 let wb = wb
3278 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3279 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2));
3280 let ctx = wb.interpreter();
3281 let arr = range("A1:A2", 1, 1, 2, 1);
3282 let f = ctx.context.get_function("", "TAKE").unwrap();
3283 let one = lit(LiteralValue::Int(1));
3284 let args = vec![
3285 ArgumentHandle::new(&arr, &ctx),
3286 ArgumentHandle::new(&one, &ctx),
3287 ];
3288 let v = f
3289 .dispatch(&args, &ctx.function_context(None))
3290 .unwrap()
3291 .into_literal();
3292 assert_eq!(v, LiteralValue::Number(1.0));
3293 }
3294
3295 #[test]
3296 fn drop_basic() {
3297 let wb = TestWorkbook::new().with_function(Arc::new(DropFn));
3298 let wb = wb
3299 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3300 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2));
3301 let ctx = wb.interpreter();
3302 let arr = range("A1:A2", 1, 1, 2, 1);
3303 let f = ctx.context.get_function("", "DROP").unwrap();
3304 let one = lit(LiteralValue::Int(1));
3305 let args = vec![
3306 ArgumentHandle::new(&arr, &ctx),
3307 ArgumentHandle::new(&one, &ctx),
3308 ];
3309 let v = f
3310 .dispatch(&args, &ctx.function_context(None))
3311 .unwrap()
3312 .into_literal();
3313 assert_eq!(v, LiteralValue::Number(2.0));
3314 }
3315
3316 #[test]
3317 fn xmatch_exact_match_default() {
3318 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3319 let wb = wb
3320 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("apple".into()))
3321 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("banana".into()))
3322 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("cherry".into()));
3323 let ctx = wb.interpreter();
3324 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3325 let f = ctx.context.get_function("", "XMATCH").unwrap();
3326 let key = lit(LiteralValue::Text("banana".into()));
3327 let args = vec![
3328 ArgumentHandle::new(&key, &ctx),
3329 ArgumentHandle::new(&lookup_range, &ctx),
3330 ];
3331 let v = f
3332 .dispatch(&args, &ctx.function_context(None))
3333 .unwrap()
3334 .into_literal();
3335 assert_eq!(v, LiteralValue::Int(2));
3336 }
3337
3338 #[test]
3339 fn xmatch_exact_or_next_smaller() {
3340 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3341 let wb = wb
3342 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(10))
3343 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(20))
3344 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(30));
3345 let ctx = wb.interpreter();
3346 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3347 let f = ctx.context.get_function("", "XMATCH").unwrap();
3348 let needle = lit(LiteralValue::Int(25));
3349 let match_mode = lit(LiteralValue::Int(-1)); let args = vec![
3351 ArgumentHandle::new(&needle, &ctx),
3352 ArgumentHandle::new(&lookup_range, &ctx),
3353 ArgumentHandle::new(&match_mode, &ctx),
3354 ];
3355 let v = f
3356 .dispatch(&args, &ctx.function_context(None))
3357 .unwrap()
3358 .into_literal();
3359 assert_eq!(v, LiteralValue::Int(2)); }
3361
3362 #[test]
3363 fn xmatch_exact_or_next_larger() {
3364 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3365 let wb = wb
3366 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(10))
3367 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(20))
3368 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(30));
3369 let ctx = wb.interpreter();
3370 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3371 let f = ctx.context.get_function("", "XMATCH").unwrap();
3372 let needle = lit(LiteralValue::Int(25));
3373 let match_mode = lit(LiteralValue::Int(1)); let args = vec![
3375 ArgumentHandle::new(&needle, &ctx),
3376 ArgumentHandle::new(&lookup_range, &ctx),
3377 ArgumentHandle::new(&match_mode, &ctx),
3378 ];
3379 let v = f
3380 .dispatch(&args, &ctx.function_context(None))
3381 .unwrap()
3382 .into_literal();
3383 assert_eq!(v, LiteralValue::Int(3)); }
3385
3386 #[test]
3387 fn xmatch_wildcard() {
3388 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3389 let wb = wb
3390 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("alpha".into()))
3391 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("beta".into()))
3392 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("gamma".into()));
3393 let ctx = wb.interpreter();
3394 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3395 let f = ctx.context.get_function("", "XMATCH").unwrap();
3396 let pattern = lit(LiteralValue::Text("*eta".into()));
3397 let match_mode = lit(LiteralValue::Int(2)); let args = vec![
3399 ArgumentHandle::new(&pattern, &ctx),
3400 ArgumentHandle::new(&lookup_range, &ctx),
3401 ArgumentHandle::new(&match_mode, &ctx),
3402 ];
3403 let v = f
3404 .dispatch(&args, &ctx.function_context(None))
3405 .unwrap()
3406 .into_literal();
3407 assert_eq!(v, LiteralValue::Int(2)); }
3409
3410 #[test]
3411 fn xmatch_reverse_search() {
3412 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3413 let wb = wb
3414 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3415 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2))
3416 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(1)); let ctx = wb.interpreter();
3418 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3419 let f = ctx.context.get_function("", "XMATCH").unwrap();
3420 let needle = lit(LiteralValue::Int(1));
3421 let match_mode = lit(LiteralValue::Int(0));
3422 let search_mode = lit(LiteralValue::Int(-1)); let args = vec![
3424 ArgumentHandle::new(&needle, &ctx),
3425 ArgumentHandle::new(&lookup_range, &ctx),
3426 ArgumentHandle::new(&match_mode, &ctx),
3427 ArgumentHandle::new(&search_mode, &ctx),
3428 ];
3429 let v = f
3430 .dispatch(&args, &ctx.function_context(None))
3431 .unwrap()
3432 .into_literal();
3433 assert_eq!(v, LiteralValue::Int(3)); }
3435
3436 #[test]
3437 fn xmatch_not_found() {
3438 let wb = TestWorkbook::new().with_function(Arc::new(XMatchFn));
3439 let wb = wb
3440 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
3441 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(2))
3442 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(3));
3443 let ctx = wb.interpreter();
3444 let lookup_range = range("A1:A3", 1, 1, 3, 1);
3445 let f = ctx.context.get_function("", "XMATCH").unwrap();
3446 let needle = lit(LiteralValue::Int(5));
3447 let args = vec![
3448 ArgumentHandle::new(&needle, &ctx),
3449 ArgumentHandle::new(&lookup_range, &ctx),
3450 ];
3451 let v = f
3452 .dispatch(&args, &ctx.function_context(None))
3453 .unwrap()
3454 .into_literal();
3455 match v {
3456 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Na),
3457 other => panic!("expected #N/A got {other:?}"),
3458 }
3459 }
3460
3461 #[test]
3462 fn sort_basic_ascending() {
3463 let wb = TestWorkbook::new().with_function(Arc::new(SortFn));
3464 let wb = wb
3465 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(30))
3466 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(10))
3467 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(20));
3468 let ctx = wb.interpreter();
3469 let arr = range("A1:A3", 1, 1, 3, 1);
3470 let f = ctx.context.get_function("", "SORT").unwrap();
3471 let args = vec![ArgumentHandle::new(&arr, &ctx)];
3472 let v = f
3473 .dispatch(&args, &ctx.function_context(None))
3474 .unwrap()
3475 .into_literal();
3476 match v {
3477 LiteralValue::Array(a) => {
3478 assert_eq!(a.len(), 3);
3479 assert_eq!(a[0][0], LiteralValue::Number(10.0));
3480 assert_eq!(a[1][0], LiteralValue::Number(20.0));
3481 assert_eq!(a[2][0], LiteralValue::Number(30.0));
3482 }
3483 other => panic!("expected array got {other:?}"),
3484 }
3485 }
3486
3487 #[test]
3488 fn sort_descending() {
3489 let wb = TestWorkbook::new().with_function(Arc::new(SortFn));
3490 let wb = wb
3491 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(30))
3492 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(10))
3493 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(20));
3494 let ctx = wb.interpreter();
3495 let arr = range("A1:A3", 1, 1, 3, 1);
3496 let f = ctx.context.get_function("", "SORT").unwrap();
3497 let sort_index = lit(LiteralValue::Int(1));
3498 let sort_order = lit(LiteralValue::Int(-1)); let args = vec![
3500 ArgumentHandle::new(&arr, &ctx),
3501 ArgumentHandle::new(&sort_index, &ctx),
3502 ArgumentHandle::new(&sort_order, &ctx),
3503 ];
3504 let v = f
3505 .dispatch(&args, &ctx.function_context(None))
3506 .unwrap()
3507 .into_literal();
3508 match v {
3509 LiteralValue::Array(a) => {
3510 assert_eq!(a.len(), 3);
3511 assert_eq!(a[0][0], LiteralValue::Number(30.0));
3512 assert_eq!(a[1][0], LiteralValue::Number(20.0));
3513 assert_eq!(a[2][0], LiteralValue::Number(10.0));
3514 }
3515 other => panic!("expected array got {other:?}"),
3516 }
3517 }
3518
3519 #[test]
3520 fn sort_by_column() {
3521 let wb = TestWorkbook::new().with_function(Arc::new(SortFn));
3522 let wb = wb
3523 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("Charlie".into()))
3524 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(30))
3525 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("Alice".into()))
3526 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(10))
3527 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("Bob".into()))
3528 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(20));
3529 let ctx = wb.interpreter();
3530 let arr = range("A1:B3", 1, 1, 3, 2);
3531 let f = ctx.context.get_function("", "SORT").unwrap();
3532 let sort_index = lit(LiteralValue::Int(2)); let args = vec![
3534 ArgumentHandle::new(&arr, &ctx),
3535 ArgumentHandle::new(&sort_index, &ctx),
3536 ];
3537 let v = f
3538 .dispatch(&args, &ctx.function_context(None))
3539 .unwrap()
3540 .into_literal();
3541 match v {
3542 LiteralValue::Array(a) => {
3543 assert_eq!(a.len(), 3);
3544 assert_eq!(a[0][0], LiteralValue::Text("Alice".into()));
3546 assert_eq!(a[1][0], LiteralValue::Text("Bob".into()));
3547 assert_eq!(a[2][0], LiteralValue::Text("Charlie".into()));
3548 }
3549 other => panic!("expected array got {other:?}"),
3550 }
3551 }
3552
3553 #[test]
3554 fn sortby_basic() {
3555 let wb = TestWorkbook::new().with_function(Arc::new(SortByFn));
3556 let wb = wb
3557 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("Charlie".into()))
3558 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("Alice".into()))
3559 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("Bob".into()))
3560 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(3))
3561 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(1))
3562 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(2));
3563 let ctx = wb.interpreter();
3564 let arr = range("A1:A3", 1, 1, 3, 1);
3565 let by_arr = range("B1:B3", 1, 2, 3, 2);
3566 let f = ctx.context.get_function("", "SORTBY").unwrap();
3567 let args = vec![
3568 ArgumentHandle::new(&arr, &ctx),
3569 ArgumentHandle::new(&by_arr, &ctx),
3570 ];
3571 let v = f
3572 .dispatch(&args, &ctx.function_context(None))
3573 .unwrap()
3574 .into_literal();
3575 match v {
3576 LiteralValue::Array(a) => {
3577 assert_eq!(a.len(), 3);
3578 assert_eq!(a[0][0], LiteralValue::Text("Alice".into()));
3580 assert_eq!(a[1][0], LiteralValue::Text("Bob".into()));
3581 assert_eq!(a[2][0], LiteralValue::Text("Charlie".into()));
3582 }
3583 other => panic!("expected array got {other:?}"),
3584 }
3585 }
3586
3587 #[test]
3588 fn sortby_descending() {
3589 let wb = TestWorkbook::new().with_function(Arc::new(SortByFn));
3590 let wb = wb
3591 .with_cell_a1("Sheet1", "A1", LiteralValue::Text("Charlie".into()))
3592 .with_cell_a1("Sheet1", "A2", LiteralValue::Text("Alice".into()))
3593 .with_cell_a1("Sheet1", "A3", LiteralValue::Text("Bob".into()))
3594 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(3))
3595 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(1))
3596 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(2));
3597 let ctx = wb.interpreter();
3598 let arr = range("A1:A3", 1, 1, 3, 1);
3599 let by_arr = range("B1:B3", 1, 2, 3, 2);
3600 let sort_order = lit(LiteralValue::Int(-1)); let f = ctx.context.get_function("", "SORTBY").unwrap();
3602 let args = vec![
3603 ArgumentHandle::new(&arr, &ctx),
3604 ArgumentHandle::new(&by_arr, &ctx),
3605 ArgumentHandle::new(&sort_order, &ctx),
3606 ];
3607 let v = f
3608 .dispatch(&args, &ctx.function_context(None))
3609 .unwrap()
3610 .into_literal();
3611 match v {
3612 LiteralValue::Array(a) => {
3613 assert_eq!(a.len(), 3);
3614 assert_eq!(a[0][0], LiteralValue::Text("Charlie".into()));
3616 assert_eq!(a[1][0], LiteralValue::Text("Bob".into()));
3617 assert_eq!(a[2][0], LiteralValue::Text("Alice".into()));
3618 }
3619 other => panic!("expected array got {other:?}"),
3620 }
3621 }
3622
3623 #[test]
3624 fn randarray_basic() {
3625 let wb = TestWorkbook::new().with_function(Arc::new(RandArrayFn));
3626 let ctx = wb.interpreter();
3627 let f = ctx.context.get_function("", "RANDARRAY").unwrap();
3628
3629 let rows = lit(LiteralValue::Int(2));
3631 let cols = lit(LiteralValue::Int(3));
3632 let args = vec![
3633 ArgumentHandle::new(&rows, &ctx),
3634 ArgumentHandle::new(&cols, &ctx),
3635 ];
3636 let v = f
3637 .dispatch(&args, &ctx.function_context(None))
3638 .unwrap()
3639 .into_literal();
3640 match v {
3641 LiteralValue::Array(a) => {
3642 assert_eq!(a.len(), 2);
3643 assert_eq!(a[0].len(), 3);
3644 for row in &a {
3646 for cell in row {
3647 match cell {
3648 LiteralValue::Number(n) => {
3649 assert!(*n >= 0.0 && *n < 1.0, "Value {n} not in [0, 1)");
3650 }
3651 other => panic!("expected Number got {other:?}"),
3652 }
3653 }
3654 }
3655 }
3656 other => panic!("expected array got {other:?}"),
3657 }
3658 }
3659
3660 #[test]
3661 fn randarray_whole_numbers() {
3662 let wb = TestWorkbook::new().with_function(Arc::new(RandArrayFn));
3663 let ctx = wb.interpreter();
3664 let f = ctx.context.get_function("", "RANDARRAY").unwrap();
3665
3666 let rows = lit(LiteralValue::Int(3));
3668 let cols = lit(LiteralValue::Int(2));
3669 let min = lit(LiteralValue::Int(1));
3670 let max = lit(LiteralValue::Int(10));
3671 let whole = lit(LiteralValue::Boolean(true));
3672 let args = vec![
3673 ArgumentHandle::new(&rows, &ctx),
3674 ArgumentHandle::new(&cols, &ctx),
3675 ArgumentHandle::new(&min, &ctx),
3676 ArgumentHandle::new(&max, &ctx),
3677 ArgumentHandle::new(&whole, &ctx),
3678 ];
3679 let v = f
3680 .dispatch(&args, &ctx.function_context(None))
3681 .unwrap()
3682 .into_literal();
3683 match v {
3684 LiteralValue::Array(a) => {
3685 assert_eq!(a.len(), 3);
3686 assert_eq!(a[0].len(), 2);
3687 for row in &a {
3689 for cell in row {
3690 let n = match cell {
3691 LiteralValue::Int(n) => *n as f64,
3692 LiteralValue::Number(n) => *n,
3693 other => panic!("expected Int or Number got {other:?}"),
3694 };
3695 assert!((1.0..=10.0).contains(&n), "Value {n} not in [1, 10]");
3696 assert!(n.fract() == 0.0, "Value {n} is not a whole number");
3698 }
3699 }
3700 }
3701 other => panic!("expected array got {other:?}"),
3702 }
3703 }
3704}