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 _ => std::cmp::Ordering::Equal,
491 }
492}
493
494fn sortby_fn(args: &[Value]) -> Value {
497 if let Some(e) = check_arity(args, 2, usize::MAX) {
498 return e;
499 }
500 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
501
502 if is_1d {
503 let elems = flatten_val(&args[0]);
505 let n = elems.len();
506
507 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
508 let mut i = 1;
509 while i < args.len() {
510 let key_vals = flatten_val(&args[i]);
511 if key_vals.len() != n {
512 return Value::Error(ErrorKind::Value);
513 }
514 let ascending = if i + 1 < args.len() {
515 match to_f64(&args[i + 1]) {
516 Some(v) => v >= 0.0,
517 None => true,
518 }
519 } else {
520 true
521 };
522 sort_keys.push((key_vals, ascending));
523 i += 2;
524 }
525
526 let mut indices: Vec<usize> = (0..n).collect();
527 indices.sort_by(|&ra, &rb| {
528 for (keys, asc) in &sort_keys {
529 let va = keys.get(ra).unwrap_or(&Value::Empty);
530 let vb = keys.get(rb).unwrap_or(&Value::Empty);
531 let cmp = compare_values_sort(va, vb);
532 if cmp != std::cmp::Ordering::Equal {
533 return if *asc { cmp } else { cmp.reverse() };
534 }
535 }
536 std::cmp::Ordering::Equal
537 });
538
539 return Value::Array(indices.iter().map(|&r| elems[r].clone()).collect());
540 }
541
542 let grid = to_2d(&args[0]);
543 let nrows = grid.len();
544
545 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
547 let mut i = 1;
548 while i < args.len() {
549 let key_vals = flatten_val(&args[i]);
550 if key_vals.len() != nrows && nrows > 1 {
551 return Value::Error(ErrorKind::Value);
552 }
553 let ascending = if i + 1 < args.len() {
554 match to_f64(&args[i + 1]) {
555 Some(n) => n >= 0.0,
556 None => true,
557 }
558 } else {
559 true
560 };
561 sort_keys.push((key_vals, ascending));
562 i += 2;
563 }
564
565 let mut indices: Vec<usize> = (0..nrows).collect();
566 indices.sort_by(|&ra, &rb| {
567 for (keys, asc) in &sort_keys {
568 let va = keys.get(ra).unwrap_or(&Value::Empty);
569 let vb = keys.get(rb).unwrap_or(&Value::Empty);
570 let cmp = compare_values_sort(va, vb);
571 if cmp != std::cmp::Ordering::Equal {
572 return if *asc { cmp } else { cmp.reverse() };
573 }
574 }
575 std::cmp::Ordering::Equal
576 });
577
578 let sorted: Vec<Vec<Value>> = indices.iter().map(|&r| grid[r].clone()).collect();
579 drop(grid);
580 from_2d(sorted)
581}
582
583pub(crate) fn unique_fn(args: &[Value]) -> Value {
586 if let Some(e) = check_arity(args, 1, 3) {
587 return e;
588 }
589 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
590 let grid = to_2d(&args[0]);
591 let by_col = args.get(1).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
593 let exactly_once = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
594
595 if is_1d && !by_col {
599 return args[0].clone();
600 }
601
602 if by_col {
603 let nrows = grid.len();
605 if nrows == 0 {
606 return from_2d(vec![]);
607 }
608 let ncols = grid[0].len();
609 let columns: Vec<Vec<Value>> = (0..ncols)
611 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
612 .collect();
613 let mut seen_cols: Vec<Vec<Value>> = Vec::new();
614 let mut counts: Vec<usize> = Vec::new();
615 for col in columns {
616 if let Some(pos) = seen_cols.iter().position(|sc| sc == &col) {
617 counts[pos] += 1;
618 } else {
619 seen_cols.push(col);
620 counts.push(1);
621 }
622 }
623 let result_cols: Vec<Vec<Value>> = seen_cols
624 .into_iter()
625 .zip(counts)
626 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
627 .map(|(col, _)| col)
628 .collect();
629 let ncols2 = result_cols.len();
631 let result: Vec<Vec<Value>> = (0..nrows)
632 .map(|r| (0..ncols2).map(|c| result_cols[c][r].clone()).collect())
633 .collect();
634 return from_2d(result);
635 }
636
637 let mut seen_rows: Vec<Vec<Value>> = Vec::new();
639 let mut counts: Vec<usize> = Vec::new();
640 for row in &grid {
641 if let Some(pos) = seen_rows.iter().position(|sr| sr == row) {
642 counts[pos] += 1;
643 } else {
644 seen_rows.push(row.clone());
645 counts.push(1);
646 }
647 }
648 let result: Vec<Vec<Value>> = seen_rows
649 .into_iter()
650 .zip(counts)
651 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
652 .map(|(row, _)| row)
653 .collect();
654 from_2d(result)
655}
656
657pub(crate) fn sumproduct_fn(args: &[Value]) -> Value {
660 if let Some(e) = check_arity(args, 1, usize::MAX) {
661 return e;
662 }
663 let arrays: Vec<Vec<Value>> = args.iter().map(flatten_val).collect();
664 let len = arrays[0].len();
665 for arr in &arrays[1..] {
667 if arr.len() != len {
668 return Value::Error(ErrorKind::Value);
669 }
670 }
671 let mut sum = 0.0;
672 for i in 0..len {
673 let mut prod = 1.0;
674 for arr in &arrays {
675 prod *= to_f64(&arr[i]).unwrap_or(0.0);
676 }
677 sum += prod;
678 }
679 Value::Number(sum)
680}
681
682fn sumxmy2_fn(args: &[Value]) -> Value {
685 if let Some(e) = check_arity(args, 2, 2) {
686 return e;
687 }
688 let xs = flatten_val(&args[0]);
689 let ys = flatten_val(&args[1]);
690 if xs.len() != ys.len() {
691 return Value::Error(ErrorKind::NA);
692 }
693 let mut sum = 0.0;
694 for (x, y) in xs.iter().zip(ys.iter()) {
695 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
697 sum += (*xn - *yn).powi(2);
698 }
699 }
700 Value::Number(sum)
701}
702
703fn sumx2my2_fn(args: &[Value]) -> Value {
706 if let Some(e) = check_arity(args, 2, 2) {
707 return e;
708 }
709 let xs = flatten_val(&args[0]);
710 let ys = flatten_val(&args[1]);
711 if xs.len() != ys.len() {
712 return Value::Error(ErrorKind::NA);
713 }
714 let mut sum = 0.0;
715 for (x, y) in xs.iter().zip(ys.iter()) {
716 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
717 sum += *xn * *xn - *yn * *yn;
718 }
719 }
720 Value::Number(sum)
721}
722
723fn sumx2py2_fn(args: &[Value]) -> Value {
726 if let Some(e) = check_arity(args, 2, 2) {
727 return e;
728 }
729 let xs = flatten_val(&args[0]);
730 let ys = flatten_val(&args[1]);
731 if xs.len() != ys.len() {
732 return Value::Error(ErrorKind::NA);
733 }
734 let mut sum = 0.0;
735 for (x, y) in xs.iter().zip(ys.iter()) {
736 if let (Value::Number(xn), Value::Number(yn)) = (x, y) {
737 sum += *xn * *xn + *yn * *yn;
738 }
739 }
740 Value::Number(sum)
741}
742
743fn mmult_fn(args: &[Value]) -> Value {
746 if let Some(e) = check_arity(args, 2, 2) {
747 return e;
748 }
749 let a = to_2d(&args[0]);
750 let b = to_2d(&args[1]);
751 if a.iter().chain(b.iter()).any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
752 return Value::Error(ErrorKind::Value);
753 }
754 let n = a.first().map(|r| r.len()).unwrap_or(0);
755 let p = b.first().map(|r| r.len()).unwrap_or(0);
756 if b.len() != n {
757 return Value::Error(ErrorKind::Value);
758 }
759 let af: Vec<Vec<f64>> = a.iter().map(|row| {
761 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
762 }).collect();
763 let bf: Vec<Vec<f64>> = b.iter().map(|row| {
764 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
765 }).collect();
766 if af.iter().any(|r| r.iter().any(|v| v.is_nan())) || bf.iter().any(|r| r.iter().any(|v| v.is_nan())) {
767 return Value::Error(ErrorKind::Value);
768 }
769 let result: Vec<Vec<Value>> = af.iter().map(|row_a| {
770 (0..p).map(|j| {
771 let sum: f64 = row_a.iter().enumerate().map(|(k, &av)| av * bf[k][j]).sum();
772 Value::Number(sum)
773 }).collect()
774 }).collect();
775 from_2d(result)
776}
777
778fn mdeterm_fn(args: &[Value]) -> Value {
781 if let Some(e) = check_arity(args, 1, 1) {
782 return e;
783 }
784 let grid = to_2d(&args[0]);
785 let n = grid.len();
786 if n == 0 {
787 return Value::Error(ErrorKind::Value);
788 }
789 for row in &grid {
790 if row.len() != n {
791 return Value::Error(ErrorKind::Value);
792 }
793 }
794 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
795 return Value::Error(ErrorKind::Value);
796 }
797 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
799 for row in &grid {
800 let mut r = Vec::with_capacity(n);
801 for v in row {
802 match to_f64(v) {
803 Some(x) => r.push(x),
804 None => return Value::Error(ErrorKind::Value),
805 }
806 }
807 mat.push(r);
808 }
809 Value::Number(determinant(&mat))
810}
811
812fn determinant(mat: &[Vec<f64>]) -> f64 {
813 let n = mat.len();
814 if n == 1 {
815 return mat[0][0];
816 }
817 if n == 2 {
818 return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
819 }
820 let mut det = 0.0;
821 for c in 0..n {
822 let minor: Vec<Vec<f64>> = (1..n)
823 .map(|r| {
824 (0..n)
825 .filter(|&cc| cc != c)
826 .map(|cc| mat[r][cc])
827 .collect()
828 })
829 .collect();
830 let sign = if c % 2 == 0 { 1.0 } else { -1.0 };
831 det += sign * mat[0][c] * determinant(&minor);
832 }
833 det
834}
835
836fn minverse_fn(args: &[Value]) -> Value {
839 if let Some(e) = check_arity(args, 1, 1) {
840 return e;
841 }
842 let grid = to_2d(&args[0]);
843 let n = grid.len();
844 if n == 0 {
845 return Value::Error(ErrorKind::Value);
846 }
847 for row in &grid {
848 if row.len() != n {
849 return Value::Error(ErrorKind::Value);
850 }
851 }
852 if grid.iter().any(|row| row.iter().any(|v| matches!(v, Value::Bool(_)))) {
853 return Value::Error(ErrorKind::Value);
854 }
855 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
856 for row in &grid {
857 let mut r = Vec::with_capacity(n);
858 for v in row {
859 match to_f64(v) {
860 Some(x) => r.push(x),
861 None => return Value::Error(ErrorKind::Value),
862 }
863 }
864 mat.push(r);
865 }
866 match invert_matrix(mat) {
867 Some(inv) => from_2d(inv.into_iter().map(|r| r.into_iter().map(Value::Number).collect()).collect()),
868 None => Value::Error(ErrorKind::Num),
869 }
870}
871
872fn invert_matrix(mut mat: Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
873 let n = mat.len();
874 let mut inv: Vec<Vec<f64>> = (0..n)
876 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
877 .collect();
878 for col in 0..n {
879 let pivot = (col..n).max_by(|&a, &b| mat[a][col].abs().partial_cmp(&mat[b][col].abs()).unwrap_or(std::cmp::Ordering::Equal))?;
881 if mat[pivot][col].abs() < 1e-12 {
882 return None; }
884 mat.swap(col, pivot);
885 inv.swap(col, pivot);
886 let div = mat[col][col];
887 for j in 0..n {
888 mat[col][j] /= div;
889 inv[col][j] /= div;
890 }
891 for r in 0..n {
892 if r != col {
893 let factor = mat[r][col];
894 for j in 0..n {
895 mat[r][j] -= factor * mat[col][j];
896 inv[r][j] -= factor * inv[col][j];
897 }
898 }
899 }
900 }
901 Some(inv)
902}
903
904fn frequency_fn(args: &[Value]) -> Value {
908 if let Some(e) = check_arity(args, 2, 2) {
909 return e;
910 }
911 let data: Vec<f64> = flatten_val(&args[0])
913 .iter()
914 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
915 .collect();
916 let bins_raw = flatten_val(&args[1]);
917 if bins_raw.is_empty() || matches!(bins_raw.as_slice(), [Value::Empty]) {
919 return Value::Error(ErrorKind::Ref);
920 }
921 let all_empty = bins_raw.iter().all(|v| matches!(v, Value::Empty));
923 if all_empty {
924 return Value::Error(ErrorKind::Ref);
925 }
926 let bins: Vec<f64> = bins_raw
927 .iter()
928 .filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
929 .collect();
930 if bins.is_empty() {
931 return Value::Error(ErrorKind::Ref);
932 }
933 let mut counts = vec![0i64; bins.len() + 1];
935 for &x in &data {
936 let mut placed = false;
937 for (i, &b) in bins.iter().enumerate() {
938 if x <= b {
939 counts[i] += 1;
940 placed = true;
941 break;
942 }
943 }
944 if !placed {
945 counts[bins.len()] += 1;
946 }
947 }
948 let col: Vec<Vec<Value>> = counts
950 .into_iter()
951 .map(|c| vec![Value::Number(c as f64)])
952 .collect();
953 from_2d(col)
954}
955
956fn linest_fn(args: &[Value]) -> Value {
960 if let Some(e) = check_arity(args, 1, 4) {
961 return e;
962 }
963 let ys = flatten_val(&args[0]);
964 let n = ys.len();
965 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
967 return Value::Error(ErrorKind::Value);
968 }
969 if n < 2 {
970 return Value::Error(ErrorKind::NA);
971 }
972 let xs: Vec<f64> = if args.len() >= 2 {
973 let xv = flatten_val(&args[1]);
974 if xv.len() != n {
975 return Value::Error(ErrorKind::Ref);
976 }
977 xv.iter().filter_map(to_f64).collect()
978 } else {
979 (1..=n).map(|i| i as f64).collect()
980 };
981 if xs.len() != n {
982 return Value::Error(ErrorKind::Ref);
983 }
984 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
985 if y_vals.len() != n {
986 return Value::Error(ErrorKind::Value);
987 }
988 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
989 Value::Array(vec![Value::Number(slope), Value::Number(intercept)])
990}
991
992fn simple_linear_regression(xs: &[f64], ys: &[f64]) -> (f64, f64) {
993 let n = xs.len() as f64;
994 let sum_x: f64 = xs.iter().sum();
995 let sum_y: f64 = ys.iter().sum();
996 let sum_xy: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * y).sum();
997 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
998 let denom = n * sum_xx - sum_x * sum_x;
999 if denom.abs() < 1e-15 {
1000 let intercept = sum_y / n;
1001 return (0.0, intercept);
1002 }
1003 let slope = (n * sum_xy - sum_x * sum_y) / denom;
1004 let intercept = (sum_y - slope * sum_x) / n;
1005 (slope, intercept)
1006}
1007
1008fn logest_fn(args: &[Value]) -> Value {
1012 if let Some(e) = check_arity(args, 1, 4) {
1013 return e;
1014 }
1015 let ys = flatten_val(&args[0]);
1016 let n = ys.len();
1017 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1019 return Value::Error(ErrorKind::Value);
1020 }
1021 if n < 2 {
1022 return Value::Error(ErrorKind::NA);
1023 }
1024 let xs: Vec<f64> = if args.len() >= 2 {
1025 let xv = flatten_val(&args[1]);
1026 if xv.len() != n {
1027 return Value::Error(ErrorKind::Ref);
1028 }
1029 xv.iter().filter_map(to_f64).collect()
1030 } else {
1031 (1..=n).map(|i| i as f64).collect()
1032 };
1033 if xs.len() != n {
1034 return Value::Error(ErrorKind::Ref);
1035 }
1036 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1037 if y_vals.len() != n {
1038 return Value::Error(ErrorKind::Value);
1039 }
1040 let log_y: Vec<f64> = y_vals.iter().map(|&y| y.ln()).collect();
1042 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1043 return Value::Error(ErrorKind::Num);
1044 }
1045 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
1046 let base = log_base.exp();
1047 let intercept = log_intercept.exp();
1048 Value::Array(vec![Value::Number(base), Value::Number(intercept)])
1049}
1050
1051fn trend_fn(args: &[Value]) -> Value {
1055 if let Some(e) = check_arity(args, 1, 4) {
1056 return e;
1057 }
1058 let ys = flatten_val(&args[0]);
1059 let n = ys.len();
1060 if ys.iter().any(|v| matches!(v, Value::Bool(_) | Value::Text(_))) {
1062 return Value::Error(ErrorKind::Value);
1063 }
1064 if n < 2 {
1065 return Value::Error(ErrorKind::NA);
1066 }
1067 let xs: Vec<f64> = if args.len() >= 2 {
1068 let xv = flatten_val(&args[1]);
1069 if xv.len() != n {
1070 return Value::Error(ErrorKind::Ref);
1071 }
1072 xv.iter().filter_map(to_f64).collect()
1073 } else {
1074 (1..=n).map(|i| i as f64).collect()
1075 };
1076 if xs.len() != n {
1077 return Value::Error(ErrorKind::Ref);
1078 }
1079 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1080 if y_vals.len() != n {
1081 return Value::Error(ErrorKind::Value);
1082 }
1083 let new_xs: Vec<f64> = if args.len() >= 3 {
1084 flatten_val(&args[2]).iter().filter_map(to_f64).collect()
1085 } else {
1086 xs.clone()
1087 };
1088 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1089 let result: Vec<Value> = new_xs.iter().map(|&x| Value::Number(slope * x + intercept)).collect();
1090 Value::Array(result)
1091}
1092
1093fn growth_fn(args: &[Value]) -> Value {
1097 if let Some(e) = check_arity(args, 1, 4) {
1098 return e;
1099 }
1100 let ys = flatten_val(&args[0]);
1101 let n = ys.len();
1102 if ys.iter().any(|v| matches!(v, Value::Bool(_))) {
1104 return Value::Error(ErrorKind::Value);
1105 }
1106 if n < 2 {
1107 return Value::Error(ErrorKind::NA);
1108 }
1109 let xs: Vec<f64> = if args.len() >= 2 {
1110 let xv = flatten_val(&args[1]);
1111 if xv.len() != n {
1112 return Value::Error(ErrorKind::Ref);
1113 }
1114 xv.iter().filter_map(to_f64).collect()
1115 } else {
1116 (1..=n).map(|i| i as f64).collect()
1117 };
1118 if xs.len() != n {
1119 return Value::Error(ErrorKind::Ref);
1120 }
1121 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1122 if y_vals.len() != n {
1123 return Value::Error(ErrorKind::Value);
1124 }
1125 let log_y: Vec<f64> = y_vals.iter().map(|&y| y.ln()).collect();
1126 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1127 return Value::Error(ErrorKind::Num);
1128 }
1129 let new_xs: Vec<f64> = if args.len() >= 3 && !matches!(args[2], Value::Empty) {
1130 let vals: Vec<f64> = flatten_val(&args[2]).iter().filter_map(to_f64).collect();
1131 if vals.is_empty() { xs.clone() } else { vals }
1132 } else {
1133 xs.clone()
1134 };
1135 let use_intercept = if args.len() >= 4 {
1138 match &args[3] {
1139 Value::Bool(b) => *b,
1140 Value::Number(n) => *n != 0.0,
1141 _ => true,
1142 }
1143 } else {
1144 true
1145 };
1146 let (log_base, log_intercept) = if use_intercept {
1147 simple_linear_regression(&xs, &log_y)
1148 } else {
1149 let sum_xy: f64 = xs.iter().zip(log_y.iter()).map(|(x, ly)| x * ly).sum();
1151 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
1152 let slope = if sum_xx.abs() < 1e-15 { 0.0 } else { sum_xy / sum_xx };
1153 (slope, 0.0)
1154 };
1155 let result: Vec<Value> = new_xs
1156 .iter()
1157 .map(|&x| Value::Number((log_base * x + log_intercept).exp()))
1158 .collect();
1159 Value::Array(result)
1160}
1161
1162fn apply_lambda(lambda_expr: &Expr, bound_args: &[Value], ctx: &mut EvalCtx<'_>) -> Option<Value> {
1168 match lambda_expr {
1169 Expr::FunctionCall { name, args, .. } if name == "LAMBDA" => {
1170 if args.is_empty() {
1171 return None;
1172 }
1173 let body = &args[args.len() - 1];
1174 let params = &args[..args.len() - 1];
1175 if params.len() != bound_args.len() {
1176 return None;
1177 }
1178 let mut saved: Vec<(String, Value)> = Vec::new();
1180 for (param_expr, val) in params.iter().zip(bound_args.iter()) {
1181 if let Expr::Variable(name, _) = param_expr {
1182 let old = ctx.ctx.get(name);
1183 saved.push((name.clone(), old));
1184 ctx.ctx.set(name.clone(), val.clone());
1185 } else {
1186 return None;
1187 }
1188 }
1189 let result = evaluate_expr(body, ctx);
1190 for (name, old_val) in saved {
1192 ctx.ctx.set(name, old_val);
1193 }
1194 Some(result)
1195 }
1196 _ => None,
1197 }
1198}
1199
1200pub fn byrow_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1203 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1204 return e;
1205 }
1206 let arr_val = evaluate_expr(&args[0], ctx);
1207 if matches!(arr_val, Value::Error(_)) {
1208 return arr_val;
1209 }
1210 let grid = to_2d(&arr_val);
1211 let lambda_expr = &args[1];
1212 let mut results: Vec<Value> = Vec::with_capacity(grid.len());
1213 for row in &grid {
1214 let row_val = Value::Array(row.clone());
1215 match apply_lambda(lambda_expr, &[row_val], ctx) {
1216 Some(v) => results.push(v),
1217 None => return Value::Error(ErrorKind::NA),
1218 }
1219 }
1220 let col: Vec<Vec<Value>> = results.into_iter().map(|v| vec![v]).collect();
1222 from_2d(col)
1223}
1224
1225pub fn bycol_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1228 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1229 return e;
1230 }
1231 let arr_val = evaluate_expr(&args[0], ctx);
1232 if matches!(arr_val, Value::Error(_)) {
1233 return arr_val;
1234 }
1235 let grid = to_2d(&arr_val);
1236 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
1237 let columns: Vec<Vec<Value>> = (0..ncols)
1239 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
1240 .collect();
1241 let lambda_expr = &args[1];
1242 let mut results: Vec<Value> = Vec::with_capacity(ncols);
1243 for col in columns {
1244 let col_val = Value::Array(col);
1246 match apply_lambda(lambda_expr, &[col_val], ctx) {
1247 Some(v) => results.push(v),
1248 None => return Value::Error(ErrorKind::NA),
1249 }
1250 }
1251 Value::Array(results)
1253}
1254
1255pub fn map_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1258 if let Some(e) = check_arity_len(args.len(), 2, usize::MAX) {
1259 return e;
1260 }
1261 let lambda_expr = &args[args.len() - 1];
1263 let arr_count = args.len() - 1;
1264 let arrays: Vec<Vec<Value>> = args[..arr_count]
1265 .iter()
1266 .map(|a| {
1267 let v = evaluate_expr(a, ctx);
1268 flatten_val(&v)
1269 })
1270 .collect();
1271 let len = arrays[0].len();
1272 for arr in &arrays[1..] {
1273 if arr.len() != len {
1274 return Value::Error(ErrorKind::Value);
1275 }
1276 }
1277 let mut results: Vec<Value> = Vec::with_capacity(len);
1278 for i in 0..len {
1279 let bound: Vec<Value> = arrays.iter().map(|a| a[i].clone()).collect();
1280 match apply_lambda(lambda_expr, &bound, ctx) {
1281 Some(v) => results.push(v),
1282 None => return Value::Error(ErrorKind::NA),
1283 }
1284 }
1285 let first_grid = to_2d(&evaluate_expr(&args[0], ctx));
1287 if first_grid.len() > 1 {
1288 let ncols = first_grid[0].len();
1290 let nrows = first_grid.len();
1291 let grid: Vec<Vec<Value>> = (0..nrows)
1292 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1293 .collect();
1294 from_2d(grid)
1295 } else {
1296 Value::Array(results)
1297 }
1298}
1299
1300pub fn reduce_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1303 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1304 return e;
1305 }
1306 let initial = evaluate_expr(&args[0], ctx);
1307 if matches!(initial, Value::Error(_)) {
1308 return initial;
1309 }
1310 let arr_val = evaluate_expr(&args[1], ctx);
1311 if matches!(arr_val, Value::Error(_)) {
1312 return arr_val;
1313 }
1314 let items = flatten_val(&arr_val);
1315 if items.is_empty() {
1316 return Value::Error(ErrorKind::Ref);
1317 }
1318 let lambda_expr = &args[2];
1319 let mut acc = initial;
1320 for item in &items {
1321 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1322 Some(v) => acc = v,
1323 None => return Value::Error(ErrorKind::NA),
1324 }
1325 }
1326 acc
1327}
1328
1329pub fn scan_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1332 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1333 return e;
1334 }
1335 let initial = evaluate_expr(&args[0], ctx);
1336 if matches!(initial, Value::Error(_)) {
1337 return initial;
1338 }
1339 let arr_val = evaluate_expr(&args[1], ctx);
1340 if matches!(arr_val, Value::Error(_)) {
1341 return arr_val;
1342 }
1343 let grid = to_2d(&arr_val);
1344 let items = flatten_val(&arr_val);
1345 let lambda_expr = &args[2];
1346 let mut acc = initial;
1347 let mut results: Vec<Value> = Vec::with_capacity(items.len());
1348 for item in &items {
1349 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1350 Some(v) => {
1351 acc = v.clone();
1352 results.push(v);
1353 }
1354 None => return Value::Error(ErrorKind::NA),
1355 }
1356 }
1357 if grid.len() > 1 {
1359 let ncols = grid[0].len();
1360 let nrows = grid.len();
1361 let result_grid: Vec<Vec<Value>> = (0..nrows)
1362 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1363 .collect();
1364 from_2d(result_grid)
1365 } else {
1366 Value::Array(results)
1367 }
1368}
1369
1370pub fn makearray_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1373 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1374 return e;
1375 }
1376 let rows_val = evaluate_expr(&args[0], ctx);
1377 let cols_val = evaluate_expr(&args[1], ctx);
1378 if matches!(rows_val, Value::Error(_)) {
1379 return rows_val;
1380 }
1381 if matches!(cols_val, Value::Error(_)) {
1382 return cols_val;
1383 }
1384 let nrows = match to_f64(&rows_val) {
1385 Some(n) if n >= 1.0 => n as usize,
1386 _ => return Value::Error(ErrorKind::Value),
1387 };
1388 let ncols = match to_f64(&cols_val) {
1389 Some(n) if n >= 1.0 => n as usize,
1390 _ => return Value::Error(ErrorKind::Value),
1391 };
1392 let lambda_expr = &args[2];
1393 let mut grid: Vec<Vec<Value>> = Vec::with_capacity(nrows);
1394 for r in 1..=nrows {
1395 let mut row = Vec::with_capacity(ncols);
1396 for c in 1..=ncols {
1397 let rv = Value::Number(r as f64);
1398 let cv = Value::Number(c as f64);
1399 match apply_lambda(lambda_expr, &[rv, cv], ctx) {
1400 Some(Value::Array(_)) => return Value::Error(ErrorKind::Value),
1401 Some(v) => row.push(v),
1402 None => return Value::Error(ErrorKind::NA),
1403 }
1404 }
1405 grid.push(row);
1406 }
1407 from_2d(grid)
1408}
1409
1410pub fn arrayformula_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1418 if args.len() != 1 {
1419 return Value::Error(ErrorKind::NA);
1420 }
1421 evaluate_expr(&args[0], ctx)
1422}
1423
1424pub fn register_array(registry: &mut Registry) {
1425 registry.register_eager("ROWS", rows_fn, FunctionMeta {
1426 category: "array",
1427 signature: "ROWS(array)",
1428 description: "Returns the number of rows in an array or range",
1429 });
1430 registry.register_eager("COLUMNS", columns_fn, FunctionMeta {
1431 category: "array",
1432 signature: "COLUMNS(array)",
1433 description: "Returns the number of columns in an array or range",
1434 });
1435 registry.register_eager("TRANSPOSE", transpose_fn, FunctionMeta {
1436 category: "array",
1437 signature: "TRANSPOSE(array)",
1438 description: "Transposes the rows and columns of an array",
1439 });
1440 registry.register_eager("ARRAY_CONSTRAIN", array_constrain_fn, FunctionMeta {
1441 category: "array",
1442 signature: "ARRAY_CONSTRAIN(input, num_rows, num_cols)",
1443 description: "Constrains an array to a given number of rows and columns",
1444 });
1445 registry.register_eager("CHOOSECOLS", choosecols_fn, FunctionMeta {
1446 category: "array",
1447 signature: "CHOOSECOLS(array, col_num1, ...)",
1448 description: "Returns selected columns from an array",
1449 });
1450 registry.register_eager("CHOOSEROWS", chooserows_fn, FunctionMeta {
1451 category: "array",
1452 signature: "CHOOSEROWS(array, row_num1, ...)",
1453 description: "Returns selected rows from an array",
1454 });
1455 registry.register_eager("FLATTEN", flatten_fn, FunctionMeta {
1456 category: "array",
1457 signature: "FLATTEN(array)",
1458 description: "Flattens an array into a single column",
1459 });
1460 registry.register_eager("HSTACK", hstack_fn, FunctionMeta {
1461 category: "array",
1462 signature: "HSTACK(array1, ...)",
1463 description: "Horizontally stacks arrays",
1464 });
1465 registry.register_eager("VSTACK", vstack_fn, FunctionMeta {
1466 category: "array",
1467 signature: "VSTACK(array1, ...)",
1468 description: "Vertically stacks arrays",
1469 });
1470 registry.register_eager("TOCOL", tocol_fn, FunctionMeta {
1471 category: "array",
1472 signature: "TOCOL(array, [ignore], [scan_by_col])",
1473 description: "Converts an array to a single column",
1474 });
1475 registry.register_eager("TOROW", torow_fn, FunctionMeta {
1476 category: "array",
1477 signature: "TOROW(array, [ignore], [scan_by_col])",
1478 description: "Converts an array to a single row",
1479 });
1480 registry.register_eager("WRAPCOLS", wrapcols_fn, FunctionMeta {
1481 category: "array",
1482 signature: "WRAPCOLS(vector, wrap_count, [pad_with])",
1483 description: "Wraps a vector into columns of the given length",
1484 });
1485 registry.register_eager("WRAPROWS", wraprows_fn, FunctionMeta {
1486 category: "array",
1487 signature: "WRAPROWS(vector, wrap_count, [pad_with])",
1488 description: "Wraps a vector into rows of the given length",
1489 });
1490 registry.register_eager("SORT", sort_fn, FunctionMeta {
1491 category: "array",
1492 signature: "SORT(array, [sort_index], [sort_order], [by_col])",
1493 description: "Sorts an array",
1494 });
1495 registry.register_eager("SORTBY", sortby_fn, FunctionMeta {
1496 category: "array",
1497 signature: "SORTBY(array, by_array1, [sort_order1], ...)",
1498 description: "Sorts an array based on the values in corresponding arrays",
1499 });
1500 registry.register_eager("UNIQUE", unique_fn, FunctionMeta {
1501 category: "array",
1502 signature: "UNIQUE(array, [by_col], [exactly_once])",
1503 description: "Returns unique rows or columns from an array",
1504 });
1505 registry.register_eager("SUMPRODUCT", sumproduct_fn, FunctionMeta {
1506 category: "array",
1507 signature: "SUMPRODUCT(array1, [array2], ...)",
1508 description: "Returns the sum of products of corresponding elements",
1509 });
1510 registry.register_eager("SUMXMY2", sumxmy2_fn, FunctionMeta {
1511 category: "array",
1512 signature: "SUMXMY2(array_x, array_y)",
1513 description: "Returns sum of squares of differences",
1514 });
1515 registry.register_eager("SUMX2MY2", sumx2my2_fn, FunctionMeta {
1516 category: "array",
1517 signature: "SUMX2MY2(array_x, array_y)",
1518 description: "Returns sum of (x^2 - y^2)",
1519 });
1520 registry.register_eager("SUMX2PY2", sumx2py2_fn, FunctionMeta {
1521 category: "array",
1522 signature: "SUMX2PY2(array_x, array_y)",
1523 description: "Returns sum of (x^2 + y^2)",
1524 });
1525 registry.register_eager("MMULT", mmult_fn, FunctionMeta {
1526 category: "array",
1527 signature: "MMULT(array1, array2)",
1528 description: "Returns the matrix product of two arrays",
1529 });
1530 registry.register_eager("MDETERM", mdeterm_fn, FunctionMeta {
1531 category: "array",
1532 signature: "MDETERM(array)",
1533 description: "Returns the matrix determinant",
1534 });
1535 registry.register_eager("MINVERSE", minverse_fn, FunctionMeta {
1536 category: "array",
1537 signature: "MINVERSE(array)",
1538 description: "Returns the matrix inverse",
1539 });
1540 registry.register_eager("FREQUENCY", frequency_fn, FunctionMeta {
1541 category: "array",
1542 signature: "FREQUENCY(data, bins)",
1543 description: "Calculates the frequency distribution of values",
1544 });
1545 registry.register_eager("LINEST", linest_fn, FunctionMeta {
1546 category: "array",
1547 signature: "LINEST(known_y, [known_x], [const], [stats])",
1548 description: "Returns linear regression statistics",
1549 });
1550 registry.register_eager("LOGEST", logest_fn, FunctionMeta {
1551 category: "array",
1552 signature: "LOGEST(known_y, [known_x], [const], [stats])",
1553 description: "Returns exponential regression statistics",
1554 });
1555 registry.register_eager("TREND", trend_fn, FunctionMeta {
1556 category: "array",
1557 signature: "TREND(known_y, [known_x], [new_x], [const])",
1558 description: "Returns values along a linear trend",
1559 });
1560 registry.register_eager("GROWTH", growth_fn, FunctionMeta {
1561 category: "array",
1562 signature: "GROWTH(known_y, [known_x], [new_x], [const])",
1563 description: "Returns values along an exponential trend",
1564 });
1565 registry.register_lazy("BYROW", byrow_lazy_fn, FunctionMeta {
1566 category: "array",
1567 signature: "BYROW(array, lambda)",
1568 description: "Applies a LAMBDA to each row of an array",
1569 });
1570 registry.register_lazy("BYCOL", bycol_lazy_fn, FunctionMeta {
1571 category: "array",
1572 signature: "BYCOL(array, lambda)",
1573 description: "Applies a LAMBDA to each column of an array",
1574 });
1575 registry.register_lazy("MAP", map_lazy_fn, FunctionMeta {
1576 category: "array",
1577 signature: "MAP(array1, [array2, ...], lambda)",
1578 description: "Maps a LAMBDA over one or more arrays",
1579 });
1580 registry.register_lazy("REDUCE", reduce_lazy_fn, FunctionMeta {
1581 category: "array",
1582 signature: "REDUCE(initial_value, array, lambda)",
1583 description: "Reduces an array to a single value using a LAMBDA",
1584 });
1585 registry.register_lazy("SCAN", scan_lazy_fn, FunctionMeta {
1586 category: "array",
1587 signature: "SCAN(initial_value, array, lambda)",
1588 description: "Returns running accumulation using a LAMBDA",
1589 });
1590 registry.register_lazy("MAKEARRAY", makearray_lazy_fn, FunctionMeta {
1591 category: "array",
1592 signature: "MAKEARRAY(rows, cols, lambda)",
1593 description: "Creates an array using a LAMBDA for each cell value",
1594 });
1595 registry.register_lazy("ARRAYFORMULA", arrayformula_lazy_fn, FunctionMeta {
1596 category: "array",
1597 signature: "ARRAYFORMULA(array_formula)",
1598 description: "Evaluates a formula as an array formula",
1599 });
1600}
1601
1602#[cfg(test)]
1603mod tests;