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);
320 let is_error = v.is_error();
321 if ignore == 1 && is_blank { return false; }
322 if ignore == 2 && is_error { return false; }
323 if ignore == 3 && (is_blank || is_error) { return false; }
324 true
325 }).collect();
326
327 let col: Vec<Vec<Value>> = filtered.into_iter().map(|v| vec![v]).collect();
328 from_2d(col)
329}
330
331fn torow_fn(args: &[Value]) -> Value {
337 if let Some(e) = check_arity(args, 1, 3) {
338 return e;
339 }
340 let ignore = if let Some(m) = args.get(1) {
341 match to_f64(m) {
342 Some(n) if (0.0..=3.0).contains(&n) => n as u8,
343 _ => return Value::Error(ErrorKind::Value),
344 }
345 } else {
346 0
347 };
348 let scan_by_col = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
349
350 let flat = if scan_by_col {
351 let grid = to_2d(&args[0]);
353 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
354 let mut out = Vec::new();
355 for c in 0..ncols {
356 for row in &grid {
357 out.push(row[c].clone());
358 }
359 }
360 out
361 } else {
362 flatten_val(&args[0])
363 };
364
365 let filtered: Vec<Value> = flat.into_iter().filter(|v| {
366 let is_blank = matches!(v, Value::Empty);
369 let is_error = v.is_error();
370 if ignore == 1 && is_blank { return false; }
371 if ignore == 2 && is_error { return false; }
372 if ignore == 3 && (is_blank || is_error) { return false; }
373 true
374 }).collect();
375
376 Value::Array(filtered)
377}
378
379fn wrapcols_fn(args: &[Value]) -> Value {
384 if let Some(e) = check_arity(args, 2, 3) {
385 return e;
386 }
387 let flat = flatten_val(&args[0]);
388 let wrap_count = match to_f64(&args[1]) {
389 Some(n) if n >= 1.0 => n as usize,
390 Some(_) => return Value::Error(ErrorKind::Num),
391 None => return Value::Error(ErrorKind::Value),
392 };
393 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
394
395 let ncols = flat.len().div_ceil(wrap_count);
397 let nrows = wrap_count;
398
399 let grid: Vec<Vec<Value>> = (0..nrows)
401 .map(|r| {
402 (0..ncols)
403 .map(|c| {
404 let idx = c * wrap_count + r;
405 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
406 })
407 .collect()
408 })
409 .collect();
410 from_2d(grid)
411}
412
413fn wraprows_fn(args: &[Value]) -> Value {
417 if let Some(e) = check_arity(args, 2, 3) {
418 return e;
419 }
420 let flat = flatten_val(&args[0]);
421 let wrap_count = match to_f64(&args[1]) {
422 Some(n) if n >= 1.0 => n as usize,
423 Some(_) => return Value::Error(ErrorKind::Num),
424 None => return Value::Error(ErrorKind::Value),
425 };
426 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
427
428 let nrows = flat.len().div_ceil(wrap_count);
429 let grid: Vec<Vec<Value>> = (0..nrows)
430 .map(|r| {
431 (0..wrap_count)
432 .map(|c| {
433 let idx = r * wrap_count + c;
434 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
435 })
436 .collect()
437 })
438 .collect();
439 from_2d(grid)
440}
441
442pub(crate) fn sort_fn(args: &[Value]) -> Value {
445 if let Some(e) = check_arity(args, 1, 4) {
446 return e;
447 }
448 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
449
450 if is_1d {
454 let by_col = args.get(3).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
455 if by_col {
456 return Value::Error(ErrorKind::NA);
457 }
458 return args[0].clone();
459 }
460
461 let mut grid = to_2d(&args[0]);
462 let sort_col = if args.len() >= 2 {
463 match to_f64(&args[1]) {
464 Some(n) if n >= 1.0 => n as usize - 1,
465 Some(_) => return Value::Error(ErrorKind::Value),
466 None => 0,
467 }
468 } else {
469 0
470 };
471 let ascending = if args.len() >= 3 {
472 match &args[2] {
473 Value::Number(n) => *n >= 0.0,
474 Value::Bool(b) => *b,
475 _ => true,
476 }
477 } else {
478 true
479 };
480
481 grid.sort_by(|a, b| {
482 let va = a.get(sort_col).unwrap_or(&Value::Empty);
483 let vb = b.get(sort_col).unwrap_or(&Value::Empty);
484 let cmp = compare_values_sort(va, vb);
485 if ascending { cmp } else { cmp.reverse() }
486 });
487 from_2d(grid)
488}
489
490fn compare_values_sort(a: &Value, b: &Value) -> std::cmp::Ordering {
491 match (a, b) {
492 (Value::Number(x), Value::Number(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
493 (Value::Text(x), Value::Text(y)) => x.cmp(y),
494 (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
495 (Value::Zoned(x), Value::Zoned(y)) => x.utc_nanos.cmp(&y.utc_nanos),
497 (Value::Sparkline(_), Value::Sparkline(_)) => std::cmp::Ordering::Equal,
502 _ => std::cmp::Ordering::Equal,
503 }
504}
505
506fn sortby_fn(args: &[Value]) -> Value {
509 if let Some(e) = check_arity(args, 2, usize::MAX) {
510 return e;
511 }
512 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
513
514 if is_1d {
515 let elems = flatten_val(&args[0]);
517 let n = elems.len();
518
519 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
520 let mut i = 1;
521 while i < args.len() {
522 let key_vals = flatten_val(&args[i]);
523 if key_vals.len() != n {
524 return Value::Error(ErrorKind::Value);
525 }
526 let ascending = if i + 1 < args.len() {
527 match to_f64(&args[i + 1]) {
528 Some(v) => v >= 0.0,
529 None => true,
530 }
531 } else {
532 true
533 };
534 sort_keys.push((key_vals, ascending));
535 i += 2;
536 }
537
538 let mut indices: Vec<usize> = (0..n).collect();
539 indices.sort_by(|&ra, &rb| {
540 for (keys, asc) in &sort_keys {
541 let va = keys.get(ra).unwrap_or(&Value::Empty);
542 let vb = keys.get(rb).unwrap_or(&Value::Empty);
543 let cmp = compare_values_sort(va, vb);
544 if cmp != std::cmp::Ordering::Equal {
545 return if *asc { cmp } else { cmp.reverse() };
546 }
547 }
548 std::cmp::Ordering::Equal
549 });
550
551 return Value::Array(indices.iter().map(|&r| elems[r].clone()).collect());
552 }
553
554 let grid = to_2d(&args[0]);
555 let nrows = grid.len();
556
557 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
559 let mut i = 1;
560 while i < args.len() {
561 let key_vals = flatten_val(&args[i]);
562 if key_vals.len() != nrows && nrows > 1 {
563 return Value::Error(ErrorKind::Value);
564 }
565 let ascending = if i + 1 < args.len() {
566 match to_f64(&args[i + 1]) {
567 Some(n) => n >= 0.0,
568 None => true,
569 }
570 } else {
571 true
572 };
573 sort_keys.push((key_vals, ascending));
574 i += 2;
575 }
576
577 let mut indices: Vec<usize> = (0..nrows).collect();
578 indices.sort_by(|&ra, &rb| {
579 for (keys, asc) in &sort_keys {
580 let va = keys.get(ra).unwrap_or(&Value::Empty);
581 let vb = keys.get(rb).unwrap_or(&Value::Empty);
582 let cmp = compare_values_sort(va, vb);
583 if cmp != std::cmp::Ordering::Equal {
584 return if *asc { cmp } else { cmp.reverse() };
585 }
586 }
587 std::cmp::Ordering::Equal
588 });
589
590 let sorted: Vec<Vec<Value>> = indices.iter().map(|&r| grid[r].clone()).collect();
591 drop(grid);
592 from_2d(sorted)
593}
594
595pub(crate) fn unique_fn(args: &[Value]) -> Value {
598 if let Some(e) = check_arity(args, 1, 3) {
599 return e;
600 }
601 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
602 let grid = to_2d(&args[0]);
603 let by_col = args.get(1).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
605 let exactly_once = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
606
607 if is_1d && !by_col {
611 return args[0].clone();
612 }
613
614 if by_col {
615 let nrows = grid.len();
617 if nrows == 0 {
618 return from_2d(vec![]);
619 }
620 let ncols = grid[0].len();
621 let columns: Vec<Vec<Value>> = (0..ncols)
623 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
624 .collect();
625 let mut seen_cols: Vec<Vec<Value>> = Vec::new();
626 let mut counts: Vec<usize> = Vec::new();
627 for col in columns {
628 if let Some(pos) = seen_cols.iter().position(|sc| sc == &col) {
629 counts[pos] += 1;
630 } else {
631 seen_cols.push(col);
632 counts.push(1);
633 }
634 }
635 let result_cols: Vec<Vec<Value>> = seen_cols
636 .into_iter()
637 .zip(counts)
638 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
639 .map(|(col, _)| col)
640 .collect();
641 let ncols2 = result_cols.len();
643 let result: Vec<Vec<Value>> = (0..nrows)
644 .map(|r| (0..ncols2).map(|c| result_cols[c][r].clone()).collect())
645 .collect();
646 return from_2d(result);
647 }
648
649 let mut seen_rows: Vec<Vec<Value>> = Vec::new();
651 let mut counts: Vec<usize> = Vec::new();
652 for row in &grid {
653 if let Some(pos) = seen_rows.iter().position(|sr| sr == row) {
654 counts[pos] += 1;
655 } else {
656 seen_rows.push(row.clone());
657 counts.push(1);
658 }
659 }
660 let result: Vec<Vec<Value>> = seen_rows
661 .into_iter()
662 .zip(counts)
663 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
664 .map(|(row, _)| row)
665 .collect();
666 from_2d(result)
667}
668
669pub(crate) fn sumproduct_fn(args: &[Value]) -> Value {
672 if let Some(e) = check_arity(args, 1, usize::MAX) {
673 return e;
674 }
675 let arrays: Vec<Vec<Value>> = args.iter().map(flatten_val).collect();
676 let len = arrays[0].len();
677 for arr in &arrays[1..] {
679 if arr.len() != len {
680 return Value::Error(ErrorKind::Value);
681 }
682 }
683 let mut sum = 0.0;
684 for i in 0..len {
685 let mut prod = 1.0;
686 for arr in &arrays {
687 prod *= to_f64(&arr[i]).unwrap_or(0.0);
688 }
689 sum += prod;
690 }
691 Value::Number(sum)
692}
693
694fn sumxmy2_fn(args: &[Value]) -> Value {
697 if let Some(e) = check_arity(args, 2, 2) {
698 return e;
699 }
700 let xs = flatten_val(&args[0]);
701 let ys = flatten_val(&args[1]);
702 if xs.len() != ys.len() {
703 return Value::Error(ErrorKind::NA);
704 }
705 let mut sum = 0.0;
706 for (x, y) in xs.iter().zip(ys.iter()) {
707 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
709 sum += (*xn - *yn).powi(2);
710 }
711 }
712 Value::Number(sum)
713}
714
715fn sumx2my2_fn(args: &[Value]) -> Value {
718 if let Some(e) = check_arity(args, 2, 2) {
719 return e;
720 }
721 let xs = flatten_val(&args[0]);
722 let ys = flatten_val(&args[1]);
723 if xs.len() != ys.len() {
724 return Value::Error(ErrorKind::NA);
725 }
726 let mut sum = 0.0;
727 for (x, y) in xs.iter().zip(ys.iter()) {
728 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
729 sum += *xn * *xn - *yn * *yn;
730 }
731 }
732 Value::Number(sum)
733}
734
735fn sumx2py2_fn(args: &[Value]) -> Value {
738 if let Some(e) = check_arity(args, 2, 2) {
739 return e;
740 }
741 let xs = flatten_val(&args[0]);
742 let ys = flatten_val(&args[1]);
743 if xs.len() != ys.len() {
744 return Value::Error(ErrorKind::NA);
745 }
746 let mut sum = 0.0;
747 for (x, y) in xs.iter().zip(ys.iter()) {
748 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
749 sum += *xn * *xn + *yn * *yn;
750 }
751 }
752 Value::Number(sum)
753}
754
755fn mmult_fn(args: &[Value]) -> Value {
758 if let Some(e) = check_arity(args, 2, 2) {
759 return e;
760 }
761 let a = to_2d(&args[0]);
762 let b = to_2d(&args[1]);
763 if a.iter().chain(b.iter()).any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
764 return Value::Error(ErrorKind::Value);
765 }
766 let n = a.first().map(|r| r.len()).unwrap_or(0);
767 let p = b.first().map(|r| r.len()).unwrap_or(0);
768 if b.len() != n {
769 return Value::Error(ErrorKind::Value);
770 }
771 let af: Vec<Vec<f64>> = a.iter().map(|row| {
773 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
774 }).collect();
775 let bf: Vec<Vec<f64>> = b.iter().map(|row| {
776 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
777 }).collect();
778 if af.iter().any(|r| r.iter().any(|v| v.is_nan())) || bf.iter().any(|r| r.iter().any(|v| v.is_nan())) {
779 return Value::Error(ErrorKind::Value);
780 }
781 let result: Vec<Vec<Value>> = af.iter().map(|row_a| {
782 (0..p).map(|j| {
783 let sum: f64 = row_a.iter().enumerate().map(|(k, &av)| av * bf[k][j]).sum();
784 Value::Number(sum)
785 }).collect()
786 }).collect();
787 from_2d(result)
788}
789
790fn mdeterm_fn(args: &[Value]) -> Value {
793 if let Some(e) = check_arity(args, 1, 1) {
794 return e;
795 }
796 let grid = to_2d(&args[0]);
797 let n = grid.len();
798 if n == 0 {
799 return Value::Error(ErrorKind::Value);
800 }
801 for row in &grid {
802 if row.len() != n {
803 return Value::Error(ErrorKind::Value);
804 }
805 }
806 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
807 return Value::Error(ErrorKind::Value);
808 }
809 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
811 for row in &grid {
812 let mut r = Vec::with_capacity(n);
813 for v in row {
814 match to_f64(v) {
815 Some(x) => r.push(x),
816 None => return Value::Error(ErrorKind::Value),
817 }
818 }
819 mat.push(r);
820 }
821 Value::Number(determinant(&mat))
822}
823
824fn determinant(mat: &[Vec<f64>]) -> f64 {
825 let n = mat.len();
826 if n == 1 {
827 return mat[0][0];
828 }
829 if n == 2 {
830 return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
831 }
832 let mut det = 0.0;
833 for c in 0..n {
834 let minor: Vec<Vec<f64>> = (1..n)
835 .map(|r| {
836 (0..n)
837 .filter(|&cc| cc != c)
838 .map(|cc| mat[r][cc])
839 .collect()
840 })
841 .collect();
842 let sign = if c % 2 == 0 { 1.0 } else { -1.0 };
843 det += sign * mat[0][c] * determinant(&minor);
844 }
845 det
846}
847
848fn minverse_fn(args: &[Value]) -> Value {
851 if let Some(e) = check_arity(args, 1, 1) {
852 return e;
853 }
854 let grid = to_2d(&args[0]);
855 let n = grid.len();
856 if n == 0 {
857 return Value::Error(ErrorKind::Value);
858 }
859 for row in &grid {
860 if row.len() != n {
861 return Value::Error(ErrorKind::Value);
862 }
863 }
864 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
865 return Value::Error(ErrorKind::Value);
866 }
867 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
868 for row in &grid {
869 let mut r = Vec::with_capacity(n);
870 for v in row {
871 match to_f64(v) {
872 Some(x) => r.push(x),
873 None => return Value::Error(ErrorKind::Value),
874 }
875 }
876 mat.push(r);
877 }
878 match invert_matrix(mat) {
879 Some(inv) => from_2d(inv.into_iter().map(|r| r.into_iter().map(Value::Number).collect()).collect()),
880 None => Value::Error(ErrorKind::Num),
881 }
882}
883
884fn invert_matrix(mut mat: Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
885 let n = mat.len();
886 let mut inv: Vec<Vec<f64>> = (0..n)
888 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
889 .collect();
890 for col in 0..n {
891 let pivot = (col..n).max_by(|&a, &b| mat[a][col].abs().partial_cmp(&mat[b][col].abs()).unwrap_or(std::cmp::Ordering::Equal))?;
893 if mat[pivot][col].abs() < 1e-12 {
894 return None; }
896 mat.swap(col, pivot);
897 inv.swap(col, pivot);
898 let div = mat[col][col];
899 for j in 0..n {
900 mat[col][j] /= div;
901 inv[col][j] /= div;
902 }
903 for r in 0..n {
904 if r != col {
905 let factor = mat[r][col];
906 for j in 0..n {
907 mat[r][j] -= factor * mat[col][j];
908 inv[r][j] -= factor * inv[col][j];
909 }
910 }
911 }
912 }
913 Some(inv)
914}
915
916fn frequency_fn(args: &[Value]) -> Value {
920 if let Some(e) = check_arity(args, 2, 2) {
921 return e;
922 }
923 let data: Vec<f64> = flatten_val(&args[0])
925 .iter()
926 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
927 .collect();
928 let bins_raw = flatten_val(&args[1]);
929 if bins_raw.is_empty() || matches!(bins_raw.as_slice(), [Value::Empty]) {
931 return Value::Error(ErrorKind::Ref);
932 }
933 let all_empty = bins_raw.iter().all(|v| matches!(v, Value::Empty));
935 if all_empty {
936 return Value::Error(ErrorKind::Ref);
937 }
938 let bins: Vec<f64> = bins_raw
939 .iter()
940 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
941 .collect();
942 if bins.is_empty() {
943 return Value::Error(ErrorKind::Ref);
944 }
945 let mut counts = vec![0i64; bins.len() + 1];
947 for &x in &data {
948 let mut placed = false;
949 for (i, &b) in bins.iter().enumerate() {
950 if x <= b {
951 counts[i] += 1;
952 placed = true;
953 break;
954 }
955 }
956 if !placed {
957 counts[bins.len()] += 1;
958 }
959 }
960 let col: Vec<Vec<Value>> = counts
962 .into_iter()
963 .map(|c| vec![Value::Number(c as f64)])
964 .collect();
965 from_2d(col)
966}
967
968fn linest_fn(args: &[Value]) -> Value {
972 if let Some(e) = check_arity(args, 1, 4) {
973 return e;
974 }
975 let ys = flatten_val(&args[0]);
976 let n = ys.len();
977 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
979 return Value::Error(ErrorKind::Value);
980 }
981 if n < 2 {
982 return Value::Error(ErrorKind::NA);
983 }
984 let xs: Vec<f64> = if args.len() >= 2 {
985 let xv = flatten_val(&args[1]);
986 if xv.len() != n {
987 return Value::Error(ErrorKind::Ref);
988 }
989 xv.iter().filter_map(to_f64).collect()
990 } else {
991 (1..=n).map(|i| i as f64).collect()
992 };
993 if xs.len() != n {
994 return Value::Error(ErrorKind::Ref);
995 }
996 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
997 if y_vals.len() != n {
998 return Value::Error(ErrorKind::Value);
999 }
1000 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1001 Value::Array(vec![Value::Number(slope), Value::Number(intercept)])
1002}
1003
1004fn simple_linear_regression(xs: &[f64], ys: &[f64]) -> (f64, f64) {
1005 let n = xs.len() as f64;
1006 let sum_x: f64 = xs.iter().sum();
1007 let sum_y: f64 = ys.iter().sum();
1008 let sum_xy: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * y).sum();
1009 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1010 let denom = n * sum_xx - sum_x * sum_x;
1011 if denom.abs() < 1e-15 {
1012 let intercept = sum_y / n;
1013 return (0.0, intercept);
1014 }
1015 let slope = (n * sum_xy - sum_x * sum_y) / denom;
1016 let intercept = (sum_y - slope * sum_x) / n;
1017 (slope, intercept)
1018}
1019
1020fn logest_fn(args: &[Value]) -> Value {
1024 if let Some(e) = check_arity(args, 1, 4) {
1025 return e;
1026 }
1027 let ys = flatten_val(&args[0]);
1028 let n = ys.len();
1029 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1031 return Value::Error(ErrorKind::Value);
1032 }
1033 if n < 2 {
1034 return Value::Error(ErrorKind::NA);
1035 }
1036 let xs: Vec<f64> = if args.len() >= 2 {
1037 let xv = flatten_val(&args[1]);
1038 if xv.len() != n {
1039 return Value::Error(ErrorKind::Ref);
1040 }
1041 xv.iter().filter_map(to_f64).collect()
1042 } else {
1043 (1..=n).map(|i| i as f64).collect()
1044 };
1045 if xs.len() != n {
1046 return Value::Error(ErrorKind::Ref);
1047 }
1048 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1049 if y_vals.len() != n {
1050 return Value::Error(ErrorKind::Value);
1051 }
1052 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1054 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1055 return Value::Error(ErrorKind::Num);
1056 }
1057 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
1058 let base = libm::exp(log_base);
1059 let intercept = libm::exp(log_intercept);
1060 Value::Array(vec![Value::Number(base), Value::Number(intercept)])
1061}
1062
1063fn trend_fn(args: &[Value]) -> Value {
1067 if let Some(e) = check_arity(args, 1, 4) {
1068 return e;
1069 }
1070 let ys = flatten_val(&args[0]);
1071 let n = ys.len();
1072 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
1074 return Value::Error(ErrorKind::Value);
1075 }
1076 if n < 2 {
1077 return Value::Error(ErrorKind::NA);
1078 }
1079 let xs: Vec<f64> = if args.len() >= 2 {
1080 let xv = flatten_val(&args[1]);
1081 if xv.len() != n {
1082 return Value::Error(ErrorKind::Ref);
1083 }
1084 xv.iter().filter_map(to_f64).collect()
1085 } else {
1086 (1..=n).map(|i| i as f64).collect()
1087 };
1088 if xs.len() != n {
1089 return Value::Error(ErrorKind::Ref);
1090 }
1091 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1092 if y_vals.len() != n {
1093 return Value::Error(ErrorKind::Value);
1094 }
1095 let new_xs: Vec<f64> = if args.len() >= 3 {
1096 flatten_val(&args[2]).iter().filter_map(to_f64).collect()
1097 } else {
1098 xs.clone()
1099 };
1100 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1101 let result: Vec<Value> = new_xs.iter().map(|&x| Value::Number(slope * x + intercept)).collect();
1102 Value::Array(result)
1103}
1104
1105fn growth_fn(args: &[Value]) -> Value {
1109 if let Some(e) = check_arity(args, 1, 4) {
1110 return e;
1111 }
1112 let ys = flatten_val(&args[0]);
1113 let n = ys.len();
1114 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1116 return Value::Error(ErrorKind::Value);
1117 }
1118 if n < 2 {
1119 return Value::Error(ErrorKind::NA);
1120 }
1121 let xs: Vec<f64> = if args.len() >= 2 {
1122 let xv = flatten_val(&args[1]);
1123 if xv.len() != n {
1124 return Value::Error(ErrorKind::Ref);
1125 }
1126 xv.iter().filter_map(to_f64).collect()
1127 } else {
1128 (1..=n).map(|i| i as f64).collect()
1129 };
1130 if xs.len() != n {
1131 return Value::Error(ErrorKind::Ref);
1132 }
1133 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1134 if y_vals.len() != n {
1135 return Value::Error(ErrorKind::Value);
1136 }
1137 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1138 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1139 return Value::Error(ErrorKind::Num);
1140 }
1141 let new_xs: Vec<f64> = if args.len() >= 3 && !matches!(args[2], Value::Empty) {
1142 let vals: Vec<f64> = flatten_val(&args[2]).iter().filter_map(to_f64).collect();
1143 if vals.is_empty() { xs.clone() } else { vals }
1144 } else {
1145 xs.clone()
1146 };
1147 let use_intercept = if args.len() >= 4 {
1150 match &args[3] {
1151 Value::Bool(b) => *b,
1152 Value::Number(n) => *n != 0.0,
1153 _ => true,
1154 }
1155 } else {
1156 true
1157 };
1158 let (log_base, log_intercept) = if use_intercept {
1159 simple_linear_regression(&xs, &log_y)
1160 } else {
1161 let sum_xy: f64 = xs.iter().zip(log_y.iter()).map(|(x, ly)| x * ly).sum();
1163 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1164 let slope = if sum_xx.abs() < 1e-15 { 0.0 } else { sum_xy / sum_xx };
1165 (slope, 0.0)
1166 };
1167 let result: Vec<Value> = new_xs
1168 .iter()
1169 .map(|&x| Value::Number(libm::exp(log_base * x + log_intercept)))
1170 .collect();
1171 Value::Array(result)
1172}
1173
1174fn apply_lambda(lambda_expr: &Expr, bound_args: &[Value], ctx: &mut EvalCtx<'_>) -> Option<Value> {
1186 match lambda_expr {
1187 Expr::FunctionCall { name, args, .. } if name == "LAMBDA" => {
1188 if args.is_empty() {
1189 return None;
1190 }
1191 let body = &args[args.len() - 1];
1192 let params = &args[..args.len() - 1];
1193 if params.len() != bound_args.len() {
1194 return None;
1195 }
1196 let mut saved: Vec<(String, Value)> = Vec::new();
1198 for (param_expr, val) in params.iter().zip(bound_args.iter()) {
1199 if let Expr::Variable(name, span) = param_expr {
1200 if let Some(hook) = ctx.hook.as_deref_mut() {
1210 hook.on_node(EvalOp::Variable(name), *span, val);
1211 }
1212 let bind_key = name.to_uppercase().replace('$', "");
1220 let old = ctx.ctx.get(&bind_key);
1221 saved.push((bind_key.clone(), old));
1222 ctx.ctx.set(bind_key, val.clone());
1223 } else {
1224 return None;
1225 }
1226 }
1227 let result = evaluate_expr(body, ctx);
1228 for (name, old_val) in saved {
1230 ctx.ctx.set(name, old_val);
1231 }
1232 Some(result)
1233 }
1234 _ => None,
1235 }
1236}
1237
1238pub fn byrow_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1241 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1242 return e;
1243 }
1244 let arr_val = evaluate_expr(&args[0], ctx);
1245 if arr_val.is_error() {
1246 return arr_val;
1247 }
1248 let grid = to_2d(&arr_val);
1249 let lambda_expr = &args[1];
1250 let mut results: Vec<Value> = Vec::with_capacity(grid.len());
1251 for row in &grid {
1252 let row_val = Value::Array(row.clone());
1253 match apply_lambda(lambda_expr, &[row_val], ctx) {
1254 Some(v) => results.push(v),
1255 None => return Value::Error(ErrorKind::NA),
1256 }
1257 }
1258 let col: Vec<Vec<Value>> = results.into_iter().map(|v| vec![v]).collect();
1260 from_2d(col)
1261}
1262
1263pub fn bycol_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1266 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1267 return e;
1268 }
1269 let arr_val = evaluate_expr(&args[0], ctx);
1270 if arr_val.is_error() {
1271 return arr_val;
1272 }
1273 let grid = to_2d(&arr_val);
1274 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
1275 let columns: Vec<Vec<Value>> = (0..ncols)
1277 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
1278 .collect();
1279 let lambda_expr = &args[1];
1280 let mut results: Vec<Value> = Vec::with_capacity(ncols);
1281 for col in columns {
1282 let col_val = Value::Array(col);
1284 match apply_lambda(lambda_expr, &[col_val], ctx) {
1285 Some(v) => results.push(v),
1286 None => return Value::Error(ErrorKind::NA),
1287 }
1288 }
1289 Value::Array(results)
1291}
1292
1293pub fn map_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1296 if let Some(e) = check_arity_len(args.len(), 2, usize::MAX) {
1297 return e;
1298 }
1299 let lambda_expr = &args[args.len() - 1];
1301 let arr_count = args.len() - 1;
1302 let mut first_shape_val: Option<Value> = None;
1310 let mut arrays: Vec<Vec<Value>> = Vec::with_capacity(arr_count);
1311 for (i, a) in args[..arr_count].iter().enumerate() {
1312 let v = evaluate_expr(a, ctx);
1313 if v.is_error() {
1314 return v;
1315 }
1316 if i == 0 {
1317 first_shape_val = Some(v.clone());
1318 }
1319 arrays.push(flatten_val(&v));
1320 }
1321 let len = arrays[0].len();
1322 for arr in &arrays[1..] {
1323 if arr.len() != len {
1324 return Value::Error(ErrorKind::Value);
1325 }
1326 }
1327 let mut results: Vec<Value> = Vec::with_capacity(len);
1328 for i in 0..len {
1329 let bound: Vec<Value> = arrays.iter().map(|a| a[i].clone()).collect();
1330 match apply_lambda(lambda_expr, &bound, ctx) {
1331 Some(v) => results.push(v),
1332 None => return Value::Error(ErrorKind::NA),
1333 }
1334 }
1335 let first_grid = to_2d(
1337 first_shape_val
1338 .as_ref()
1339 .expect("arr_count >= 1 (check_arity_len enforces at least 2 args)"),
1340 );
1341 if first_grid.len() > 1 {
1342 let ncols = first_grid[0].len();
1344 let nrows = first_grid.len();
1345 let grid: Vec<Vec<Value>> = (0..nrows)
1346 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1347 .collect();
1348 from_2d(grid)
1349 } else {
1350 Value::Array(results)
1351 }
1352}
1353
1354pub fn reduce_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1357 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1358 return e;
1359 }
1360 let initial = evaluate_expr(&args[0], ctx);
1361 if initial.is_error() {
1362 return initial;
1363 }
1364 let arr_val = evaluate_expr(&args[1], ctx);
1365 if arr_val.is_error() {
1366 return arr_val;
1367 }
1368 let items = flatten_val(&arr_val);
1369 if items.is_empty() {
1370 return Value::Error(ErrorKind::Ref);
1371 }
1372 let lambda_expr = &args[2];
1373 let mut acc = initial;
1374 for item in &items {
1375 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1376 Some(v) => acc = v,
1377 None => return Value::Error(ErrorKind::NA),
1378 }
1379 }
1380 acc
1381}
1382
1383pub fn scan_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1386 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1387 return e;
1388 }
1389 let initial = evaluate_expr(&args[0], ctx);
1390 if initial.is_error() {
1391 return initial;
1392 }
1393 let arr_val = evaluate_expr(&args[1], ctx);
1394 if arr_val.is_error() {
1395 return arr_val;
1396 }
1397 let grid = to_2d(&arr_val);
1398 let items = flatten_val(&arr_val);
1399 let lambda_expr = &args[2];
1400 let mut acc = initial;
1401 let mut results: Vec<Value> = Vec::with_capacity(items.len());
1402 for item in &items {
1403 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1404 Some(v) => {
1405 acc = v.clone();
1406 results.push(v);
1407 }
1408 None => return Value::Error(ErrorKind::NA),
1409 }
1410 }
1411 if grid.len() > 1 {
1413 let ncols = grid[0].len();
1414 let nrows = grid.len();
1415 let result_grid: Vec<Vec<Value>> = (0..nrows)
1416 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1417 .collect();
1418 from_2d(result_grid)
1419 } else {
1420 Value::Array(results)
1421 }
1422}
1423
1424pub fn makearray_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1427 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1428 return e;
1429 }
1430 let rows_val = evaluate_expr(&args[0], ctx);
1431 let cols_val = evaluate_expr(&args[1], ctx);
1432 if rows_val.is_error() {
1433 return rows_val;
1434 }
1435 if cols_val.is_error() {
1436 return cols_val;
1437 }
1438 let nrows = match to_f64(&rows_val) {
1439 Some(n) if n >= 1.0 => n as usize,
1440 _ => return Value::Error(ErrorKind::Value),
1441 };
1442 let ncols = match to_f64(&cols_val) {
1443 Some(n) if n >= 1.0 => n as usize,
1444 _ => return Value::Error(ErrorKind::Value),
1445 };
1446 let lambda_expr = &args[2];
1447 let mut grid: Vec<Vec<Value>> = Vec::with_capacity(nrows);
1448 for r in 1..=nrows {
1449 let mut row = Vec::with_capacity(ncols);
1450 for c in 1..=ncols {
1451 let rv = Value::Number(r as f64);
1452 let cv = Value::Number(c as f64);
1453 match apply_lambda(lambda_expr, &[rv, cv], ctx) {
1454 Some(Value::Array(_)) => return Value::Error(ErrorKind::Value),
1455 Some(v) => row.push(v),
1456 None => return Value::Error(ErrorKind::NA),
1457 }
1458 }
1459 grid.push(row);
1460 }
1461 if nrows == 1 && ncols == 1 {
1462 return grid[0][0].clone();
1463 }
1464 from_2d(grid)
1465}
1466
1467pub fn arrayformula_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1480 if args.len() != 1 {
1481 return Value::Error(ErrorKind::NA);
1482 }
1483 broadcast_expr(&args[0], ctx)
1484}
1485
1486fn broadcast_expr(expr: &Expr, ctx: &mut EvalCtx<'_>) -> Value {
1487 match expr {
1488 Expr::FunctionCall { name, args: if_args, .. }
1493 if name == "IF" && (if_args.len() == 2 || if_args.len() == 3) =>
1494 {
1495 let cond = evaluate_expr(&if_args[0], ctx);
1496 if !matches!(cond, Value::Array(_)) {
1497 return evaluate_expr(expr, ctx);
1498 }
1499 let true_val = evaluate_expr(&if_args[1], ctx);
1500 let false_val = if if_args.len() == 3 {
1501 evaluate_expr(&if_args[2], ctx)
1502 } else {
1503 Value::Bool(false)
1504 };
1505 broadcast_if(&cond, &true_val, &false_val)
1506 }
1507 Expr::FunctionCall { name, args: inner_args, .. }
1513 if name == "ISNUMBER" && inner_args.len() == 1 =>
1514 {
1515 let v = evaluate_expr(&inner_args[0], ctx);
1516 if v.is_error() {
1517 return v;
1518 }
1519 if matches!(v, Value::Array(_)) {
1520 broadcast_eager(super::logical::is_checks::isnumber_fn, &[v])
1521 } else {
1522 super::logical::is_checks::isnumber_fn(&[v])
1523 }
1524 }
1525 Expr::FunctionCall { name, args: inner_args, .. }
1533 if matches!(name.as_str(), "LEN" | "UPPER") =>
1534 {
1535 match ctx.registry.get(name) {
1536 Some(FunctionKind::Eager(f)) => {
1537 let f: EagerFn = *f;
1538 let mut evaluated = Vec::with_capacity(inner_args.len());
1539 for a in inner_args {
1540 let v = evaluate_expr(a, ctx);
1541 if v.is_error() {
1542 return v;
1543 }
1544 evaluated.push(v);
1545 }
1546 if evaluated.iter().any(|v| matches!(v, Value::Array(_))) {
1547 broadcast_eager(f, &evaluated)
1548 } else {
1549 f(&evaluated)
1550 }
1551 }
1552 _ => evaluate_expr(expr, ctx),
1553 }
1554 }
1555 _ => evaluate_expr(expr, ctx),
1556 }
1557}
1558
1559fn broadcast_shape(values: &[Value]) -> Option<(usize, usize)> {
1562 let mut shape = None;
1563 for v in values {
1564 if matches!(v, Value::Array(_)) {
1565 let grid = to_2d(v);
1566 let nr = grid.len();
1567 let nc = grid.first().map(Vec::len).unwrap_or(0);
1568 match shape {
1569 None => shape = Some((nr, nc)),
1570 Some((r, c)) if r == nr && c == nc => {}
1571 Some(_) => return None,
1572 }
1573 }
1574 }
1575 shape
1576}
1577
1578fn broadcast_eager(f: EagerFn, evaluated: &[Value]) -> Value {
1581 let (nrows, ncols) = match broadcast_shape(evaluated) {
1582 Some(s) => s,
1583 None => return Value::Error(ErrorKind::Value),
1584 };
1585 let grids: Vec<Option<Vec<Vec<Value>>>> = evaluated
1586 .iter()
1587 .map(|v| matches!(v, Value::Array(_)).then(|| to_2d(v)))
1588 .collect();
1589 let mut out = Vec::with_capacity(nrows);
1590 for r in 0..nrows {
1591 let mut row = Vec::with_capacity(ncols);
1592 for c in 0..ncols {
1593 let per_pos: Vec<Value> = evaluated
1594 .iter()
1595 .enumerate()
1596 .map(|(i, v)| match &grids[i] {
1597 Some(g) => g[r][c].clone(),
1598 None => v.clone(),
1599 })
1600 .collect();
1601 row.push(f(&per_pos));
1602 }
1603 out.push(row);
1604 }
1605 from_2d(out)
1606}
1607
1608fn broadcast_if(cond: &Value, true_val: &Value, false_val: &Value) -> Value {
1612 let cond_grid = to_2d(cond);
1613 let true_grid = matches!(true_val, Value::Array(_)).then(|| to_2d(true_val));
1614 let false_grid = matches!(false_val, Value::Array(_)).then(|| to_2d(false_val));
1615 let nrows = cond_grid.len();
1616 let ncols = cond_grid.first().map(Vec::len).unwrap_or(0);
1617 let mut out = Vec::with_capacity(nrows);
1618 for (r, cond_row) in cond_grid.iter().enumerate() {
1619 let mut row = Vec::with_capacity(ncols);
1620 for (c, cond_cell) in cond_row.iter().enumerate() {
1621 let branch_val = match to_bool(cond_cell.clone()) {
1622 Ok(true) => match &true_grid {
1623 Some(g) => g
1624 .get(r)
1625 .and_then(|row| row.get(c))
1626 .cloned()
1627 .unwrap_or(Value::Error(ErrorKind::Value)),
1628 None => true_val.clone(),
1629 },
1630 Ok(false) => match &false_grid {
1631 Some(g) => g
1632 .get(r)
1633 .and_then(|row| row.get(c))
1634 .cloned()
1635 .unwrap_or(Value::Error(ErrorKind::Value)),
1636 None => false_val.clone(),
1637 },
1638 Err(e) => e,
1639 };
1640 row.push(branch_val);
1641 }
1642 out.push(row);
1643 }
1644 from_2d(out)
1645}
1646
1647pub fn register_array(registry: &mut Registry) {
1648 registry.register_eager("ROWS", rows_fn, FunctionMeta {
1649 category: "array",
1650 signature: "ROWS(array)",
1651 description: "Returns the number of rows in an array or range",
1652 });
1653 registry.register_eager("COLUMNS", columns_fn, FunctionMeta {
1654 category: "array",
1655 signature: "COLUMNS(array)",
1656 description: "Returns the number of columns in an array or range",
1657 });
1658 registry.register_eager("TRANSPOSE", transpose_fn, FunctionMeta {
1659 category: "array",
1660 signature: "TRANSPOSE(array)",
1661 description: "Transposes the rows and columns of an array",
1662 });
1663 registry.register_eager("ARRAY_CONSTRAIN", array_constrain_fn, FunctionMeta {
1664 category: "array",
1665 signature: "ARRAY_CONSTRAIN(input, num_rows, num_cols)",
1666 description: "Constrains an array to a given number of rows and columns",
1667 });
1668 registry.register_eager("CHOOSECOLS", choosecols_fn, FunctionMeta {
1669 category: "array",
1670 signature: "CHOOSECOLS(array, col_num1, ...)",
1671 description: "Returns selected columns from an array",
1672 });
1673 registry.register_eager("CHOOSEROWS", chooserows_fn, FunctionMeta {
1674 category: "array",
1675 signature: "CHOOSEROWS(array, row_num1, ...)",
1676 description: "Returns selected rows from an array",
1677 });
1678 registry.register_eager("FLATTEN", flatten_fn, FunctionMeta {
1679 category: "array",
1680 signature: "FLATTEN(array)",
1681 description: "Flattens an array into a single column",
1682 });
1683 registry.register_eager("HSTACK", hstack_fn, FunctionMeta {
1684 category: "array",
1685 signature: "HSTACK(array1, ...)",
1686 description: "Horizontally stacks arrays",
1687 });
1688 registry.register_eager("VSTACK", vstack_fn, FunctionMeta {
1689 category: "array",
1690 signature: "VSTACK(array1, ...)",
1691 description: "Vertically stacks arrays",
1692 });
1693 registry.register_eager("TOCOL", tocol_fn, FunctionMeta {
1694 category: "array",
1695 signature: "TOCOL(array, [ignore], [scan_by_col])",
1696 description: "Converts an array to a single column",
1697 });
1698 registry.register_eager("TOROW", torow_fn, FunctionMeta {
1699 category: "array",
1700 signature: "TOROW(array, [ignore], [scan_by_col])",
1701 description: "Converts an array to a single row",
1702 });
1703 registry.register_eager("WRAPCOLS", wrapcols_fn, FunctionMeta {
1704 category: "array",
1705 signature: "WRAPCOLS(vector, wrap_count, [pad_with])",
1706 description: "Wraps a vector into columns of the given length",
1707 });
1708 registry.register_eager("WRAPROWS", wraprows_fn, FunctionMeta {
1709 category: "array",
1710 signature: "WRAPROWS(vector, wrap_count, [pad_with])",
1711 description: "Wraps a vector into rows of the given length",
1712 });
1713 registry.register_eager("SORT", sort_fn, FunctionMeta {
1714 category: "array",
1715 signature: "SORT(array, [sort_index], [sort_order], [by_col])",
1716 description: "Sorts an array",
1717 });
1718 registry.register_eager("SORTBY", sortby_fn, FunctionMeta {
1719 category: "array",
1720 signature: "SORTBY(array, by_array1, [sort_order1], ...)",
1721 description: "Sorts an array based on the values in corresponding arrays",
1722 });
1723 registry.register_eager("UNIQUE", unique_fn, FunctionMeta {
1724 category: "array",
1725 signature: "UNIQUE(array, [by_col], [exactly_once])",
1726 description: "Returns unique rows or columns from an array",
1727 });
1728 registry.register_eager("SUMPRODUCT", sumproduct_fn, FunctionMeta {
1729 category: "array",
1730 signature: "SUMPRODUCT(array1, [array2], ...)",
1731 description: "Returns the sum of products of corresponding elements",
1732 });
1733 registry.register_eager("SUMXMY2", sumxmy2_fn, FunctionMeta {
1734 category: "array",
1735 signature: "SUMXMY2(array_x, array_y)",
1736 description: "Returns sum of squares of differences",
1737 });
1738 registry.register_eager("SUMX2MY2", sumx2my2_fn, FunctionMeta {
1739 category: "array",
1740 signature: "SUMX2MY2(array_x, array_y)",
1741 description: "Returns sum of (x^2 - y^2)",
1742 });
1743 registry.register_eager("SUMX2PY2", sumx2py2_fn, FunctionMeta {
1744 category: "array",
1745 signature: "SUMX2PY2(array_x, array_y)",
1746 description: "Returns sum of (x^2 + y^2)",
1747 });
1748 registry.register_eager("MMULT", mmult_fn, FunctionMeta {
1749 category: "array",
1750 signature: "MMULT(array1, array2)",
1751 description: "Returns the matrix product of two arrays",
1752 });
1753 registry.register_eager("MDETERM", mdeterm_fn, FunctionMeta {
1754 category: "array",
1755 signature: "MDETERM(array)",
1756 description: "Returns the matrix determinant",
1757 });
1758 registry.register_eager("MINVERSE", minverse_fn, FunctionMeta {
1759 category: "array",
1760 signature: "MINVERSE(array)",
1761 description: "Returns the matrix inverse",
1762 });
1763 registry.register_eager("FREQUENCY", frequency_fn, FunctionMeta {
1764 category: "array",
1765 signature: "FREQUENCY(data, bins)",
1766 description: "Calculates the frequency distribution of values",
1767 });
1768 registry.register_eager("LINEST", linest_fn, FunctionMeta {
1769 category: "array",
1770 signature: "LINEST(known_y, [known_x], [const], [stats])",
1771 description: "Returns linear regression statistics",
1772 });
1773 registry.register_eager("LOGEST", logest_fn, FunctionMeta {
1774 category: "array",
1775 signature: "LOGEST(known_y, [known_x], [const], [stats])",
1776 description: "Returns exponential regression statistics",
1777 });
1778 registry.register_eager("TREND", trend_fn, FunctionMeta {
1779 category: "array",
1780 signature: "TREND(known_y, [known_x], [new_x], [const])",
1781 description: "Returns values along a linear trend",
1782 });
1783 registry.register_eager("GROWTH", growth_fn, FunctionMeta {
1784 category: "array",
1785 signature: "GROWTH(known_y, [known_x], [new_x], [const])",
1786 description: "Returns values along an exponential trend",
1787 });
1788 registry.register_lazy("BYROW", byrow_lazy_fn, FunctionMeta {
1789 category: "array",
1790 signature: "BYROW(array, lambda)",
1791 description: "Applies a LAMBDA to each row of an array",
1792 });
1793 registry.register_lazy("BYCOL", bycol_lazy_fn, FunctionMeta {
1794 category: "array",
1795 signature: "BYCOL(array, lambda)",
1796 description: "Applies a LAMBDA to each column of an array",
1797 });
1798 registry.register_lazy("MAP", map_lazy_fn, FunctionMeta {
1799 category: "array",
1800 signature: "MAP(array1, [array2, ...], lambda)",
1801 description: "Maps a LAMBDA over one or more arrays",
1802 });
1803 registry.register_lazy("REDUCE", reduce_lazy_fn, FunctionMeta {
1804 category: "array",
1805 signature: "REDUCE(initial_value, array, lambda)",
1806 description: "Reduces an array to a single value using a LAMBDA",
1807 });
1808 registry.register_lazy("SCAN", scan_lazy_fn, FunctionMeta {
1809 category: "array",
1810 signature: "SCAN(initial_value, array, lambda)",
1811 description: "Returns running accumulation using a LAMBDA",
1812 });
1813 registry.register_lazy("MAKEARRAY", makearray_lazy_fn, FunctionMeta {
1814 category: "array",
1815 signature: "MAKEARRAY(rows, cols, lambda)",
1816 description: "Creates an array using a LAMBDA for each cell value",
1817 });
1818 registry.register_lazy("ARRAYFORMULA", arrayformula_lazy_fn, FunctionMeta {
1819 category: "array",
1820 signature: "ARRAYFORMULA(array_formula)",
1821 description: "Evaluates a formula as an array formula",
1822 });
1823}
1824
1825#[cfg(test)]
1826mod tests;