1use crate::eval::{evaluate_expr, EvalCtx};
4use crate::parser::ast::Expr;
5use crate::types::{ErrorKind, Value};
6
7use super::{check_arity, check_arity_len, FunctionMeta, Registry};
8
9pub fn to_2d(v: &Value) -> Vec<Vec<Value>> {
16 match v {
17 Value::Array(outer) => {
18 if outer.iter().any(|e| matches!(e, Value::Array(_))) {
19 outer
20 .iter()
21 .map(|row| match row {
22 Value::Array(cols) => cols.clone(),
23 other => vec![other.clone()],
24 })
25 .collect()
26 } else {
27 vec![outer.clone()] }
29 }
30 other => vec![vec![other.clone()]], }
32}
33
34pub fn from_2d(rows: Vec<Vec<Value>>) -> Value {
39 if rows.is_empty() {
40 return Value::Array(vec![]);
41 }
42 if rows.len() == 1 {
43 return Value::Array(rows.into_iter().next().unwrap());
44 }
45 Value::Array(rows.into_iter().map(Value::Array).collect())
46}
47
48pub fn flatten_val(v: &Value) -> Vec<Value> {
50 match v {
51 Value::Array(outer) => {
52 if outer.iter().any(|e| matches!(e, Value::Array(_))) {
53 outer
54 .iter()
55 .flat_map(|row| match row {
56 Value::Array(cols) => cols.clone(),
57 other => vec![other.clone()],
58 })
59 .collect()
60 } else {
61 outer.clone()
62 }
63 }
64 other => vec![other.clone()],
65 }
66}
67
68fn to_f64(v: &Value) -> Option<f64> {
70 match v {
71 Value::Number(n) => Some(*n),
72 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
73 _ => None,
74 }
75}
76
77
78
79pub(crate) fn rows_fn(args: &[Value]) -> Value {
82 if let Some(e) = check_arity(args, 1, 1) {
83 return e;
84 }
85 let grid = to_2d(&args[0]);
86 Value::Number(grid.len() as f64)
87}
88
89pub(crate) fn columns_fn(args: &[Value]) -> Value {
92 if let Some(e) = check_arity(args, 1, 1) {
93 return e;
94 }
95 let grid = to_2d(&args[0]);
96 let cols = grid.first().map(|r| r.len()).unwrap_or(0);
97 Value::Number(cols as f64)
98}
99
100pub(crate) fn transpose_fn(args: &[Value]) -> Value {
103 if let Some(e) = check_arity(args, 1, 1) {
104 return e;
105 }
106 let grid = to_2d(&args[0]);
107 if grid.is_empty() {
108 return Value::Array(vec![]);
109 }
110 let nrows = grid.len();
111 let ncols = grid[0].len();
112 let transposed: Vec<Vec<Value>> = (0..ncols)
113 .map(|c| (0..nrows).map(|r| grid[r][c].clone()).collect())
114 .collect();
115 from_2d(transposed)
116}
117
118pub(crate) fn array_constrain_fn(args: &[Value]) -> Value {
121 if let Some(e) = check_arity(args, 3, 3) {
122 return e;
123 }
124 let grid = to_2d(&args[0]);
125 let num_rows = match to_f64(&args[1]) {
126 Some(n) if n >= 1.0 => n as usize,
127 Some(n) if n < 0.0 => return Value::Error(ErrorKind::Num),
128 Some(_) => return Value::Error(ErrorKind::Ref),
129 None => return Value::Error(ErrorKind::Value),
130 };
131 let num_cols = match to_f64(&args[2]) {
132 Some(n) if n >= 1.0 => n as usize,
133 Some(n) if n < 0.0 => return Value::Error(ErrorKind::Num),
134 Some(_) => return Value::Error(ErrorKind::Ref),
135 None => return Value::Error(ErrorKind::Value),
136 };
137 let rows_to_take = num_rows.min(grid.len());
138 let result: Vec<Vec<Value>> = grid[..rows_to_take]
139 .iter()
140 .map(|row| {
141 let cols_to_take = num_cols.min(row.len());
142 row[..cols_to_take].to_vec()
143 })
144 .collect();
145 from_2d(result)
146}
147
148fn choosecols_fn(args: &[Value]) -> Value {
151 if let Some(e) = check_arity(args, 2, usize::MAX) {
152 return e;
153 }
154 let grid = to_2d(&args[0]);
155 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
156 let mut selected_cols: Vec<usize> = Vec::new();
157 for col_arg in &args[1..] {
158 match to_f64(col_arg) {
159 Some(0.0) => return Value::Error(ErrorKind::Value),
160 Some(n) => {
161 let idx = if n < 0.0 {
162 let i = (ncols as isize + n as isize) as usize;
163 if n as isize + (ncols as isize) < 0 {
164 return Value::Error(ErrorKind::Value);
165 }
166 i
167 } else {
168 let i = n as usize - 1;
169 if i >= ncols {
170 return Value::Error(ErrorKind::Value);
171 }
172 i
173 };
174 selected_cols.push(idx);
175 }
176 None => return Value::Error(ErrorKind::Value),
177 }
178 }
179 let result: Vec<Vec<Value>> = grid
180 .iter()
181 .map(|row| {
182 selected_cols
183 .iter()
184 .map(|&c| row.get(c).cloned().unwrap_or(Value::Empty))
185 .collect()
186 })
187 .collect();
188 from_2d(result)
189}
190
191fn chooserows_fn(args: &[Value]) -> Value {
194 if let Some(e) = check_arity(args, 2, usize::MAX) {
195 return e;
196 }
197 let grid = to_2d(&args[0]);
198 let nrows = grid.len();
199 let mut selected_rows: Vec<usize> = Vec::new();
200 for row_arg in &args[1..] {
201 match to_f64(row_arg) {
202 Some(0.0) => return Value::Error(ErrorKind::Value),
203 Some(n) => {
204 let idx = if n < 0.0 {
205 let i = (nrows as isize + n as isize) as usize;
206 if n as isize + (nrows as isize) < 0 {
207 return Value::Error(ErrorKind::Value);
208 }
209 i
210 } else {
211 let i = n as usize - 1;
212 if i >= nrows {
213 return Value::Error(ErrorKind::Value);
214 }
215 i
216 };
217 selected_rows.push(idx);
218 }
219 None => return Value::Error(ErrorKind::Value),
220 }
221 }
222 let result: Vec<Vec<Value>> = selected_rows
223 .iter()
224 .map(|&r| grid.get(r).cloned().unwrap_or_default())
225 .collect();
226 from_2d(result)
227}
228
229pub(crate) fn flatten_fn(args: &[Value]) -> Value {
234 if let Some(e) = check_arity(args, 1, usize::MAX) {
235 return e;
236 }
237 let mut flat: Vec<Value> = Vec::new();
238 for arg in args {
239 flat.extend(flatten_val(arg));
240 }
241 let col: Vec<Vec<Value>> = flat.into_iter().map(|v| vec![v]).collect();
243 from_2d(col)
244}
245
246fn hstack_fn(args: &[Value]) -> Value {
249 if let Some(e) = check_arity(args, 1, usize::MAX) {
250 return e;
251 }
252 let grids: Vec<Vec<Vec<Value>>> = args.iter().map(to_2d).collect();
253 let nrows = grids.iter().map(|g| g.len()).max().unwrap_or(0);
254 let result: Vec<Vec<Value>> = (0..nrows)
255 .map(|r| {
256 grids
257 .iter()
258 .flat_map(|g| {
259 g.get(r).cloned().unwrap_or_default()
260 })
261 .collect()
262 })
263 .collect();
264 from_2d(result)
265}
266
267fn vstack_fn(args: &[Value]) -> Value {
270 if let Some(e) = check_arity(args, 1, usize::MAX) {
271 return e;
272 }
273 let mut result: Vec<Vec<Value>> = Vec::new();
274 for arg in args {
275 let grid = to_2d(arg);
276 result.extend(grid);
277 }
278 from_2d(result)
279}
280
281fn tocol_fn(args: &[Value]) -> Value {
287 if let Some(e) = check_arity(args, 1, 3) {
288 return e;
289 }
290 let ignore = if let Some(m) = args.get(1) {
291 match to_f64(m) {
292 Some(n) if (0.0..=3.0).contains(&n) => n as u8,
293 _ => return Value::Error(ErrorKind::Value),
294 }
295 } else {
296 0
297 };
298 let scan_by_col = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
299
300 let flat = if scan_by_col {
301 let grid = to_2d(&args[0]);
303 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
304 let mut out = Vec::new();
305 for c in 0..ncols {
306 for row in &grid {
307 out.push(row[c].clone());
308 }
309 }
310 out
311 } else {
312 flatten_val(&args[0])
313 };
314
315 let filtered: Vec<Value> = flat.into_iter().filter(|v| {
316 let is_blank = matches!(v, Value::Empty) || matches!(v, Value::Text(s) if s.is_empty());
317 let is_error = matches!(v, Value::Error(_));
318 if ignore == 1 && is_blank { return false; }
319 if ignore == 2 && is_error { return false; }
320 if ignore == 3 && (is_blank || is_error) { return false; }
321 true
322 }).collect();
323
324 let col: Vec<Vec<Value>> = filtered.into_iter().map(|v| vec![v]).collect();
325 from_2d(col)
326}
327
328fn torow_fn(args: &[Value]) -> Value {
334 if let Some(e) = check_arity(args, 1, 3) {
335 return e;
336 }
337 let ignore = if let Some(m) = args.get(1) {
338 match to_f64(m) {
339 Some(n) if (0.0..=3.0).contains(&n) => n as u8,
340 _ => return Value::Error(ErrorKind::Value),
341 }
342 } else {
343 0
344 };
345 let scan_by_col = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
346
347 let flat = if scan_by_col {
348 let grid = to_2d(&args[0]);
350 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
351 let mut out = Vec::new();
352 for c in 0..ncols {
353 for row in &grid {
354 out.push(row[c].clone());
355 }
356 }
357 out
358 } else {
359 flatten_val(&args[0])
360 };
361
362 let filtered: Vec<Value> = flat.into_iter().filter(|v| {
363 let is_blank = matches!(v, Value::Empty) || matches!(v, Value::Text(s) if s.is_empty());
364 let is_error = matches!(v, Value::Error(_));
365 if ignore == 1 && is_blank { return false; }
366 if ignore == 2 && is_error { return false; }
367 if ignore == 3 && (is_blank || is_error) { return false; }
368 true
369 }).collect();
370
371 Value::Array(filtered)
372}
373
374fn wrapcols_fn(args: &[Value]) -> Value {
379 if let Some(e) = check_arity(args, 2, 3) {
380 return e;
381 }
382 let flat = flatten_val(&args[0]);
383 let wrap_count = match to_f64(&args[1]) {
384 Some(n) if n >= 1.0 => n as usize,
385 Some(_) => return Value::Error(ErrorKind::Num),
386 None => return Value::Error(ErrorKind::Value),
387 };
388 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
389
390 let ncols = flat.len().div_ceil(wrap_count);
392 let nrows = wrap_count;
393
394 let grid: Vec<Vec<Value>> = (0..nrows)
396 .map(|r| {
397 (0..ncols)
398 .map(|c| {
399 let idx = c * wrap_count + r;
400 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
401 })
402 .collect()
403 })
404 .collect();
405 from_2d(grid)
406}
407
408fn wraprows_fn(args: &[Value]) -> Value {
412 if let Some(e) = check_arity(args, 2, 3) {
413 return e;
414 }
415 let flat = flatten_val(&args[0]);
416 let wrap_count = match to_f64(&args[1]) {
417 Some(n) if n >= 1.0 => n as usize,
418 Some(_) => return Value::Error(ErrorKind::Num),
419 None => return Value::Error(ErrorKind::Value),
420 };
421 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
422
423 let nrows = flat.len().div_ceil(wrap_count);
424 let grid: Vec<Vec<Value>> = (0..nrows)
425 .map(|r| {
426 (0..wrap_count)
427 .map(|c| {
428 let idx = r * wrap_count + c;
429 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
430 })
431 .collect()
432 })
433 .collect();
434 from_2d(grid)
435}
436
437pub(crate) fn sort_fn(args: &[Value]) -> Value {
440 if let Some(e) = check_arity(args, 1, 4) {
441 return e;
442 }
443 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
444
445 if is_1d {
449 let by_col = args.get(3).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
450 if by_col {
451 return Value::Error(ErrorKind::NA);
452 }
453 return args[0].clone();
454 }
455
456 let mut grid = to_2d(&args[0]);
457 let sort_col = if args.len() >= 2 {
458 match to_f64(&args[1]) {
459 Some(n) if n >= 1.0 => n as usize - 1,
460 Some(_) => return Value::Error(ErrorKind::Value),
461 None => 0,
462 }
463 } else {
464 0
465 };
466 let ascending = if args.len() >= 3 {
467 match &args[2] {
468 Value::Number(n) => *n >= 0.0,
469 Value::Bool(b) => *b,
470 _ => true,
471 }
472 } else {
473 true
474 };
475
476 grid.sort_by(|a, b| {
477 let va = a.get(sort_col).unwrap_or(&Value::Empty);
478 let vb = b.get(sort_col).unwrap_or(&Value::Empty);
479 let cmp = compare_values_sort(va, vb);
480 if ascending { cmp } else { cmp.reverse() }
481 });
482 from_2d(grid)
483}
484
485fn compare_values_sort(a: &Value, b: &Value) -> std::cmp::Ordering {
486 match (a, b) {
487 (Value::Number(x), Value::Number(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
488 (Value::Text(x), Value::Text(y)) => x.cmp(y),
489 (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
490 (Value::Zoned(x), Value::Zoned(y)) => x.utc_nanos.cmp(&y.utc_nanos),
492 _ => std::cmp::Ordering::Equal,
493 }
494}
495
496fn sortby_fn(args: &[Value]) -> Value {
499 if let Some(e) = check_arity(args, 2, usize::MAX) {
500 return e;
501 }
502 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
503
504 if is_1d {
505 let elems = flatten_val(&args[0]);
507 let n = elems.len();
508
509 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
510 let mut i = 1;
511 while i < args.len() {
512 let key_vals = flatten_val(&args[i]);
513 if key_vals.len() != n {
514 return Value::Error(ErrorKind::Value);
515 }
516 let ascending = if i + 1 < args.len() {
517 match to_f64(&args[i + 1]) {
518 Some(v) => v >= 0.0,
519 None => true,
520 }
521 } else {
522 true
523 };
524 sort_keys.push((key_vals, ascending));
525 i += 2;
526 }
527
528 let mut indices: Vec<usize> = (0..n).collect();
529 indices.sort_by(|&ra, &rb| {
530 for (keys, asc) in &sort_keys {
531 let va = keys.get(ra).unwrap_or(&Value::Empty);
532 let vb = keys.get(rb).unwrap_or(&Value::Empty);
533 let cmp = compare_values_sort(va, vb);
534 if cmp != std::cmp::Ordering::Equal {
535 return if *asc { cmp } else { cmp.reverse() };
536 }
537 }
538 std::cmp::Ordering::Equal
539 });
540
541 return Value::Array(indices.iter().map(|&r| elems[r].clone()).collect());
542 }
543
544 let grid = to_2d(&args[0]);
545 let nrows = grid.len();
546
547 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
549 let mut i = 1;
550 while i < args.len() {
551 let key_vals = flatten_val(&args[i]);
552 if key_vals.len() != nrows && nrows > 1 {
553 return Value::Error(ErrorKind::Value);
554 }
555 let ascending = if i + 1 < args.len() {
556 match to_f64(&args[i + 1]) {
557 Some(n) => n >= 0.0,
558 None => true,
559 }
560 } else {
561 true
562 };
563 sort_keys.push((key_vals, ascending));
564 i += 2;
565 }
566
567 let mut indices: Vec<usize> = (0..nrows).collect();
568 indices.sort_by(|&ra, &rb| {
569 for (keys, asc) in &sort_keys {
570 let va = keys.get(ra).unwrap_or(&Value::Empty);
571 let vb = keys.get(rb).unwrap_or(&Value::Empty);
572 let cmp = compare_values_sort(va, vb);
573 if cmp != std::cmp::Ordering::Equal {
574 return if *asc { cmp } else { cmp.reverse() };
575 }
576 }
577 std::cmp::Ordering::Equal
578 });
579
580 let sorted: Vec<Vec<Value>> = indices.iter().map(|&r| grid[r].clone()).collect();
581 drop(grid);
582 from_2d(sorted)
583}
584
585pub(crate) fn unique_fn(args: &[Value]) -> Value {
588 if let Some(e) = check_arity(args, 1, 3) {
589 return e;
590 }
591 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
592 let grid = to_2d(&args[0]);
593 let by_col = args.get(1).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
595 let exactly_once = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
596
597 if is_1d && !by_col {
601 return args[0].clone();
602 }
603
604 if by_col {
605 let nrows = grid.len();
607 if nrows == 0 {
608 return from_2d(vec![]);
609 }
610 let ncols = grid[0].len();
611 let columns: Vec<Vec<Value>> = (0..ncols)
613 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
614 .collect();
615 let mut seen_cols: Vec<Vec<Value>> = Vec::new();
616 let mut counts: Vec<usize> = Vec::new();
617 for col in columns {
618 if let Some(pos) = seen_cols.iter().position(|sc| sc == &col) {
619 counts[pos] += 1;
620 } else {
621 seen_cols.push(col);
622 counts.push(1);
623 }
624 }
625 let result_cols: Vec<Vec<Value>> = seen_cols
626 .into_iter()
627 .zip(counts)
628 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
629 .map(|(col, _)| col)
630 .collect();
631 let ncols2 = result_cols.len();
633 let result: Vec<Vec<Value>> = (0..nrows)
634 .map(|r| (0..ncols2).map(|c| result_cols[c][r].clone()).collect())
635 .collect();
636 return from_2d(result);
637 }
638
639 let mut seen_rows: Vec<Vec<Value>> = Vec::new();
641 let mut counts: Vec<usize> = Vec::new();
642 for row in &grid {
643 if let Some(pos) = seen_rows.iter().position(|sr| sr == row) {
644 counts[pos] += 1;
645 } else {
646 seen_rows.push(row.clone());
647 counts.push(1);
648 }
649 }
650 let result: Vec<Vec<Value>> = seen_rows
651 .into_iter()
652 .zip(counts)
653 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
654 .map(|(row, _)| row)
655 .collect();
656 from_2d(result)
657}
658
659pub(crate) fn sumproduct_fn(args: &[Value]) -> Value {
662 if let Some(e) = check_arity(args, 1, usize::MAX) {
663 return e;
664 }
665 let arrays: Vec<Vec<Value>> = args.iter().map(flatten_val).collect();
666 let len = arrays[0].len();
667 for arr in &arrays[1..] {
669 if arr.len() != len {
670 return Value::Error(ErrorKind::Value);
671 }
672 }
673 let mut sum = 0.0;
674 for i in 0..len {
675 let mut prod = 1.0;
676 for arr in &arrays {
677 prod *= to_f64(&arr[i]).unwrap_or(0.0);
678 }
679 sum += prod;
680 }
681 Value::Number(sum)
682}
683
684fn sumxmy2_fn(args: &[Value]) -> Value {
687 if let Some(e) = check_arity(args, 2, 2) {
688 return e;
689 }
690 let xs = flatten_val(&args[0]);
691 let ys = flatten_val(&args[1]);
692 if xs.len() != ys.len() {
693 return Value::Error(ErrorKind::NA);
694 }
695 let mut sum = 0.0;
696 for (x, y) in xs.iter().zip(ys.iter()) {
697 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
699 sum += (*xn - *yn).powi(2);
700 }
701 }
702 Value::Number(sum)
703}
704
705fn sumx2my2_fn(args: &[Value]) -> Value {
708 if let Some(e) = check_arity(args, 2, 2) {
709 return e;
710 }
711 let xs = flatten_val(&args[0]);
712 let ys = flatten_val(&args[1]);
713 if xs.len() != ys.len() {
714 return Value::Error(ErrorKind::NA);
715 }
716 let mut sum = 0.0;
717 for (x, y) in xs.iter().zip(ys.iter()) {
718 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
719 sum += *xn * *xn - *yn * *yn;
720 }
721 }
722 Value::Number(sum)
723}
724
725fn sumx2py2_fn(args: &[Value]) -> Value {
728 if let Some(e) = check_arity(args, 2, 2) {
729 return e;
730 }
731 let xs = flatten_val(&args[0]);
732 let ys = flatten_val(&args[1]);
733 if xs.len() != ys.len() {
734 return Value::Error(ErrorKind::NA);
735 }
736 let mut sum = 0.0;
737 for (x, y) in xs.iter().zip(ys.iter()) {
738 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
739 sum += *xn * *xn + *yn * *yn;
740 }
741 }
742 Value::Number(sum)
743}
744
745fn mmult_fn(args: &[Value]) -> Value {
748 if let Some(e) = check_arity(args, 2, 2) {
749 return e;
750 }
751 let a = to_2d(&args[0]);
752 let b = to_2d(&args[1]);
753 if a.iter().chain(b.iter()).any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
754 return Value::Error(ErrorKind::Value);
755 }
756 let n = a.first().map(|r| r.len()).unwrap_or(0);
757 let p = b.first().map(|r| r.len()).unwrap_or(0);
758 if b.len() != n {
759 return Value::Error(ErrorKind::Value);
760 }
761 let af: Vec<Vec<f64>> = a.iter().map(|row| {
763 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
764 }).collect();
765 let bf: Vec<Vec<f64>> = b.iter().map(|row| {
766 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
767 }).collect();
768 if af.iter().any(|r| r.iter().any(|v| v.is_nan())) || bf.iter().any(|r| r.iter().any(|v| v.is_nan())) {
769 return Value::Error(ErrorKind::Value);
770 }
771 let result: Vec<Vec<Value>> = af.iter().map(|row_a| {
772 (0..p).map(|j| {
773 let sum: f64 = row_a.iter().enumerate().map(|(k, &av)| av * bf[k][j]).sum();
774 Value::Number(sum)
775 }).collect()
776 }).collect();
777 from_2d(result)
778}
779
780fn mdeterm_fn(args: &[Value]) -> Value {
783 if let Some(e) = check_arity(args, 1, 1) {
784 return e;
785 }
786 let grid = to_2d(&args[0]);
787 let n = grid.len();
788 if n == 0 {
789 return Value::Error(ErrorKind::Value);
790 }
791 for row in &grid {
792 if row.len() != n {
793 return Value::Error(ErrorKind::Value);
794 }
795 }
796 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
797 return Value::Error(ErrorKind::Value);
798 }
799 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
801 for row in &grid {
802 let mut r = Vec::with_capacity(n);
803 for v in row {
804 match to_f64(v) {
805 Some(x) => r.push(x),
806 None => return Value::Error(ErrorKind::Value),
807 }
808 }
809 mat.push(r);
810 }
811 Value::Number(determinant(&mat))
812}
813
814fn determinant(mat: &[Vec<f64>]) -> f64 {
815 let n = mat.len();
816 if n == 1 {
817 return mat[0][0];
818 }
819 if n == 2 {
820 return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
821 }
822 let mut det = 0.0;
823 for c in 0..n {
824 let minor: Vec<Vec<f64>> = (1..n)
825 .map(|r| {
826 (0..n)
827 .filter(|&cc| cc != c)
828 .map(|cc| mat[r][cc])
829 .collect()
830 })
831 .collect();
832 let sign = if c % 2 == 0 { 1.0 } else { -1.0 };
833 det += sign * mat[0][c] * determinant(&minor);
834 }
835 det
836}
837
838fn minverse_fn(args: &[Value]) -> Value {
841 if let Some(e) = check_arity(args, 1, 1) {
842 return e;
843 }
844 let grid = to_2d(&args[0]);
845 let n = grid.len();
846 if n == 0 {
847 return Value::Error(ErrorKind::Value);
848 }
849 for row in &grid {
850 if row.len() != n {
851 return Value::Error(ErrorKind::Value);
852 }
853 }
854 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
855 return Value::Error(ErrorKind::Value);
856 }
857 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
858 for row in &grid {
859 let mut r = Vec::with_capacity(n);
860 for v in row {
861 match to_f64(v) {
862 Some(x) => r.push(x),
863 None => return Value::Error(ErrorKind::Value),
864 }
865 }
866 mat.push(r);
867 }
868 match invert_matrix(mat) {
869 Some(inv) => from_2d(inv.into_iter().map(|r| r.into_iter().map(Value::Number).collect()).collect()),
870 None => Value::Error(ErrorKind::Num),
871 }
872}
873
874fn invert_matrix(mut mat: Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
875 let n = mat.len();
876 let mut inv: Vec<Vec<f64>> = (0..n)
878 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
879 .collect();
880 for col in 0..n {
881 let pivot = (col..n).max_by(|&a, &b| mat[a][col].abs().partial_cmp(&mat[b][col].abs()).unwrap_or(std::cmp::Ordering::Equal))?;
883 if mat[pivot][col].abs() < 1e-12 {
884 return None; }
886 mat.swap(col, pivot);
887 inv.swap(col, pivot);
888 let div = mat[col][col];
889 for j in 0..n {
890 mat[col][j] /= div;
891 inv[col][j] /= div;
892 }
893 for r in 0..n {
894 if r != col {
895 let factor = mat[r][col];
896 for j in 0..n {
897 mat[r][j] -= factor * mat[col][j];
898 inv[r][j] -= factor * inv[col][j];
899 }
900 }
901 }
902 }
903 Some(inv)
904}
905
906fn frequency_fn(args: &[Value]) -> Value {
910 if let Some(e) = check_arity(args, 2, 2) {
911 return e;
912 }
913 let data: Vec<f64> = flatten_val(&args[0])
915 .iter()
916 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
917 .collect();
918 let bins_raw = flatten_val(&args[1]);
919 if bins_raw.is_empty() || matches!(bins_raw.as_slice(), [Value::Empty]) {
921 return Value::Error(ErrorKind::Ref);
922 }
923 let all_empty = bins_raw.iter().all(|v| matches!(v, Value::Empty));
925 if all_empty {
926 return Value::Error(ErrorKind::Ref);
927 }
928 let bins: Vec<f64> = bins_raw
929 .iter()
930 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
931 .collect();
932 if bins.is_empty() {
933 return Value::Error(ErrorKind::Ref);
934 }
935 let mut counts = vec![0i64; bins.len() + 1];
937 for &x in &data {
938 let mut placed = false;
939 for (i, &b) in bins.iter().enumerate() {
940 if x <= b {
941 counts[i] += 1;
942 placed = true;
943 break;
944 }
945 }
946 if !placed {
947 counts[bins.len()] += 1;
948 }
949 }
950 let col: Vec<Vec<Value>> = counts
952 .into_iter()
953 .map(|c| vec![Value::Number(c as f64)])
954 .collect();
955 from_2d(col)
956}
957
958fn linest_fn(args: &[Value]) -> Value {
962 if let Some(e) = check_arity(args, 1, 4) {
963 return e;
964 }
965 let ys = flatten_val(&args[0]);
966 let n = ys.len();
967 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
969 return Value::Error(ErrorKind::Value);
970 }
971 if n < 2 {
972 return Value::Error(ErrorKind::NA);
973 }
974 let xs: Vec<f64> = if args.len() >= 2 {
975 let xv = flatten_val(&args[1]);
976 if xv.len() != n {
977 return Value::Error(ErrorKind::Ref);
978 }
979 xv.iter().filter_map(to_f64).collect()
980 } else {
981 (1..=n).map(|i| i as f64).collect()
982 };
983 if xs.len() != n {
984 return Value::Error(ErrorKind::Ref);
985 }
986 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
987 if y_vals.len() != n {
988 return Value::Error(ErrorKind::Value);
989 }
990 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
991 Value::Array(vec![Value::Number(slope), Value::Number(intercept)])
992}
993
994fn simple_linear_regression(xs: &[f64], ys: &[f64]) -> (f64, f64) {
995 let n = xs.len() as f64;
996 let sum_x: f64 = xs.iter().sum();
997 let sum_y: f64 = ys.iter().sum();
998 let sum_xy: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * y).sum();
999 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1000 let denom = n * sum_xx - sum_x * sum_x;
1001 if denom.abs() < 1e-15 {
1002 let intercept = sum_y / n;
1003 return (0.0, intercept);
1004 }
1005 let slope = (n * sum_xy - sum_x * sum_y) / denom;
1006 let intercept = (sum_y - slope * sum_x) / n;
1007 (slope, intercept)
1008}
1009
1010fn logest_fn(args: &[Value]) -> Value {
1014 if let Some(e) = check_arity(args, 1, 4) {
1015 return e;
1016 }
1017 let ys = flatten_val(&args[0]);
1018 let n = ys.len();
1019 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1021 return Value::Error(ErrorKind::Value);
1022 }
1023 if n < 2 {
1024 return Value::Error(ErrorKind::NA);
1025 }
1026 let xs: Vec<f64> = if args.len() >= 2 {
1027 let xv = flatten_val(&args[1]);
1028 if xv.len() != n {
1029 return Value::Error(ErrorKind::Ref);
1030 }
1031 xv.iter().filter_map(to_f64).collect()
1032 } else {
1033 (1..=n).map(|i| i as f64).collect()
1034 };
1035 if xs.len() != n {
1036 return Value::Error(ErrorKind::Ref);
1037 }
1038 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1039 if y_vals.len() != n {
1040 return Value::Error(ErrorKind::Value);
1041 }
1042 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1044 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1045 return Value::Error(ErrorKind::Num);
1046 }
1047 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
1048 let base = libm::exp(log_base);
1049 let intercept = libm::exp(log_intercept);
1050 Value::Array(vec![Value::Number(base), Value::Number(intercept)])
1051}
1052
1053fn trend_fn(args: &[Value]) -> Value {
1057 if let Some(e) = check_arity(args, 1, 4) {
1058 return e;
1059 }
1060 let ys = flatten_val(&args[0]);
1061 let n = ys.len();
1062 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
1064 return Value::Error(ErrorKind::Value);
1065 }
1066 if n < 2 {
1067 return Value::Error(ErrorKind::NA);
1068 }
1069 let xs: Vec<f64> = if args.len() >= 2 {
1070 let xv = flatten_val(&args[1]);
1071 if xv.len() != n {
1072 return Value::Error(ErrorKind::Ref);
1073 }
1074 xv.iter().filter_map(to_f64).collect()
1075 } else {
1076 (1..=n).map(|i| i as f64).collect()
1077 };
1078 if xs.len() != n {
1079 return Value::Error(ErrorKind::Ref);
1080 }
1081 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1082 if y_vals.len() != n {
1083 return Value::Error(ErrorKind::Value);
1084 }
1085 let new_xs: Vec<f64> = if args.len() >= 3 {
1086 flatten_val(&args[2]).iter().filter_map(to_f64).collect()
1087 } else {
1088 xs.clone()
1089 };
1090 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1091 let result: Vec<Value> = new_xs.iter().map(|&x| Value::Number(slope * x + intercept)).collect();
1092 Value::Array(result)
1093}
1094
1095fn growth_fn(args: &[Value]) -> Value {
1099 if let Some(e) = check_arity(args, 1, 4) {
1100 return e;
1101 }
1102 let ys = flatten_val(&args[0]);
1103 let n = ys.len();
1104 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1106 return Value::Error(ErrorKind::Value);
1107 }
1108 if n < 2 {
1109 return Value::Error(ErrorKind::NA);
1110 }
1111 let xs: Vec<f64> = if args.len() >= 2 {
1112 let xv = flatten_val(&args[1]);
1113 if xv.len() != n {
1114 return Value::Error(ErrorKind::Ref);
1115 }
1116 xv.iter().filter_map(to_f64).collect()
1117 } else {
1118 (1..=n).map(|i| i as f64).collect()
1119 };
1120 if xs.len() != n {
1121 return Value::Error(ErrorKind::Ref);
1122 }
1123 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1124 if y_vals.len() != n {
1125 return Value::Error(ErrorKind::Value);
1126 }
1127 let log_y: Vec<f64> = y_vals.iter().map(|&y| libm::log(y)).collect();
1128 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1129 return Value::Error(ErrorKind::Num);
1130 }
1131 let new_xs: Vec<f64> = if args.len() >= 3 && !matches!(args[2], Value::Empty) {
1132 let vals: Vec<f64> = flatten_val(&args[2]).iter().filter_map(to_f64).collect();
1133 if vals.is_empty() { xs.clone() } else { vals }
1134 } else {
1135 xs.clone()
1136 };
1137 let use_intercept = if args.len() >= 4 {
1140 match &args[3] {
1141 Value::Bool(b) => *b,
1142 Value::Number(n) => *n != 0.0,
1143 _ => true,
1144 }
1145 } else {
1146 true
1147 };
1148 let (log_base, log_intercept) = if use_intercept {
1149 simple_linear_regression(&xs, &log_y)
1150 } else {
1151 let sum_xy: f64 = xs.iter().zip(log_y.iter()).map(|(x, ly)| x * ly).sum();
1153 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1154 let slope = if sum_xx.abs() < 1e-15 { 0.0 } else { sum_xy / sum_xx };
1155 (slope, 0.0)
1156 };
1157 let result: Vec<Value> = new_xs
1158 .iter()
1159 .map(|&x| Value::Number(libm::exp(log_base * x + log_intercept)))
1160 .collect();
1161 Value::Array(result)
1162}
1163
1164fn apply_lambda(lambda_expr: &Expr, bound_args: &[Value], ctx: &mut EvalCtx<'_>) -> Option<Value> {
1170 match lambda_expr {
1171 Expr::FunctionCall { name, args, .. } if name == "LAMBDA" => {
1172 if args.is_empty() {
1173 return None;
1174 }
1175 let body = &args[args.len() - 1];
1176 let params = &args[..args.len() - 1];
1177 if params.len() != bound_args.len() {
1178 return None;
1179 }
1180 let mut saved: Vec<(String, Value)> = Vec::new();
1182 for (param_expr, val) in params.iter().zip(bound_args.iter()) {
1183 if let Expr::Variable(name, _) = param_expr {
1184 let old = ctx.ctx.get(name);
1185 saved.push((name.clone(), old));
1186 ctx.ctx.set(name.clone(), val.clone());
1187 } else {
1188 return None;
1189 }
1190 }
1191 let result = evaluate_expr(body, ctx);
1192 for (name, old_val) in saved {
1194 ctx.ctx.set(name, old_val);
1195 }
1196 Some(result)
1197 }
1198 _ => None,
1199 }
1200}
1201
1202pub fn byrow_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1205 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1206 return e;
1207 }
1208 let arr_val = evaluate_expr(&args[0], ctx);
1209 if matches!(arr_val, Value::Error(_)) {
1210 return arr_val;
1211 }
1212 let grid = to_2d(&arr_val);
1213 let lambda_expr = &args[1];
1214 let mut results: Vec<Value> = Vec::with_capacity(grid.len());
1215 for row in &grid {
1216 let row_val = Value::Array(row.clone());
1217 match apply_lambda(lambda_expr, &[row_val], ctx) {
1218 Some(v) => results.push(v),
1219 None => return Value::Error(ErrorKind::NA),
1220 }
1221 }
1222 let col: Vec<Vec<Value>> = results.into_iter().map(|v| vec![v]).collect();
1224 from_2d(col)
1225}
1226
1227pub fn bycol_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1230 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1231 return e;
1232 }
1233 let arr_val = evaluate_expr(&args[0], ctx);
1234 if matches!(arr_val, Value::Error(_)) {
1235 return arr_val;
1236 }
1237 let grid = to_2d(&arr_val);
1238 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
1239 let columns: Vec<Vec<Value>> = (0..ncols)
1241 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
1242 .collect();
1243 let lambda_expr = &args[1];
1244 let mut results: Vec<Value> = Vec::with_capacity(ncols);
1245 for col in columns {
1246 let col_val = Value::Array(col);
1248 match apply_lambda(lambda_expr, &[col_val], ctx) {
1249 Some(v) => results.push(v),
1250 None => return Value::Error(ErrorKind::NA),
1251 }
1252 }
1253 Value::Array(results)
1255}
1256
1257pub fn map_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1260 if let Some(e) = check_arity_len(args.len(), 2, usize::MAX) {
1261 return e;
1262 }
1263 let lambda_expr = &args[args.len() - 1];
1265 let arr_count = args.len() - 1;
1266 let arrays: Vec<Vec<Value>> = args[..arr_count]
1267 .iter()
1268 .map(|a| {
1269 let v = evaluate_expr(a, ctx);
1270 flatten_val(&v)
1271 })
1272 .collect();
1273 let len = arrays[0].len();
1274 for arr in &arrays[1..] {
1275 if arr.len() != len {
1276 return Value::Error(ErrorKind::Value);
1277 }
1278 }
1279 let mut results: Vec<Value> = Vec::with_capacity(len);
1280 for i in 0..len {
1281 let bound: Vec<Value> = arrays.iter().map(|a| a[i].clone()).collect();
1282 match apply_lambda(lambda_expr, &bound, ctx) {
1283 Some(v) => results.push(v),
1284 None => return Value::Error(ErrorKind::NA),
1285 }
1286 }
1287 let first_grid = to_2d(&evaluate_expr(&args[0], ctx));
1289 if first_grid.len() > 1 {
1290 let ncols = first_grid[0].len();
1292 let nrows = first_grid.len();
1293 let grid: Vec<Vec<Value>> = (0..nrows)
1294 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1295 .collect();
1296 from_2d(grid)
1297 } else {
1298 Value::Array(results)
1299 }
1300}
1301
1302pub fn reduce_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1305 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1306 return e;
1307 }
1308 let initial = evaluate_expr(&args[0], ctx);
1309 if matches!(initial, Value::Error(_)) {
1310 return initial;
1311 }
1312 let arr_val = evaluate_expr(&args[1], ctx);
1313 if matches!(arr_val, Value::Error(_)) {
1314 return arr_val;
1315 }
1316 let items = flatten_val(&arr_val);
1317 if items.is_empty() {
1318 return Value::Error(ErrorKind::Ref);
1319 }
1320 let lambda_expr = &args[2];
1321 let mut acc = initial;
1322 for item in &items {
1323 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1324 Some(v) => acc = v,
1325 None => return Value::Error(ErrorKind::NA),
1326 }
1327 }
1328 acc
1329}
1330
1331pub fn scan_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1334 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1335 return e;
1336 }
1337 let initial = evaluate_expr(&args[0], ctx);
1338 if matches!(initial, Value::Error(_)) {
1339 return initial;
1340 }
1341 let arr_val = evaluate_expr(&args[1], ctx);
1342 if matches!(arr_val, Value::Error(_)) {
1343 return arr_val;
1344 }
1345 let grid = to_2d(&arr_val);
1346 let items = flatten_val(&arr_val);
1347 let lambda_expr = &args[2];
1348 let mut acc = initial;
1349 let mut results: Vec<Value> = Vec::with_capacity(items.len());
1350 for item in &items {
1351 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1352 Some(v) => {
1353 acc = v.clone();
1354 results.push(v);
1355 }
1356 None => return Value::Error(ErrorKind::NA),
1357 }
1358 }
1359 if grid.len() > 1 {
1361 let ncols = grid[0].len();
1362 let nrows = grid.len();
1363 let result_grid: Vec<Vec<Value>> = (0..nrows)
1364 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1365 .collect();
1366 from_2d(result_grid)
1367 } else {
1368 Value::Array(results)
1369 }
1370}
1371
1372pub fn makearray_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1375 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1376 return e;
1377 }
1378 let rows_val = evaluate_expr(&args[0], ctx);
1379 let cols_val = evaluate_expr(&args[1], ctx);
1380 if matches!(rows_val, Value::Error(_)) {
1381 return rows_val;
1382 }
1383 if matches!(cols_val, Value::Error(_)) {
1384 return cols_val;
1385 }
1386 let nrows = match to_f64(&rows_val) {
1387 Some(n) if n >= 1.0 => n as usize,
1388 _ => return Value::Error(ErrorKind::Value),
1389 };
1390 let ncols = match to_f64(&cols_val) {
1391 Some(n) if n >= 1.0 => n as usize,
1392 _ => return Value::Error(ErrorKind::Value),
1393 };
1394 let lambda_expr = &args[2];
1395 let mut grid: Vec<Vec<Value>> = Vec::with_capacity(nrows);
1396 for r in 1..=nrows {
1397 let mut row = Vec::with_capacity(ncols);
1398 for c in 1..=ncols {
1399 let rv = Value::Number(r as f64);
1400 let cv = Value::Number(c as f64);
1401 match apply_lambda(lambda_expr, &[rv, cv], ctx) {
1402 Some(Value::Array(_)) => return Value::Error(ErrorKind::Value),
1403 Some(v) => row.push(v),
1404 None => return Value::Error(ErrorKind::NA),
1405 }
1406 }
1407 grid.push(row);
1408 }
1409 from_2d(grid)
1410}
1411
1412pub fn arrayformula_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1420 if args.len() != 1 {
1421 return Value::Error(ErrorKind::NA);
1422 }
1423 evaluate_expr(&args[0], ctx)
1424}
1425
1426pub fn register_array(registry: &mut Registry) {
1427 registry.register_eager("ROWS", rows_fn, FunctionMeta {
1428 category: "array",
1429 signature: "ROWS(array)",
1430 description: "Returns the number of rows in an array or range",
1431 });
1432 registry.register_eager("COLUMNS", columns_fn, FunctionMeta {
1433 category: "array",
1434 signature: "COLUMNS(array)",
1435 description: "Returns the number of columns in an array or range",
1436 });
1437 registry.register_eager("TRANSPOSE", transpose_fn, FunctionMeta {
1438 category: "array",
1439 signature: "TRANSPOSE(array)",
1440 description: "Transposes the rows and columns of an array",
1441 });
1442 registry.register_eager("ARRAY_CONSTRAIN", array_constrain_fn, FunctionMeta {
1443 category: "array",
1444 signature: "ARRAY_CONSTRAIN(input, num_rows, num_cols)",
1445 description: "Constrains an array to a given number of rows and columns",
1446 });
1447 registry.register_eager("CHOOSECOLS", choosecols_fn, FunctionMeta {
1448 category: "array",
1449 signature: "CHOOSECOLS(array, col_num1, ...)",
1450 description: "Returns selected columns from an array",
1451 });
1452 registry.register_eager("CHOOSEROWS", chooserows_fn, FunctionMeta {
1453 category: "array",
1454 signature: "CHOOSEROWS(array, row_num1, ...)",
1455 description: "Returns selected rows from an array",
1456 });
1457 registry.register_eager("FLATTEN", flatten_fn, FunctionMeta {
1458 category: "array",
1459 signature: "FLATTEN(array)",
1460 description: "Flattens an array into a single column",
1461 });
1462 registry.register_eager("HSTACK", hstack_fn, FunctionMeta {
1463 category: "array",
1464 signature: "HSTACK(array1, ...)",
1465 description: "Horizontally stacks arrays",
1466 });
1467 registry.register_eager("VSTACK", vstack_fn, FunctionMeta {
1468 category: "array",
1469 signature: "VSTACK(array1, ...)",
1470 description: "Vertically stacks arrays",
1471 });
1472 registry.register_eager("TOCOL", tocol_fn, FunctionMeta {
1473 category: "array",
1474 signature: "TOCOL(array, [ignore], [scan_by_col])",
1475 description: "Converts an array to a single column",
1476 });
1477 registry.register_eager("TOROW", torow_fn, FunctionMeta {
1478 category: "array",
1479 signature: "TOROW(array, [ignore], [scan_by_col])",
1480 description: "Converts an array to a single row",
1481 });
1482 registry.register_eager("WRAPCOLS", wrapcols_fn, FunctionMeta {
1483 category: "array",
1484 signature: "WRAPCOLS(vector, wrap_count, [pad_with])",
1485 description: "Wraps a vector into columns of the given length",
1486 });
1487 registry.register_eager("WRAPROWS", wraprows_fn, FunctionMeta {
1488 category: "array",
1489 signature: "WRAPROWS(vector, wrap_count, [pad_with])",
1490 description: "Wraps a vector into rows of the given length",
1491 });
1492 registry.register_eager("SORT", sort_fn, FunctionMeta {
1493 category: "array",
1494 signature: "SORT(array, [sort_index], [sort_order], [by_col])",
1495 description: "Sorts an array",
1496 });
1497 registry.register_eager("SORTBY", sortby_fn, FunctionMeta {
1498 category: "array",
1499 signature: "SORTBY(array, by_array1, [sort_order1], ...)",
1500 description: "Sorts an array based on the values in corresponding arrays",
1501 });
1502 registry.register_eager("UNIQUE", unique_fn, FunctionMeta {
1503 category: "array",
1504 signature: "UNIQUE(array, [by_col], [exactly_once])",
1505 description: "Returns unique rows or columns from an array",
1506 });
1507 registry.register_eager("SUMPRODUCT", sumproduct_fn, FunctionMeta {
1508 category: "array",
1509 signature: "SUMPRODUCT(array1, [array2], ...)",
1510 description: "Returns the sum of products of corresponding elements",
1511 });
1512 registry.register_eager("SUMXMY2", sumxmy2_fn, FunctionMeta {
1513 category: "array",
1514 signature: "SUMXMY2(array_x, array_y)",
1515 description: "Returns sum of squares of differences",
1516 });
1517 registry.register_eager("SUMX2MY2", sumx2my2_fn, FunctionMeta {
1518 category: "array",
1519 signature: "SUMX2MY2(array_x, array_y)",
1520 description: "Returns sum of (x^2 - y^2)",
1521 });
1522 registry.register_eager("SUMX2PY2", sumx2py2_fn, FunctionMeta {
1523 category: "array",
1524 signature: "SUMX2PY2(array_x, array_y)",
1525 description: "Returns sum of (x^2 + y^2)",
1526 });
1527 registry.register_eager("MMULT", mmult_fn, FunctionMeta {
1528 category: "array",
1529 signature: "MMULT(array1, array2)",
1530 description: "Returns the matrix product of two arrays",
1531 });
1532 registry.register_eager("MDETERM", mdeterm_fn, FunctionMeta {
1533 category: "array",
1534 signature: "MDETERM(array)",
1535 description: "Returns the matrix determinant",
1536 });
1537 registry.register_eager("MINVERSE", minverse_fn, FunctionMeta {
1538 category: "array",
1539 signature: "MINVERSE(array)",
1540 description: "Returns the matrix inverse",
1541 });
1542 registry.register_eager("FREQUENCY", frequency_fn, FunctionMeta {
1543 category: "array",
1544 signature: "FREQUENCY(data, bins)",
1545 description: "Calculates the frequency distribution of values",
1546 });
1547 registry.register_eager("LINEST", linest_fn, FunctionMeta {
1548 category: "array",
1549 signature: "LINEST(known_y, [known_x], [const], [stats])",
1550 description: "Returns linear regression statistics",
1551 });
1552 registry.register_eager("LOGEST", logest_fn, FunctionMeta {
1553 category: "array",
1554 signature: "LOGEST(known_y, [known_x], [const], [stats])",
1555 description: "Returns exponential regression statistics",
1556 });
1557 registry.register_eager("TREND", trend_fn, FunctionMeta {
1558 category: "array",
1559 signature: "TREND(known_y, [known_x], [new_x], [const])",
1560 description: "Returns values along a linear trend",
1561 });
1562 registry.register_eager("GROWTH", growth_fn, FunctionMeta {
1563 category: "array",
1564 signature: "GROWTH(known_y, [known_x], [new_x], [const])",
1565 description: "Returns values along an exponential trend",
1566 });
1567 registry.register_lazy("BYROW", byrow_lazy_fn, FunctionMeta {
1568 category: "array",
1569 signature: "BYROW(array, lambda)",
1570 description: "Applies a LAMBDA to each row of an array",
1571 });
1572 registry.register_lazy("BYCOL", bycol_lazy_fn, FunctionMeta {
1573 category: "array",
1574 signature: "BYCOL(array, lambda)",
1575 description: "Applies a LAMBDA to each column of an array",
1576 });
1577 registry.register_lazy("MAP", map_lazy_fn, FunctionMeta {
1578 category: "array",
1579 signature: "MAP(array1, [array2, ...], lambda)",
1580 description: "Maps a LAMBDA over one or more arrays",
1581 });
1582 registry.register_lazy("REDUCE", reduce_lazy_fn, FunctionMeta {
1583 category: "array",
1584 signature: "REDUCE(initial_value, array, lambda)",
1585 description: "Reduces an array to a single value using a LAMBDA",
1586 });
1587 registry.register_lazy("SCAN", scan_lazy_fn, FunctionMeta {
1588 category: "array",
1589 signature: "SCAN(initial_value, array, lambda)",
1590 description: "Returns running accumulation using a LAMBDA",
1591 });
1592 registry.register_lazy("MAKEARRAY", makearray_lazy_fn, FunctionMeta {
1593 category: "array",
1594 signature: "MAKEARRAY(rows, cols, lambda)",
1595 description: "Creates an array using a LAMBDA for each cell value",
1596 });
1597 registry.register_lazy("ARRAYFORMULA", arrayformula_lazy_fn, FunctionMeta {
1598 category: "array",
1599 signature: "ARRAYFORMULA(array_formula)",
1600 description: "Evaluates a formula as an array formula",
1601 });
1602}
1603
1604#[cfg(test)]
1605mod tests;