1use crate::eval::coercion::to_bool;
4use crate::eval::{evaluate_expr, EvalCtx};
5use crate::parser::ast::Expr;
6use crate::types::{ErrorKind, Value};
7
8use super::{check_arity, check_arity_len, EagerFn, EvalOp, FunctionKind, FunctionMeta, Registry};
9
10pub fn to_2d(v: &Value) -> Vec<Vec<Value>> {
17 match v {
18 Value::Array(outer) => {
19 if outer.iter().any(|e| matches!(e, Value::Array(_))) {
20 outer
21 .iter()
22 .map(|row| match row {
23 Value::Array(cols) => cols.clone(),
24 other => vec![other.clone()],
25 })
26 .collect()
27 } else {
28 vec![outer.clone()] }
30 }
31 other => vec![vec![other.clone()]], }
33}
34
35pub fn from_2d(rows: Vec<Vec<Value>>) -> Value {
40 if rows.is_empty() {
41 return Value::Array(vec![]);
42 }
43 if rows.len() == 1 {
44 return Value::Array(rows.into_iter().next().unwrap());
45 }
46 Value::Array(rows.into_iter().map(Value::Array).collect())
47}
48
49pub fn flatten_val(v: &Value) -> Vec<Value> {
51 match v {
52 Value::Array(outer) => {
53 if outer.iter().any(|e| matches!(e, Value::Array(_))) {
54 outer
55 .iter()
56 .flat_map(|row| match row {
57 Value::Array(cols) => cols.clone(),
58 other => vec![other.clone()],
59 })
60 .collect()
61 } else {
62 outer.clone()
63 }
64 }
65 other => vec![other.clone()],
66 }
67}
68
69fn to_f64(v: &Value) -> Option<f64> {
71 match v {
72 Value::Number(n) => Some(*n),
73 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
74 _ => None,
75 }
76}
77
78
79
80pub(crate) fn rows_fn(args: &[Value]) -> Value {
83 if let Some(e) = check_arity(args, 1, 1) {
84 return e;
85 }
86 let grid = to_2d(&args[0]);
87 Value::Number(grid.len() as f64)
88}
89
90pub(crate) fn columns_fn(args: &[Value]) -> Value {
93 if let Some(e) = check_arity(args, 1, 1) {
94 return e;
95 }
96 let grid = to_2d(&args[0]);
97 let cols = grid.first().map(|r| r.len()).unwrap_or(0);
98 Value::Number(cols as f64)
99}
100
101pub(crate) fn transpose_fn(args: &[Value]) -> Value {
104 if let Some(e) = check_arity(args, 1, 1) {
105 return e;
106 }
107 let grid = to_2d(&args[0]);
108 if grid.is_empty() {
109 return Value::Array(vec![]);
110 }
111 let nrows = grid.len();
112 let ncols = grid[0].len();
113 let transposed: Vec<Vec<Value>> = (0..ncols)
114 .map(|c| (0..nrows).map(|r| grid[r][c].clone()).collect())
115 .collect();
116 from_2d(transposed)
117}
118
119pub(crate) fn array_constrain_fn(args: &[Value]) -> Value {
122 if let Some(e) = check_arity(args, 3, 3) {
123 return e;
124 }
125 let grid = to_2d(&args[0]);
126 let num_rows = match to_f64(&args[1]) {
127 Some(n) if n >= 1.0 => n as usize,
128 Some(n) if n < 0.0 => return Value::Error(ErrorKind::Num),
129 Some(_) => return Value::Error(ErrorKind::Ref),
130 None => return Value::Error(ErrorKind::Value),
131 };
132 let num_cols = match to_f64(&args[2]) {
133 Some(n) if n >= 1.0 => n as usize,
134 Some(n) if n < 0.0 => return Value::Error(ErrorKind::Num),
135 Some(_) => return Value::Error(ErrorKind::Ref),
136 None => return Value::Error(ErrorKind::Value),
137 };
138 let rows_to_take = num_rows.min(grid.len());
139 let result: Vec<Vec<Value>> = grid[..rows_to_take]
140 .iter()
141 .map(|row| {
142 let cols_to_take = num_cols.min(row.len());
143 row[..cols_to_take].to_vec()
144 })
145 .collect();
146 from_2d(result)
147}
148
149fn choosecols_fn(args: &[Value]) -> Value {
152 if let Some(e) = check_arity(args, 2, usize::MAX) {
153 return e;
154 }
155 let grid = to_2d(&args[0]);
156 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
157 let mut selected_cols: Vec<usize> = Vec::new();
158 for col_arg in &args[1..] {
159 match to_f64(col_arg) {
160 Some(0.0) => return Value::Error(ErrorKind::Value),
161 Some(n) => {
162 let idx = if n < 0.0 {
163 let i = (ncols as isize + n as isize) as usize;
164 if n as isize + (ncols as isize) < 0 {
165 return Value::Error(ErrorKind::Value);
166 }
167 i
168 } else {
169 let i = n as usize - 1;
170 if i >= ncols {
171 return Value::Error(ErrorKind::Value);
172 }
173 i
174 };
175 selected_cols.push(idx);
176 }
177 None => return Value::Error(ErrorKind::Value),
178 }
179 }
180 let result: Vec<Vec<Value>> = grid
181 .iter()
182 .map(|row| {
183 selected_cols
184 .iter()
185 .map(|&c| row.get(c).cloned().unwrap_or(Value::Empty))
186 .collect()
187 })
188 .collect();
189 from_2d(result)
190}
191
192fn chooserows_fn(args: &[Value]) -> Value {
195 if let Some(e) = check_arity(args, 2, usize::MAX) {
196 return e;
197 }
198 let grid = to_2d(&args[0]);
199 let nrows = grid.len();
200 let mut selected_rows: Vec<usize> = Vec::new();
201 for row_arg in &args[1..] {
202 match to_f64(row_arg) {
203 Some(0.0) => return Value::Error(ErrorKind::Value),
204 Some(n) => {
205 let idx = if n < 0.0 {
206 let i = (nrows as isize + n as isize) as usize;
207 if n as isize + (nrows as isize) < 0 {
208 return Value::Error(ErrorKind::Value);
209 }
210 i
211 } else {
212 let i = n as usize - 1;
213 if i >= nrows {
214 return Value::Error(ErrorKind::Value);
215 }
216 i
217 };
218 selected_rows.push(idx);
219 }
220 None => return Value::Error(ErrorKind::Value),
221 }
222 }
223 let result: Vec<Vec<Value>> = selected_rows
224 .iter()
225 .map(|&r| grid.get(r).cloned().unwrap_or_default())
226 .collect();
227 from_2d(result)
228}
229
230pub(crate) fn flatten_fn(args: &[Value]) -> Value {
235 if let Some(e) = check_arity(args, 1, usize::MAX) {
236 return e;
237 }
238 let mut flat: Vec<Value> = Vec::new();
239 for arg in args {
240 flat.extend(flatten_val(arg));
241 }
242 let col: Vec<Vec<Value>> = flat.into_iter().map(|v| vec![v]).collect();
244 from_2d(col)
245}
246
247fn hstack_fn(args: &[Value]) -> Value {
250 if let Some(e) = check_arity(args, 1, usize::MAX) {
251 return e;
252 }
253 let grids: Vec<Vec<Vec<Value>>> = args.iter().map(to_2d).collect();
254 let nrows = grids.iter().map(|g| g.len()).max().unwrap_or(0);
255 let result: Vec<Vec<Value>> = (0..nrows)
256 .map(|r| {
257 grids
258 .iter()
259 .flat_map(|g| {
260 g.get(r).cloned().unwrap_or_default()
261 })
262 .collect()
263 })
264 .collect();
265 from_2d(result)
266}
267
268fn vstack_fn(args: &[Value]) -> Value {
271 if let Some(e) = check_arity(args, 1, usize::MAX) {
272 return e;
273 }
274 let mut result: Vec<Vec<Value>> = Vec::new();
275 for arg in args {
276 let grid = to_2d(arg);
277 result.extend(grid);
278 }
279 from_2d(result)
280}
281
282fn tocol_fn(args: &[Value]) -> Value {
288 if let Some(e) = check_arity(args, 1, 3) {
289 return e;
290 }
291 let ignore = if let Some(m) = args.get(1) {
292 match to_f64(m) {
293 Some(n) if (0.0..=3.0).contains(&n) => n as u8,
294 _ => return Value::Error(ErrorKind::Value),
295 }
296 } else {
297 0
298 };
299 let scan_by_col = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
300
301 let flat = if scan_by_col {
302 let grid = to_2d(&args[0]);
304 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
305 let mut out = Vec::new();
306 for c in 0..ncols {
307 for row in &grid {
308 out.push(row[c].clone());
309 }
310 }
311 out
312 } else {
313 flatten_val(&args[0])
314 };
315
316 let filtered: Vec<Value> = flat.into_iter().filter(|v| {
317 let is_blank = matches!(v, Value::Empty) || matches!(v, Value::Text(s) if s.is_empty());
318 let is_error = v.is_error();
319 if ignore == 1 && is_blank { return false; }
320 if ignore == 2 && is_error { return false; }
321 if ignore == 3 && (is_blank || is_error) { return false; }
322 true
323 }).collect();
324
325 let col: Vec<Vec<Value>> = filtered.into_iter().map(|v| vec![v]).collect();
326 from_2d(col)
327}
328
329fn torow_fn(args: &[Value]) -> Value {
335 if let Some(e) = check_arity(args, 1, 3) {
336 return e;
337 }
338 let ignore = if let Some(m) = args.get(1) {
339 match to_f64(m) {
340 Some(n) if (0.0..=3.0).contains(&n) => n as u8,
341 _ => return Value::Error(ErrorKind::Value),
342 }
343 } else {
344 0
345 };
346 let scan_by_col = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
347
348 let flat = if scan_by_col {
349 let grid = to_2d(&args[0]);
351 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
352 let mut out = Vec::new();
353 for c in 0..ncols {
354 for row in &grid {
355 out.push(row[c].clone());
356 }
357 }
358 out
359 } else {
360 flatten_val(&args[0])
361 };
362
363 let filtered: Vec<Value> = flat.into_iter().filter(|v| {
364 let is_blank = matches!(v, Value::Empty) || matches!(v, Value::Text(s) if s.is_empty());
365 let is_error = v.is_error();
366 if ignore == 1 && is_blank { return false; }
367 if ignore == 2 && is_error { return false; }
368 if ignore == 3 && (is_blank || is_error) { return false; }
369 true
370 }).collect();
371
372 Value::Array(filtered)
373}
374
375fn wrapcols_fn(args: &[Value]) -> Value {
380 if let Some(e) = check_arity(args, 2, 3) {
381 return e;
382 }
383 let flat = flatten_val(&args[0]);
384 let wrap_count = match to_f64(&args[1]) {
385 Some(n) if n >= 1.0 => n as usize,
386 Some(_) => return Value::Error(ErrorKind::Num),
387 None => return Value::Error(ErrorKind::Value),
388 };
389 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
390
391 let ncols = flat.len().div_ceil(wrap_count);
393 let nrows = wrap_count;
394
395 let grid: Vec<Vec<Value>> = (0..nrows)
397 .map(|r| {
398 (0..ncols)
399 .map(|c| {
400 let idx = c * wrap_count + r;
401 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
402 })
403 .collect()
404 })
405 .collect();
406 from_2d(grid)
407}
408
409fn wraprows_fn(args: &[Value]) -> Value {
413 if let Some(e) = check_arity(args, 2, 3) {
414 return e;
415 }
416 let flat = flatten_val(&args[0]);
417 let wrap_count = match to_f64(&args[1]) {
418 Some(n) if n >= 1.0 => n as usize,
419 Some(_) => return Value::Error(ErrorKind::Num),
420 None => return Value::Error(ErrorKind::Value),
421 };
422 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
423
424 let nrows = flat.len().div_ceil(wrap_count);
425 let grid: Vec<Vec<Value>> = (0..nrows)
426 .map(|r| {
427 (0..wrap_count)
428 .map(|c| {
429 let idx = r * wrap_count + c;
430 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
431 })
432 .collect()
433 })
434 .collect();
435 from_2d(grid)
436}
437
438pub(crate) fn sort_fn(args: &[Value]) -> Value {
441 if let Some(e) = check_arity(args, 1, 4) {
442 return e;
443 }
444 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
445
446 if is_1d {
450 let by_col = args.get(3).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
451 if by_col {
452 return Value::Error(ErrorKind::NA);
453 }
454 return args[0].clone();
455 }
456
457 let mut grid = to_2d(&args[0]);
458 let sort_col = if args.len() >= 2 {
459 match to_f64(&args[1]) {
460 Some(n) if n >= 1.0 => n as usize - 1,
461 Some(_) => return Value::Error(ErrorKind::Value),
462 None => 0,
463 }
464 } else {
465 0
466 };
467 let ascending = if args.len() >= 3 {
468 match &args[2] {
469 Value::Number(n) => *n >= 0.0,
470 Value::Bool(b) => *b,
471 _ => true,
472 }
473 } else {
474 true
475 };
476
477 grid.sort_by(|a, b| {
478 let va = a.get(sort_col).unwrap_or(&Value::Empty);
479 let vb = b.get(sort_col).unwrap_or(&Value::Empty);
480 let cmp = compare_values_sort(va, vb);
481 if ascending { cmp } else { cmp.reverse() }
482 });
483 from_2d(grid)
484}
485
486fn compare_values_sort(a: &Value, b: &Value) -> std::cmp::Ordering {
487 match (a, b) {
488 (Value::Number(x), Value::Number(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
489 (Value::Text(x), Value::Text(y)) => x.cmp(y),
490 (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
491 (Value::Zoned(x), Value::Zoned(y)) => x.utc_nanos.cmp(&y.utc_nanos),
493 (Value::Sparkline(_), Value::Sparkline(_)) => std::cmp::Ordering::Equal,
498 _ => std::cmp::Ordering::Equal,
499 }
500}
501
502fn sortby_fn(args: &[Value]) -> Value {
505 if let Some(e) = check_arity(args, 2, usize::MAX) {
506 return e;
507 }
508 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
509
510 if is_1d {
511 let elems = flatten_val(&args[0]);
513 let n = elems.len();
514
515 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
516 let mut i = 1;
517 while i < args.len() {
518 let key_vals = flatten_val(&args[i]);
519 if key_vals.len() != n {
520 return Value::Error(ErrorKind::Value);
521 }
522 let ascending = if i + 1 < args.len() {
523 match to_f64(&args[i + 1]) {
524 Some(v) => v >= 0.0,
525 None => true,
526 }
527 } else {
528 true
529 };
530 sort_keys.push((key_vals, ascending));
531 i += 2;
532 }
533
534 let mut indices: Vec<usize> = (0..n).collect();
535 indices.sort_by(|&ra, &rb| {
536 for (keys, asc) in &sort_keys {
537 let va = keys.get(ra).unwrap_or(&Value::Empty);
538 let vb = keys.get(rb).unwrap_or(&Value::Empty);
539 let cmp = compare_values_sort(va, vb);
540 if cmp != std::cmp::Ordering::Equal {
541 return if *asc { cmp } else { cmp.reverse() };
542 }
543 }
544 std::cmp::Ordering::Equal
545 });
546
547 return Value::Array(indices.iter().map(|&r| elems[r].clone()).collect());
548 }
549
550 let grid = to_2d(&args[0]);
551 let nrows = grid.len();
552
553 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
555 let mut i = 1;
556 while i < args.len() {
557 let key_vals = flatten_val(&args[i]);
558 if key_vals.len() != nrows && nrows > 1 {
559 return Value::Error(ErrorKind::Value);
560 }
561 let ascending = if i + 1 < args.len() {
562 match to_f64(&args[i + 1]) {
563 Some(n) => n >= 0.0,
564 None => true,
565 }
566 } else {
567 true
568 };
569 sort_keys.push((key_vals, ascending));
570 i += 2;
571 }
572
573 let mut indices: Vec<usize> = (0..nrows).collect();
574 indices.sort_by(|&ra, &rb| {
575 for (keys, asc) in &sort_keys {
576 let va = keys.get(ra).unwrap_or(&Value::Empty);
577 let vb = keys.get(rb).unwrap_or(&Value::Empty);
578 let cmp = compare_values_sort(va, vb);
579 if cmp != std::cmp::Ordering::Equal {
580 return if *asc { cmp } else { cmp.reverse() };
581 }
582 }
583 std::cmp::Ordering::Equal
584 });
585
586 let sorted: Vec<Vec<Value>> = indices.iter().map(|&r| grid[r].clone()).collect();
587 drop(grid);
588 from_2d(sorted)
589}
590
591pub(crate) fn unique_fn(args: &[Value]) -> Value {
594 if let Some(e) = check_arity(args, 1, 3) {
595 return e;
596 }
597 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
598 let grid = to_2d(&args[0]);
599 let by_col = args.get(1).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
601 let exactly_once = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
602
603 if is_1d && !by_col {
607 return args[0].clone();
608 }
609
610 if by_col {
611 let nrows = grid.len();
613 if nrows == 0 {
614 return from_2d(vec![]);
615 }
616 let ncols = grid[0].len();
617 let columns: Vec<Vec<Value>> = (0..ncols)
619 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
620 .collect();
621 let mut seen_cols: Vec<Vec<Value>> = Vec::new();
622 let mut counts: Vec<usize> = Vec::new();
623 for col in columns {
624 if let Some(pos) = seen_cols.iter().position(|sc| sc == &col) {
625 counts[pos] += 1;
626 } else {
627 seen_cols.push(col);
628 counts.push(1);
629 }
630 }
631 let result_cols: Vec<Vec<Value>> = seen_cols
632 .into_iter()
633 .zip(counts)
634 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
635 .map(|(col, _)| col)
636 .collect();
637 let ncols2 = result_cols.len();
639 let result: Vec<Vec<Value>> = (0..nrows)
640 .map(|r| (0..ncols2).map(|c| result_cols[c][r].clone()).collect())
641 .collect();
642 return from_2d(result);
643 }
644
645 let mut seen_rows: Vec<Vec<Value>> = Vec::new();
647 let mut counts: Vec<usize> = Vec::new();
648 for row in &grid {
649 if let Some(pos) = seen_rows.iter().position(|sr| sr == row) {
650 counts[pos] += 1;
651 } else {
652 seen_rows.push(row.clone());
653 counts.push(1);
654 }
655 }
656 let result: Vec<Vec<Value>> = seen_rows
657 .into_iter()
658 .zip(counts)
659 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
660 .map(|(row, _)| row)
661 .collect();
662 from_2d(result)
663}
664
665pub(crate) fn sumproduct_fn(args: &[Value]) -> Value {
668 if let Some(e) = check_arity(args, 1, usize::MAX) {
669 return e;
670 }
671 let arrays: Vec<Vec<Value>> = args.iter().map(flatten_val).collect();
672 let len = arrays[0].len();
673 for arr in &arrays[1..] {
675 if arr.len() != len {
676 return Value::Error(ErrorKind::Value);
677 }
678 }
679 let mut sum = 0.0;
680 for i in 0..len {
681 let mut prod = 1.0;
682 for arr in &arrays {
683 prod *= to_f64(&arr[i]).unwrap_or(0.0);
684 }
685 sum += prod;
686 }
687 Value::Number(sum)
688}
689
690fn sumxmy2_fn(args: &[Value]) -> Value {
693 if let Some(e) = check_arity(args, 2, 2) {
694 return e;
695 }
696 let xs = flatten_val(&args[0]);
697 let ys = flatten_val(&args[1]);
698 if xs.len() != ys.len() {
699 return Value::Error(ErrorKind::NA);
700 }
701 let mut sum = 0.0;
702 for (x, y) in xs.iter().zip(ys.iter()) {
703 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
705 sum += (*xn - *yn).powi(2);
706 }
707 }
708 Value::Number(sum)
709}
710
711fn sumx2my2_fn(args: &[Value]) -> Value {
714 if let Some(e) = check_arity(args, 2, 2) {
715 return e;
716 }
717 let xs = flatten_val(&args[0]);
718 let ys = flatten_val(&args[1]);
719 if xs.len() != ys.len() {
720 return Value::Error(ErrorKind::NA);
721 }
722 let mut sum = 0.0;
723 for (x, y) in xs.iter().zip(ys.iter()) {
724 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
725 sum += *xn * *xn - *yn * *yn;
726 }
727 }
728 Value::Number(sum)
729}
730
731fn sumx2py2_fn(args: &[Value]) -> Value {
734 if let Some(e) = check_arity(args, 2, 2) {
735 return e;
736 }
737 let xs = flatten_val(&args[0]);
738 let ys = flatten_val(&args[1]);
739 if xs.len() != ys.len() {
740 return Value::Error(ErrorKind::NA);
741 }
742 let mut sum = 0.0;
743 for (x, y) in xs.iter().zip(ys.iter()) {
744 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
745 sum += *xn * *xn + *yn * *yn;
746 }
747 }
748 Value::Number(sum)
749}
750
751fn mmult_fn(args: &[Value]) -> Value {
754 if let Some(e) = check_arity(args, 2, 2) {
755 return e;
756 }
757 let a = to_2d(&args[0]);
758 let b = to_2d(&args[1]);
759 if a.iter().chain(b.iter()).any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
760 return Value::Error(ErrorKind::Value);
761 }
762 let n = a.first().map(|r| r.len()).unwrap_or(0);
763 let p = b.first().map(|r| r.len()).unwrap_or(0);
764 if b.len() != n {
765 return Value::Error(ErrorKind::Value);
766 }
767 let af: Vec<Vec<f64>> = a.iter().map(|row| {
769 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
770 }).collect();
771 let bf: Vec<Vec<f64>> = b.iter().map(|row| {
772 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
773 }).collect();
774 if af.iter().any(|r| r.iter().any(|v| v.is_nan())) || bf.iter().any(|r| r.iter().any(|v| v.is_nan())) {
775 return Value::Error(ErrorKind::Value);
776 }
777 let result: Vec<Vec<Value>> = af.iter().map(|row_a| {
778 (0..p).map(|j| {
779 let sum: f64 = row_a.iter().enumerate().map(|(k, &av)| av * bf[k][j]).sum();
780 Value::Number(sum)
781 }).collect()
782 }).collect();
783 from_2d(result)
784}
785
786fn mdeterm_fn(args: &[Value]) -> Value {
789 if let Some(e) = check_arity(args, 1, 1) {
790 return e;
791 }
792 let grid = to_2d(&args[0]);
793 let n = grid.len();
794 if n == 0 {
795 return Value::Error(ErrorKind::Value);
796 }
797 for row in &grid {
798 if row.len() != n {
799 return Value::Error(ErrorKind::Value);
800 }
801 }
802 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
803 return Value::Error(ErrorKind::Value);
804 }
805 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
807 for row in &grid {
808 let mut r = Vec::with_capacity(n);
809 for v in row {
810 match to_f64(v) {
811 Some(x) => r.push(x),
812 None => return Value::Error(ErrorKind::Value),
813 }
814 }
815 mat.push(r);
816 }
817 Value::Number(determinant(&mat))
818}
819
820fn determinant(mat: &[Vec<f64>]) -> f64 {
821 let n = mat.len();
822 if n == 1 {
823 return mat[0][0];
824 }
825 if n == 2 {
826 return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
827 }
828 let mut det = 0.0;
829 for c in 0..n {
830 let minor: Vec<Vec<f64>> = (1..n)
831 .map(|r| {
832 (0..n)
833 .filter(|&cc| cc != c)
834 .map(|cc| mat[r][cc])
835 .collect()
836 })
837 .collect();
838 let sign = if c % 2 == 0 { 1.0 } else { -1.0 };
839 det += sign * mat[0][c] * determinant(&minor);
840 }
841 det
842}
843
844fn minverse_fn(args: &[Value]) -> Value {
847 if let Some(e) = check_arity(args, 1, 1) {
848 return e;
849 }
850 let grid = to_2d(&args[0]);
851 let n = grid.len();
852 if n == 0 {
853 return Value::Error(ErrorKind::Value);
854 }
855 for row in &grid {
856 if row.len() != n {
857 return Value::Error(ErrorKind::Value);
858 }
859 }
860 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
861 return Value::Error(ErrorKind::Value);
862 }
863 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
864 for row in &grid {
865 let mut r = Vec::with_capacity(n);
866 for v in row {
867 match to_f64(v) {
868 Some(x) => r.push(x),
869 None => return Value::Error(ErrorKind::Value),
870 }
871 }
872 mat.push(r);
873 }
874 match invert_matrix(mat) {
875 Some(inv) => from_2d(inv.into_iter().map(|r| r.into_iter().map(Value::Number).collect()).collect()),
876 None => Value::Error(ErrorKind::Num),
877 }
878}
879
880fn invert_matrix(mut mat: Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
881 let n = mat.len();
882 let mut inv: Vec<Vec<f64>> = (0..n)
884 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
885 .collect();
886 for col in 0..n {
887 let pivot = (col..n).max_by(|&a, &b| mat[a][col].abs().partial_cmp(&mat[b][col].abs()).unwrap_or(std::cmp::Ordering::Equal))?;
889 if mat[pivot][col].abs() < 1e-12 {
890 return None; }
892 mat.swap(col, pivot);
893 inv.swap(col, pivot);
894 let div = mat[col][col];
895 for j in 0..n {
896 mat[col][j] /= div;
897 inv[col][j] /= div;
898 }
899 for r in 0..n {
900 if r != col {
901 let factor = mat[r][col];
902 for j in 0..n {
903 mat[r][j] -= factor * mat[col][j];
904 inv[r][j] -= factor * inv[col][j];
905 }
906 }
907 }
908 }
909 Some(inv)
910}
911
912fn frequency_fn(args: &[Value]) -> Value {
916 if let Some(e) = check_arity(args, 2, 2) {
917 return e;
918 }
919 let data: Vec<f64> = flatten_val(&args[0])
921 .iter()
922 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
923 .collect();
924 let bins_raw = flatten_val(&args[1]);
925 if bins_raw.is_empty() || matches!(bins_raw.as_slice(), [Value::Empty]) {
927 return Value::Error(ErrorKind::Ref);
928 }
929 let all_empty = bins_raw.iter().all(|v| matches!(v, Value::Empty));
931 if all_empty {
932 return Value::Error(ErrorKind::Ref);
933 }
934 let bins: Vec<f64> = bins_raw
935 .iter()
936 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
937 .collect();
938 if bins.is_empty() {
939 return Value::Error(ErrorKind::Ref);
940 }
941 let mut counts = vec![0i64; bins.len() + 1];
943 for &x in &data {
944 let mut placed = false;
945 for (i, &b) in bins.iter().enumerate() {
946 if x <= b {
947 counts[i] += 1;
948 placed = true;
949 break;
950 }
951 }
952 if !placed {
953 counts[bins.len()] += 1;
954 }
955 }
956 let col: Vec<Vec<Value>> = counts
958 .into_iter()
959 .map(|c| vec![Value::Number(c as f64)])
960 .collect();
961 from_2d(col)
962}
963
964fn linest_fn(args: &[Value]) -> Value {
968 if let Some(e) = check_arity(args, 1, 4) {
969 return e;
970 }
971 let ys = flatten_val(&args[0]);
972 let n = ys.len();
973 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
975 return Value::Error(ErrorKind::Value);
976 }
977 if n < 2 {
978 return Value::Error(ErrorKind::NA);
979 }
980 let xs: Vec<f64> = if args.len() >= 2 {
981 let xv = flatten_val(&args[1]);
982 if xv.len() != n {
983 return Value::Error(ErrorKind::Ref);
984 }
985 xv.iter().filter_map(to_f64).collect()
986 } else {
987 (1..=n).map(|i| i as f64).collect()
988 };
989 if xs.len() != n {
990 return Value::Error(ErrorKind::Ref);
991 }
992 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
993 if y_vals.len() != n {
994 return Value::Error(ErrorKind::Value);
995 }
996 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
997 Value::Array(vec![Value::Number(slope), Value::Number(intercept)])
998}
999
1000fn simple_linear_regression(xs: &[f64], ys: &[f64]) -> (f64, f64) {
1001 let n = xs.len() as f64;
1002 let sum_x: f64 = xs.iter().sum();
1003 let sum_y: f64 = ys.iter().sum();
1004 let sum_xy: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * y).sum();
1005 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1006 let denom = n * sum_xx - sum_x * sum_x;
1007 if denom.abs() < 1e-15 {
1008 let intercept = sum_y / n;
1009 return (0.0, intercept);
1010 }
1011 let slope = (n * sum_xy - sum_x * sum_y) / denom;
1012 let intercept = (sum_y - slope * sum_x) / n;
1013 (slope, intercept)
1014}
1015
1016fn logest_fn(args: &[Value]) -> Value {
1020 if let Some(e) = check_arity(args, 1, 4) {
1021 return e;
1022 }
1023 let ys = flatten_val(&args[0]);
1024 let n = ys.len();
1025 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1027 return Value::Error(ErrorKind::Value);
1028 }
1029 if n < 2 {
1030 return Value::Error(ErrorKind::NA);
1031 }
1032 let xs: Vec<f64> = if args.len() >= 2 {
1033 let xv = flatten_val(&args[1]);
1034 if xv.len() != n {
1035 return Value::Error(ErrorKind::Ref);
1036 }
1037 xv.iter().filter_map(to_f64).collect()
1038 } else {
1039 (1..=n).map(|i| i as f64).collect()
1040 };
1041 if xs.len() != n {
1042 return Value::Error(ErrorKind::Ref);
1043 }
1044 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1045 if y_vals.len() != n {
1046 return Value::Error(ErrorKind::Value);
1047 }
1048 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1050 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1051 return Value::Error(ErrorKind::Num);
1052 }
1053 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
1054 let base = libm::exp(log_base);
1055 let intercept = libm::exp(log_intercept);
1056 Value::Array(vec![Value::Number(base), Value::Number(intercept)])
1057}
1058
1059fn trend_fn(args: &[Value]) -> Value {
1063 if let Some(e) = check_arity(args, 1, 4) {
1064 return e;
1065 }
1066 let ys = flatten_val(&args[0]);
1067 let n = ys.len();
1068 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
1070 return Value::Error(ErrorKind::Value);
1071 }
1072 if n < 2 {
1073 return Value::Error(ErrorKind::NA);
1074 }
1075 let xs: Vec<f64> = if args.len() >= 2 {
1076 let xv = flatten_val(&args[1]);
1077 if xv.len() != n {
1078 return Value::Error(ErrorKind::Ref);
1079 }
1080 xv.iter().filter_map(to_f64).collect()
1081 } else {
1082 (1..=n).map(|i| i as f64).collect()
1083 };
1084 if xs.len() != n {
1085 return Value::Error(ErrorKind::Ref);
1086 }
1087 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1088 if y_vals.len() != n {
1089 return Value::Error(ErrorKind::Value);
1090 }
1091 let new_xs: Vec<f64> = if args.len() >= 3 {
1092 flatten_val(&args[2]).iter().filter_map(to_f64).collect()
1093 } else {
1094 xs.clone()
1095 };
1096 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1097 let result: Vec<Value> = new_xs.iter().map(|&x| Value::Number(slope * x + intercept)).collect();
1098 Value::Array(result)
1099}
1100
1101fn growth_fn(args: &[Value]) -> Value {
1105 if let Some(e) = check_arity(args, 1, 4) {
1106 return e;
1107 }
1108 let ys = flatten_val(&args[0]);
1109 let n = ys.len();
1110 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1112 return Value::Error(ErrorKind::Value);
1113 }
1114 if n < 2 {
1115 return Value::Error(ErrorKind::NA);
1116 }
1117 let xs: Vec<f64> = if args.len() >= 2 {
1118 let xv = flatten_val(&args[1]);
1119 if xv.len() != n {
1120 return Value::Error(ErrorKind::Ref);
1121 }
1122 xv.iter().filter_map(to_f64).collect()
1123 } else {
1124 (1..=n).map(|i| i as f64).collect()
1125 };
1126 if xs.len() != n {
1127 return Value::Error(ErrorKind::Ref);
1128 }
1129 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1130 if y_vals.len() != n {
1131 return Value::Error(ErrorKind::Value);
1132 }
1133 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1134 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1135 return Value::Error(ErrorKind::Num);
1136 }
1137 let new_xs: Vec<f64> = if args.len() >= 3 && !matches!(args[2], Value::Empty) {
1138 let vals: Vec<f64> = flatten_val(&args[2]).iter().filter_map(to_f64).collect();
1139 if vals.is_empty() { xs.clone() } else { vals }
1140 } else {
1141 xs.clone()
1142 };
1143 let use_intercept = if args.len() >= 4 {
1146 match &args[3] {
1147 Value::Bool(b) => *b,
1148 Value::Number(n) => *n != 0.0,
1149 _ => true,
1150 }
1151 } else {
1152 true
1153 };
1154 let (log_base, log_intercept) = if use_intercept {
1155 simple_linear_regression(&xs, &log_y)
1156 } else {
1157 let sum_xy: f64 = xs.iter().zip(log_y.iter()).map(|(x, ly)| x * ly).sum();
1159 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1160 let slope = if sum_xx.abs() < 1e-15 { 0.0 } else { sum_xy / sum_xx };
1161 (slope, 0.0)
1162 };
1163 let result: Vec<Value> = new_xs
1164 .iter()
1165 .map(|&x| Value::Number(libm::exp(log_base * x + log_intercept)))
1166 .collect();
1167 Value::Array(result)
1168}
1169
1170fn apply_lambda(lambda_expr: &Expr, bound_args: &[Value], ctx: &mut EvalCtx<'_>) -> Option<Value> {
1182 match lambda_expr {
1183 Expr::FunctionCall { name, args, .. } if name == "LAMBDA" => {
1184 if args.is_empty() {
1185 return None;
1186 }
1187 let body = &args[args.len() - 1];
1188 let params = &args[..args.len() - 1];
1189 if params.len() != bound_args.len() {
1190 return None;
1191 }
1192 let mut saved: Vec<(String, Value)> = Vec::new();
1194 for (param_expr, val) in params.iter().zip(bound_args.iter()) {
1195 if let Expr::Variable(name, span) = param_expr {
1196 if let Some(hook) = ctx.hook.as_deref_mut() {
1206 hook.on_node(EvalOp::Variable(name), *span, val);
1207 }
1208 let bind_key = name.to_uppercase().replace('$', "");
1216 let old = ctx.ctx.get(&bind_key);
1217 saved.push((bind_key.clone(), old));
1218 ctx.ctx.set(bind_key, val.clone());
1219 } else {
1220 return None;
1221 }
1222 }
1223 let result = evaluate_expr(body, ctx);
1224 for (name, old_val) in saved {
1226 ctx.ctx.set(name, old_val);
1227 }
1228 Some(result)
1229 }
1230 _ => None,
1231 }
1232}
1233
1234pub fn byrow_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1237 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1238 return e;
1239 }
1240 let arr_val = evaluate_expr(&args[0], ctx);
1241 if arr_val.is_error() {
1242 return arr_val;
1243 }
1244 let grid = to_2d(&arr_val);
1245 let lambda_expr = &args[1];
1246 let mut results: Vec<Value> = Vec::with_capacity(grid.len());
1247 for row in &grid {
1248 let row_val = Value::Array(row.clone());
1249 match apply_lambda(lambda_expr, &[row_val], ctx) {
1250 Some(v) => results.push(v),
1251 None => return Value::Error(ErrorKind::NA),
1252 }
1253 }
1254 let col: Vec<Vec<Value>> = results.into_iter().map(|v| vec![v]).collect();
1256 from_2d(col)
1257}
1258
1259pub fn bycol_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1262 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1263 return e;
1264 }
1265 let arr_val = evaluate_expr(&args[0], ctx);
1266 if arr_val.is_error() {
1267 return arr_val;
1268 }
1269 let grid = to_2d(&arr_val);
1270 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
1271 let columns: Vec<Vec<Value>> = (0..ncols)
1273 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
1274 .collect();
1275 let lambda_expr = &args[1];
1276 let mut results: Vec<Value> = Vec::with_capacity(ncols);
1277 for col in columns {
1278 let col_val = Value::Array(col);
1280 match apply_lambda(lambda_expr, &[col_val], ctx) {
1281 Some(v) => results.push(v),
1282 None => return Value::Error(ErrorKind::NA),
1283 }
1284 }
1285 Value::Array(results)
1287}
1288
1289pub fn map_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1292 if let Some(e) = check_arity_len(args.len(), 2, usize::MAX) {
1293 return e;
1294 }
1295 let lambda_expr = &args[args.len() - 1];
1297 let arr_count = args.len() - 1;
1298 let mut first_shape_val: Option<Value> = None;
1306 let mut arrays: Vec<Vec<Value>> = Vec::with_capacity(arr_count);
1307 for (i, a) in args[..arr_count].iter().enumerate() {
1308 let v = evaluate_expr(a, ctx);
1309 if v.is_error() {
1310 return v;
1311 }
1312 if i == 0 {
1313 first_shape_val = Some(v.clone());
1314 }
1315 arrays.push(flatten_val(&v));
1316 }
1317 let len = arrays[0].len();
1318 for arr in &arrays[1..] {
1319 if arr.len() != len {
1320 return Value::Error(ErrorKind::Value);
1321 }
1322 }
1323 let mut results: Vec<Value> = Vec::with_capacity(len);
1324 for i in 0..len {
1325 let bound: Vec<Value> = arrays.iter().map(|a| a[i].clone()).collect();
1326 match apply_lambda(lambda_expr, &bound, ctx) {
1327 Some(v) => results.push(v),
1328 None => return Value::Error(ErrorKind::NA),
1329 }
1330 }
1331 let first_grid = to_2d(
1333 first_shape_val
1334 .as_ref()
1335 .expect("arr_count >= 1 (check_arity_len enforces at least 2 args)"),
1336 );
1337 if first_grid.len() > 1 {
1338 let ncols = first_grid[0].len();
1340 let nrows = first_grid.len();
1341 let grid: Vec<Vec<Value>> = (0..nrows)
1342 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1343 .collect();
1344 from_2d(grid)
1345 } else {
1346 Value::Array(results)
1347 }
1348}
1349
1350pub fn reduce_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1353 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1354 return e;
1355 }
1356 let initial = evaluate_expr(&args[0], ctx);
1357 if initial.is_error() {
1358 return initial;
1359 }
1360 let arr_val = evaluate_expr(&args[1], ctx);
1361 if arr_val.is_error() {
1362 return arr_val;
1363 }
1364 let items = flatten_val(&arr_val);
1365 if items.is_empty() {
1366 return Value::Error(ErrorKind::Ref);
1367 }
1368 let lambda_expr = &args[2];
1369 let mut acc = initial;
1370 for item in &items {
1371 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1372 Some(v) => acc = v,
1373 None => return Value::Error(ErrorKind::NA),
1374 }
1375 }
1376 acc
1377}
1378
1379pub fn scan_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1382 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1383 return e;
1384 }
1385 let initial = evaluate_expr(&args[0], ctx);
1386 if initial.is_error() {
1387 return initial;
1388 }
1389 let arr_val = evaluate_expr(&args[1], ctx);
1390 if arr_val.is_error() {
1391 return arr_val;
1392 }
1393 let grid = to_2d(&arr_val);
1394 let items = flatten_val(&arr_val);
1395 let lambda_expr = &args[2];
1396 let mut acc = initial;
1397 let mut results: Vec<Value> = Vec::with_capacity(items.len());
1398 for item in &items {
1399 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1400 Some(v) => {
1401 acc = v.clone();
1402 results.push(v);
1403 }
1404 None => return Value::Error(ErrorKind::NA),
1405 }
1406 }
1407 if grid.len() > 1 {
1409 let ncols = grid[0].len();
1410 let nrows = grid.len();
1411 let result_grid: Vec<Vec<Value>> = (0..nrows)
1412 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1413 .collect();
1414 from_2d(result_grid)
1415 } else {
1416 Value::Array(results)
1417 }
1418}
1419
1420pub fn makearray_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1423 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1424 return e;
1425 }
1426 let rows_val = evaluate_expr(&args[0], ctx);
1427 let cols_val = evaluate_expr(&args[1], ctx);
1428 if rows_val.is_error() {
1429 return rows_val;
1430 }
1431 if cols_val.is_error() {
1432 return cols_val;
1433 }
1434 let nrows = match to_f64(&rows_val) {
1435 Some(n) if n >= 1.0 => n as usize,
1436 _ => return Value::Error(ErrorKind::Value),
1437 };
1438 let ncols = match to_f64(&cols_val) {
1439 Some(n) if n >= 1.0 => n as usize,
1440 _ => return Value::Error(ErrorKind::Value),
1441 };
1442 let lambda_expr = &args[2];
1443 let mut grid: Vec<Vec<Value>> = Vec::with_capacity(nrows);
1444 for r in 1..=nrows {
1445 let mut row = Vec::with_capacity(ncols);
1446 for c in 1..=ncols {
1447 let rv = Value::Number(r as f64);
1448 let cv = Value::Number(c as f64);
1449 match apply_lambda(lambda_expr, &[rv, cv], ctx) {
1450 Some(Value::Array(_)) => return Value::Error(ErrorKind::Value),
1451 Some(v) => row.push(v),
1452 None => return Value::Error(ErrorKind::NA),
1453 }
1454 }
1455 grid.push(row);
1456 }
1457 if nrows == 1 && ncols == 1 {
1458 return grid[0][0].clone();
1459 }
1460 from_2d(grid)
1461}
1462
1463pub fn arrayformula_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1476 if args.len() != 1 {
1477 return Value::Error(ErrorKind::NA);
1478 }
1479 broadcast_expr(&args[0], ctx)
1480}
1481
1482fn broadcast_expr(expr: &Expr, ctx: &mut EvalCtx<'_>) -> Value {
1483 match expr {
1484 Expr::FunctionCall { name, args: if_args, .. }
1489 if name == "IF" && (if_args.len() == 2 || if_args.len() == 3) =>
1490 {
1491 let cond = evaluate_expr(&if_args[0], ctx);
1492 if !matches!(cond, Value::Array(_)) {
1493 return evaluate_expr(expr, ctx);
1494 }
1495 let true_val = evaluate_expr(&if_args[1], ctx);
1496 let false_val = if if_args.len() == 3 {
1497 evaluate_expr(&if_args[2], ctx)
1498 } else {
1499 Value::Bool(false)
1500 };
1501 broadcast_if(&cond, &true_val, &false_val)
1502 }
1503 Expr::FunctionCall { name, args: inner_args, .. }
1509 if name == "ISNUMBER" && inner_args.len() == 1 =>
1510 {
1511 let v = evaluate_expr(&inner_args[0], ctx);
1512 if v.is_error() {
1513 return v;
1514 }
1515 if matches!(v, Value::Array(_)) {
1516 broadcast_eager(super::logical::is_checks::isnumber_fn, &[v])
1517 } else {
1518 super::logical::is_checks::isnumber_fn(&[v])
1519 }
1520 }
1521 Expr::FunctionCall { name, args: inner_args, .. }
1529 if matches!(name.as_str(), "LEN" | "UPPER") =>
1530 {
1531 match ctx.registry.get(name) {
1532 Some(FunctionKind::Eager(f)) => {
1533 let f: EagerFn = *f;
1534 let mut evaluated = Vec::with_capacity(inner_args.len());
1535 for a in inner_args {
1536 let v = evaluate_expr(a, ctx);
1537 if v.is_error() {
1538 return v;
1539 }
1540 evaluated.push(v);
1541 }
1542 if evaluated.iter().any(|v| matches!(v, Value::Array(_))) {
1543 broadcast_eager(f, &evaluated)
1544 } else {
1545 f(&evaluated)
1546 }
1547 }
1548 _ => evaluate_expr(expr, ctx),
1549 }
1550 }
1551 _ => evaluate_expr(expr, ctx),
1552 }
1553}
1554
1555fn broadcast_shape(values: &[Value]) -> Option<(usize, usize)> {
1558 let mut shape = None;
1559 for v in values {
1560 if matches!(v, Value::Array(_)) {
1561 let grid = to_2d(v);
1562 let nr = grid.len();
1563 let nc = grid.first().map(Vec::len).unwrap_or(0);
1564 match shape {
1565 None => shape = Some((nr, nc)),
1566 Some((r, c)) if r == nr && c == nc => {}
1567 Some(_) => return None,
1568 }
1569 }
1570 }
1571 shape
1572}
1573
1574fn broadcast_eager(f: EagerFn, evaluated: &[Value]) -> Value {
1577 let (nrows, ncols) = match broadcast_shape(evaluated) {
1578 Some(s) => s,
1579 None => return Value::Error(ErrorKind::Value),
1580 };
1581 let grids: Vec<Option<Vec<Vec<Value>>>> = evaluated
1582 .iter()
1583 .map(|v| matches!(v, Value::Array(_)).then(|| to_2d(v)))
1584 .collect();
1585 let mut out = Vec::with_capacity(nrows);
1586 for r in 0..nrows {
1587 let mut row = Vec::with_capacity(ncols);
1588 for c in 0..ncols {
1589 let per_pos: Vec<Value> = evaluated
1590 .iter()
1591 .enumerate()
1592 .map(|(i, v)| match &grids[i] {
1593 Some(g) => g[r][c].clone(),
1594 None => v.clone(),
1595 })
1596 .collect();
1597 row.push(f(&per_pos));
1598 }
1599 out.push(row);
1600 }
1601 from_2d(out)
1602}
1603
1604fn broadcast_if(cond: &Value, true_val: &Value, false_val: &Value) -> Value {
1608 let cond_grid = to_2d(cond);
1609 let true_grid = matches!(true_val, Value::Array(_)).then(|| to_2d(true_val));
1610 let false_grid = matches!(false_val, Value::Array(_)).then(|| to_2d(false_val));
1611 let nrows = cond_grid.len();
1612 let ncols = cond_grid.first().map(Vec::len).unwrap_or(0);
1613 let mut out = Vec::with_capacity(nrows);
1614 for (r, cond_row) in cond_grid.iter().enumerate() {
1615 let mut row = Vec::with_capacity(ncols);
1616 for (c, cond_cell) in cond_row.iter().enumerate() {
1617 let branch_val = match to_bool(cond_cell.clone()) {
1618 Ok(true) => match &true_grid {
1619 Some(g) => g
1620 .get(r)
1621 .and_then(|row| row.get(c))
1622 .cloned()
1623 .unwrap_or(Value::Error(ErrorKind::Value)),
1624 None => true_val.clone(),
1625 },
1626 Ok(false) => match &false_grid {
1627 Some(g) => g
1628 .get(r)
1629 .and_then(|row| row.get(c))
1630 .cloned()
1631 .unwrap_or(Value::Error(ErrorKind::Value)),
1632 None => false_val.clone(),
1633 },
1634 Err(e) => e,
1635 };
1636 row.push(branch_val);
1637 }
1638 out.push(row);
1639 }
1640 from_2d(out)
1641}
1642
1643pub fn register_array(registry: &mut Registry) {
1644 registry.register_eager("ROWS", rows_fn, FunctionMeta {
1645 category: "array",
1646 signature: "ROWS(array)",
1647 description: "Returns the number of rows in an array or range",
1648 });
1649 registry.register_eager("COLUMNS", columns_fn, FunctionMeta {
1650 category: "array",
1651 signature: "COLUMNS(array)",
1652 description: "Returns the number of columns in an array or range",
1653 });
1654 registry.register_eager("TRANSPOSE", transpose_fn, FunctionMeta {
1655 category: "array",
1656 signature: "TRANSPOSE(array)",
1657 description: "Transposes the rows and columns of an array",
1658 });
1659 registry.register_eager("ARRAY_CONSTRAIN", array_constrain_fn, FunctionMeta {
1660 category: "array",
1661 signature: "ARRAY_CONSTRAIN(input, num_rows, num_cols)",
1662 description: "Constrains an array to a given number of rows and columns",
1663 });
1664 registry.register_eager("CHOOSECOLS", choosecols_fn, FunctionMeta {
1665 category: "array",
1666 signature: "CHOOSECOLS(array, col_num1, ...)",
1667 description: "Returns selected columns from an array",
1668 });
1669 registry.register_eager("CHOOSEROWS", chooserows_fn, FunctionMeta {
1670 category: "array",
1671 signature: "CHOOSEROWS(array, row_num1, ...)",
1672 description: "Returns selected rows from an array",
1673 });
1674 registry.register_eager("FLATTEN", flatten_fn, FunctionMeta {
1675 category: "array",
1676 signature: "FLATTEN(array)",
1677 description: "Flattens an array into a single column",
1678 });
1679 registry.register_eager("HSTACK", hstack_fn, FunctionMeta {
1680 category: "array",
1681 signature: "HSTACK(array1, ...)",
1682 description: "Horizontally stacks arrays",
1683 });
1684 registry.register_eager("VSTACK", vstack_fn, FunctionMeta {
1685 category: "array",
1686 signature: "VSTACK(array1, ...)",
1687 description: "Vertically stacks arrays",
1688 });
1689 registry.register_eager("TOCOL", tocol_fn, FunctionMeta {
1690 category: "array",
1691 signature: "TOCOL(array, [ignore], [scan_by_col])",
1692 description: "Converts an array to a single column",
1693 });
1694 registry.register_eager("TOROW", torow_fn, FunctionMeta {
1695 category: "array",
1696 signature: "TOROW(array, [ignore], [scan_by_col])",
1697 description: "Converts an array to a single row",
1698 });
1699 registry.register_eager("WRAPCOLS", wrapcols_fn, FunctionMeta {
1700 category: "array",
1701 signature: "WRAPCOLS(vector, wrap_count, [pad_with])",
1702 description: "Wraps a vector into columns of the given length",
1703 });
1704 registry.register_eager("WRAPROWS", wraprows_fn, FunctionMeta {
1705 category: "array",
1706 signature: "WRAPROWS(vector, wrap_count, [pad_with])",
1707 description: "Wraps a vector into rows of the given length",
1708 });
1709 registry.register_eager("SORT", sort_fn, FunctionMeta {
1710 category: "array",
1711 signature: "SORT(array, [sort_index], [sort_order], [by_col])",
1712 description: "Sorts an array",
1713 });
1714 registry.register_eager("SORTBY", sortby_fn, FunctionMeta {
1715 category: "array",
1716 signature: "SORTBY(array, by_array1, [sort_order1], ...)",
1717 description: "Sorts an array based on the values in corresponding arrays",
1718 });
1719 registry.register_eager("UNIQUE", unique_fn, FunctionMeta {
1720 category: "array",
1721 signature: "UNIQUE(array, [by_col], [exactly_once])",
1722 description: "Returns unique rows or columns from an array",
1723 });
1724 registry.register_eager("SUMPRODUCT", sumproduct_fn, FunctionMeta {
1725 category: "array",
1726 signature: "SUMPRODUCT(array1, [array2], ...)",
1727 description: "Returns the sum of products of corresponding elements",
1728 });
1729 registry.register_eager("SUMXMY2", sumxmy2_fn, FunctionMeta {
1730 category: "array",
1731 signature: "SUMXMY2(array_x, array_y)",
1732 description: "Returns sum of squares of differences",
1733 });
1734 registry.register_eager("SUMX2MY2", sumx2my2_fn, FunctionMeta {
1735 category: "array",
1736 signature: "SUMX2MY2(array_x, array_y)",
1737 description: "Returns sum of (x^2 - y^2)",
1738 });
1739 registry.register_eager("SUMX2PY2", sumx2py2_fn, FunctionMeta {
1740 category: "array",
1741 signature: "SUMX2PY2(array_x, array_y)",
1742 description: "Returns sum of (x^2 + y^2)",
1743 });
1744 registry.register_eager("MMULT", mmult_fn, FunctionMeta {
1745 category: "array",
1746 signature: "MMULT(array1, array2)",
1747 description: "Returns the matrix product of two arrays",
1748 });
1749 registry.register_eager("MDETERM", mdeterm_fn, FunctionMeta {
1750 category: "array",
1751 signature: "MDETERM(array)",
1752 description: "Returns the matrix determinant",
1753 });
1754 registry.register_eager("MINVERSE", minverse_fn, FunctionMeta {
1755 category: "array",
1756 signature: "MINVERSE(array)",
1757 description: "Returns the matrix inverse",
1758 });
1759 registry.register_eager("FREQUENCY", frequency_fn, FunctionMeta {
1760 category: "array",
1761 signature: "FREQUENCY(data, bins)",
1762 description: "Calculates the frequency distribution of values",
1763 });
1764 registry.register_eager("LINEST", linest_fn, FunctionMeta {
1765 category: "array",
1766 signature: "LINEST(known_y, [known_x], [const], [stats])",
1767 description: "Returns linear regression statistics",
1768 });
1769 registry.register_eager("LOGEST", logest_fn, FunctionMeta {
1770 category: "array",
1771 signature: "LOGEST(known_y, [known_x], [const], [stats])",
1772 description: "Returns exponential regression statistics",
1773 });
1774 registry.register_eager("TREND", trend_fn, FunctionMeta {
1775 category: "array",
1776 signature: "TREND(known_y, [known_x], [new_x], [const])",
1777 description: "Returns values along a linear trend",
1778 });
1779 registry.register_eager("GROWTH", growth_fn, FunctionMeta {
1780 category: "array",
1781 signature: "GROWTH(known_y, [known_x], [new_x], [const])",
1782 description: "Returns values along an exponential trend",
1783 });
1784 registry.register_lazy("BYROW", byrow_lazy_fn, FunctionMeta {
1785 category: "array",
1786 signature: "BYROW(array, lambda)",
1787 description: "Applies a LAMBDA to each row of an array",
1788 });
1789 registry.register_lazy("BYCOL", bycol_lazy_fn, FunctionMeta {
1790 category: "array",
1791 signature: "BYCOL(array, lambda)",
1792 description: "Applies a LAMBDA to each column of an array",
1793 });
1794 registry.register_lazy("MAP", map_lazy_fn, FunctionMeta {
1795 category: "array",
1796 signature: "MAP(array1, [array2, ...], lambda)",
1797 description: "Maps a LAMBDA over one or more arrays",
1798 });
1799 registry.register_lazy("REDUCE", reduce_lazy_fn, FunctionMeta {
1800 category: "array",
1801 signature: "REDUCE(initial_value, array, lambda)",
1802 description: "Reduces an array to a single value using a LAMBDA",
1803 });
1804 registry.register_lazy("SCAN", scan_lazy_fn, FunctionMeta {
1805 category: "array",
1806 signature: "SCAN(initial_value, array, lambda)",
1807 description: "Returns running accumulation using a LAMBDA",
1808 });
1809 registry.register_lazy("MAKEARRAY", makearray_lazy_fn, FunctionMeta {
1810 category: "array",
1811 signature: "MAKEARRAY(rows, cols, lambda)",
1812 description: "Creates an array using a LAMBDA for each cell value",
1813 });
1814 registry.register_lazy("ARRAYFORMULA", arrayformula_lazy_fn, FunctionMeta {
1815 category: "array",
1816 signature: "ARRAYFORMULA(array_formula)",
1817 description: "Evaluates a formula as an array formula",
1818 });
1819}
1820
1821#[cfg(test)]
1822mod tests;