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
77fn values_equal_1d(a: &Value, b: &Value) -> bool {
79 match (a, b) {
80 (Value::Number(x), Value::Number(y)) => x == y,
81 (Value::Bool(x), Value::Bool(y)) => x == y,
82 (Value::Text(x), Value::Text(y)) => x.to_uppercase() == y.to_uppercase(),
83 (Value::Empty, Value::Empty) => true,
84 _ => false,
85 }
86}
87
88pub(crate) fn rows_fn(args: &[Value]) -> Value {
91 if let Some(e) = check_arity(args, 1, 1) {
92 return e;
93 }
94 let grid = to_2d(&args[0]);
95 Value::Number(grid.len() as f64)
96}
97
98pub(crate) fn columns_fn(args: &[Value]) -> Value {
101 if let Some(e) = check_arity(args, 1, 1) {
102 return e;
103 }
104 let grid = to_2d(&args[0]);
105 let cols = grid.first().map(|r| r.len()).unwrap_or(0);
106 Value::Number(cols as f64)
107}
108
109fn index_fn(args: &[Value]) -> Value {
113 if let Some(e) = check_arity(args, 1, 3) {
114 return e;
115 }
116 let grid = to_2d(&args[0]);
117 let nrows = grid.len();
118 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
119
120 let row_idx = if args.len() >= 2 {
121 match to_f64(&args[1]) {
122 Some(n) => {
123 let r = n as isize;
124 if r < 0 {
125 (nrows as isize + r + 1) as usize
126 } else {
127 r as usize
128 }
129 }
130 None => return Value::Error(ErrorKind::Value),
131 }
132 } else {
133 0 };
135
136 let col_idx = if args.len() >= 3 {
137 match to_f64(&args[2]) {
138 Some(n) => {
139 let c = n as isize;
140 if c < 0 {
141 (ncols as isize + c + 1) as usize
142 } else {
143 c as usize
144 }
145 }
146 None => return Value::Error(ErrorKind::Value),
147 }
148 } else {
149 0 };
151
152 if row_idx > 0 && col_idx > 0 {
154 if row_idx > nrows || col_idx > ncols {
155 return Value::Error(ErrorKind::Ref);
156 }
157 return grid[row_idx - 1][col_idx - 1].clone();
158 }
159
160 if row_idx > 0 && col_idx == 0 {
162 if nrows == 1 && ncols > 1 {
164 if row_idx > ncols {
165 return Value::Error(ErrorKind::Ref);
166 }
167 return grid[0][row_idx - 1].clone();
168 }
169 if row_idx > nrows {
170 return Value::Error(ErrorKind::Ref);
171 }
172 let row = grid[row_idx - 1].clone();
173 if row.len() == 1 {
174 return row.into_iter().next().unwrap();
175 }
176 return Value::Array(row);
177 }
178
179 if row_idx == 0 && col_idx > 0 {
181 if col_idx > ncols {
182 return Value::Error(ErrorKind::Ref);
183 }
184 let col: Vec<Value> = grid.iter().map(|r| r[col_idx - 1].clone()).collect();
185 if col.len() == 1 {
186 return col.into_iter().next().unwrap();
187 }
188 return from_2d(col.into_iter().map(|v| vec![v]).collect());
189 }
190
191 args[0].clone()
193}
194
195pub(crate) fn transpose_fn(args: &[Value]) -> Value {
198 if let Some(e) = check_arity(args, 1, 1) {
199 return e;
200 }
201 let grid = to_2d(&args[0]);
202 if grid.is_empty() {
203 return Value::Array(vec![]);
204 }
205 let nrows = grid.len();
206 let ncols = grid[0].len();
207 let transposed: Vec<Vec<Value>> = (0..ncols)
208 .map(|c| (0..nrows).map(|r| grid[r][c].clone()).collect())
209 .collect();
210 from_2d(transposed)
211}
212
213pub(crate) fn array_constrain_fn(args: &[Value]) -> Value {
216 if let Some(e) = check_arity(args, 3, 3) {
217 return e;
218 }
219 let grid = to_2d(&args[0]);
220 let num_rows = match to_f64(&args[1]) {
221 Some(n) if n >= 1.0 => n as usize,
222 _ => return Value::Error(ErrorKind::Value),
223 };
224 let num_cols = match to_f64(&args[2]) {
225 Some(n) if n >= 1.0 => n as usize,
226 _ => return Value::Error(ErrorKind::Value),
227 };
228 let rows_to_take = num_rows.min(grid.len());
229 let result: Vec<Vec<Value>> = grid[..rows_to_take]
230 .iter()
231 .map(|row| {
232 let cols_to_take = num_cols.min(row.len());
233 row[..cols_to_take].to_vec()
234 })
235 .collect();
236 from_2d(result)
237}
238
239fn choosecols_fn(args: &[Value]) -> Value {
242 if let Some(e) = check_arity(args, 2, usize::MAX) {
243 return e;
244 }
245 let grid = to_2d(&args[0]);
246 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
247 let mut selected_cols: Vec<usize> = Vec::new();
248 for col_arg in &args[1..] {
249 match to_f64(col_arg) {
250 Some(0.0) => return Value::Error(ErrorKind::Value),
251 Some(n) => {
252 let idx = if n < 0.0 {
253 let i = (ncols as isize + n as isize) as usize;
254 if n as isize + (ncols as isize) < 0 {
255 return Value::Error(ErrorKind::Value);
256 }
257 i
258 } else {
259 let i = n as usize - 1;
260 if i >= ncols {
261 return Value::Error(ErrorKind::Value);
262 }
263 i
264 };
265 selected_cols.push(idx);
266 }
267 None => return Value::Error(ErrorKind::Value),
268 }
269 }
270 let result: Vec<Vec<Value>> = grid
271 .iter()
272 .map(|row| {
273 selected_cols
274 .iter()
275 .map(|&c| row.get(c).cloned().unwrap_or(Value::Empty))
276 .collect()
277 })
278 .collect();
279 from_2d(result)
280}
281
282fn chooserows_fn(args: &[Value]) -> Value {
285 if let Some(e) = check_arity(args, 2, usize::MAX) {
286 return e;
287 }
288 let grid = to_2d(&args[0]);
289 let nrows = grid.len();
290 let mut selected_rows: Vec<usize> = Vec::new();
291 for row_arg in &args[1..] {
292 match to_f64(row_arg) {
293 Some(0.0) => return Value::Error(ErrorKind::Value),
294 Some(n) => {
295 let idx = if n < 0.0 {
296 let i = (nrows as isize + n as isize) as usize;
297 if n as isize + (nrows as isize) < 0 {
298 return Value::Error(ErrorKind::Value);
299 }
300 i
301 } else {
302 let i = n as usize - 1;
303 if i >= nrows {
304 return Value::Error(ErrorKind::Value);
305 }
306 i
307 };
308 selected_rows.push(idx);
309 }
310 None => return Value::Error(ErrorKind::Value),
311 }
312 }
313 let result: Vec<Vec<Value>> = selected_rows
314 .iter()
315 .map(|&r| grid.get(r).cloned().unwrap_or_default())
316 .collect();
317 from_2d(result)
318}
319
320pub(crate) fn flatten_fn(args: &[Value]) -> Value {
324 if let Some(e) = check_arity(args, 1, 1) {
325 return e;
326 }
327 let flat = flatten_val(&args[0]);
328 let col: Vec<Vec<Value>> = flat.into_iter().map(|v| vec![v]).collect();
330 from_2d(col)
331}
332
333fn hstack_fn(args: &[Value]) -> Value {
336 if let Some(e) = check_arity(args, 1, usize::MAX) {
337 return e;
338 }
339 let grids: Vec<Vec<Vec<Value>>> = args.iter().map(to_2d).collect();
340 let nrows = grids.iter().map(|g| g.len()).max().unwrap_or(0);
341 let result: Vec<Vec<Value>> = (0..nrows)
342 .map(|r| {
343 grids
344 .iter()
345 .flat_map(|g| {
346 g.get(r).cloned().unwrap_or_default()
347 })
348 .collect()
349 })
350 .collect();
351 from_2d(result)
352}
353
354fn vstack_fn(args: &[Value]) -> Value {
357 if let Some(e) = check_arity(args, 1, usize::MAX) {
358 return e;
359 }
360 let mut result: Vec<Vec<Value>> = Vec::new();
361 for arg in args {
362 let grid = to_2d(arg);
363 result.extend(grid);
364 }
365 from_2d(result)
366}
367
368fn tocol_fn(args: &[Value]) -> Value {
372 if let Some(e) = check_arity(args, 1, 3) {
373 return e;
374 }
375 let flat = flatten_val(&args[0]);
376 let col: Vec<Vec<Value>> = flat.into_iter().map(|v| vec![v]).collect();
377 from_2d(col)
378}
379
380fn torow_fn(args: &[Value]) -> Value {
384 if let Some(e) = check_arity(args, 1, 3) {
385 return e;
386 }
387 let flat = flatten_val(&args[0]);
388 Value::Array(flat)
389}
390
391fn wrapcols_fn(args: &[Value]) -> Value {
396 if let Some(e) = check_arity(args, 2, 3) {
397 return e;
398 }
399 let flat = flatten_val(&args[0]);
400 let wrap_count = match to_f64(&args[1]) {
401 Some(n) if n >= 1.0 => n as usize,
402 _ => return Value::Error(ErrorKind::Value),
403 };
404 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
405
406 let ncols = flat.len().div_ceil(wrap_count);
408 let nrows = wrap_count;
409
410 let grid: Vec<Vec<Value>> = (0..nrows)
412 .map(|r| {
413 (0..ncols)
414 .map(|c| {
415 let idx = c * wrap_count + r;
416 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
417 })
418 .collect()
419 })
420 .collect();
421 from_2d(grid)
422}
423
424fn wraprows_fn(args: &[Value]) -> Value {
428 if let Some(e) = check_arity(args, 2, 3) {
429 return e;
430 }
431 let flat = flatten_val(&args[0]);
432 let wrap_count = match to_f64(&args[1]) {
433 Some(n) if n >= 1.0 => n as usize,
434 _ => return Value::Error(ErrorKind::Value),
435 };
436 let pad = args.get(2).cloned().unwrap_or(Value::Empty);
437
438 let nrows = flat.len().div_ceil(wrap_count);
439 let grid: Vec<Vec<Value>> = (0..nrows)
440 .map(|r| {
441 (0..wrap_count)
442 .map(|c| {
443 let idx = r * wrap_count + c;
444 flat.get(idx).cloned().unwrap_or_else(|| pad.clone())
445 })
446 .collect()
447 })
448 .collect();
449 from_2d(grid)
450}
451
452pub(crate) fn sort_fn(args: &[Value]) -> Value {
455 if let Some(e) = check_arity(args, 1, 4) {
456 return e;
457 }
458 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
459 let mut grid = to_2d(&args[0]);
460 let sort_col = if args.len() >= 2 {
461 match to_f64(&args[1]) {
462 Some(n) if n >= 1.0 => n as usize - 1,
463 Some(_) => return Value::Error(ErrorKind::Value),
464 None => 0,
465 }
466 } else {
467 0
468 };
469 let ascending = if args.len() >= 3 {
470 match &args[2] {
471 Value::Number(n) => *n >= 0.0,
472 Value::Bool(b) => *b,
473 _ => true,
474 }
475 } else {
476 true
477 };
478
479 if is_1d {
480 let mut elems = grid.into_iter().next().unwrap_or_default();
482 elems.sort_by(|a, b| {
483 let cmp = compare_values_sort(a, b);
484 if ascending { cmp } else { cmp.reverse() }
485 });
486 return Value::Array(elems);
487 }
488
489 grid.sort_by(|a, b| {
490 let va = a.get(sort_col).unwrap_or(&Value::Empty);
491 let vb = b.get(sort_col).unwrap_or(&Value::Empty);
492 let cmp = compare_values_sort(va, vb);
493 if ascending { cmp } else { cmp.reverse() }
494 });
495 from_2d(grid)
496}
497
498fn compare_values_sort(a: &Value, b: &Value) -> std::cmp::Ordering {
499 match (a, b) {
500 (Value::Number(x), Value::Number(y)) => x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal),
501 (Value::Text(x), Value::Text(y)) => x.cmp(y),
502 (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
503 _ => std::cmp::Ordering::Equal,
504 }
505}
506
507fn sortby_fn(args: &[Value]) -> Value {
510 if let Some(e) = check_arity(args, 2, usize::MAX) {
511 return e;
512 }
513 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
514
515 if is_1d {
516 let elems = flatten_val(&args[0]);
518 let n = elems.len();
519
520 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
521 let mut i = 1;
522 while i < args.len() {
523 let key_vals = flatten_val(&args[i]);
524 if key_vals.len() != n {
525 return Value::Error(ErrorKind::Value);
526 }
527 let ascending = if i + 1 < args.len() {
528 match to_f64(&args[i + 1]) {
529 Some(v) => v >= 0.0,
530 None => true,
531 }
532 } else {
533 true
534 };
535 sort_keys.push((key_vals, ascending));
536 i += 2;
537 }
538
539 let mut indices: Vec<usize> = (0..n).collect();
540 indices.sort_by(|&ra, &rb| {
541 for (keys, asc) in &sort_keys {
542 let va = keys.get(ra).unwrap_or(&Value::Empty);
543 let vb = keys.get(rb).unwrap_or(&Value::Empty);
544 let cmp = compare_values_sort(va, vb);
545 if cmp != std::cmp::Ordering::Equal {
546 return if *asc { cmp } else { cmp.reverse() };
547 }
548 }
549 std::cmp::Ordering::Equal
550 });
551
552 return Value::Array(indices.iter().map(|&r| elems[r].clone()).collect());
553 }
554
555 let grid = to_2d(&args[0]);
556 let nrows = grid.len();
557
558 let mut sort_keys: Vec<(Vec<Value>, bool)> = Vec::new();
560 let mut i = 1;
561 while i < args.len() {
562 let key_vals = flatten_val(&args[i]);
563 if key_vals.len() != nrows && nrows > 1 {
564 return Value::Error(ErrorKind::Value);
565 }
566 let ascending = if i + 1 < args.len() {
567 match to_f64(&args[i + 1]) {
568 Some(n) => n >= 0.0,
569 None => true,
570 }
571 } else {
572 true
573 };
574 sort_keys.push((key_vals, ascending));
575 i += 2;
576 }
577
578 let mut indices: Vec<usize> = (0..nrows).collect();
579 indices.sort_by(|&ra, &rb| {
580 for (keys, asc) in &sort_keys {
581 let va = keys.get(ra).unwrap_or(&Value::Empty);
582 let vb = keys.get(rb).unwrap_or(&Value::Empty);
583 let cmp = compare_values_sort(va, vb);
584 if cmp != std::cmp::Ordering::Equal {
585 return if *asc { cmp } else { cmp.reverse() };
586 }
587 }
588 std::cmp::Ordering::Equal
589 });
590
591 let sorted: Vec<Vec<Value>> = indices.iter().map(|&r| grid[r].clone()).collect();
592 drop(grid);
593 from_2d(sorted)
594}
595
596pub(crate) fn unique_fn(args: &[Value]) -> Value {
599 if let Some(e) = check_arity(args, 1, 3) {
600 return e;
601 }
602 let is_1d = matches!(&args[0], Value::Array(outer) if !outer.iter().any(|e| matches!(e, Value::Array(_))));
603 let grid = to_2d(&args[0]);
604 let by_col = args.get(1).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
606 let exactly_once = args.get(2).map(|v| matches!(v, Value::Bool(true))).unwrap_or(false);
607
608 if is_1d && !by_col {
610 let elems = flatten_val(&args[0]);
611 let mut seen: Vec<Value> = Vec::new();
612 let mut counts: Vec<usize> = Vec::new();
613 for elem in &elems {
614 if let Some(pos) = seen.iter().position(|s| values_equal_1d(s, elem)) {
615 counts[pos] += 1;
616 } else {
617 seen.push(elem.clone());
618 counts.push(1);
619 }
620 }
621 let result: Vec<Value> = seen.into_iter().zip(counts)
622 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
623 .map(|(v, _)| v)
624 .collect();
625 return Value::Array(result);
626 }
627
628 if by_col {
629 let nrows = grid.len();
631 if nrows == 0 {
632 return from_2d(vec![]);
633 }
634 let ncols = grid[0].len();
635 let columns: Vec<Vec<Value>> = (0..ncols)
637 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
638 .collect();
639 let mut seen_cols: Vec<Vec<Value>> = Vec::new();
640 let mut counts: Vec<usize> = Vec::new();
641 for col in columns {
642 if let Some(pos) = seen_cols.iter().position(|sc| sc == &col) {
643 counts[pos] += 1;
644 } else {
645 seen_cols.push(col);
646 counts.push(1);
647 }
648 }
649 let result_cols: Vec<Vec<Value>> = seen_cols
650 .into_iter()
651 .zip(counts)
652 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
653 .map(|(col, _)| col)
654 .collect();
655 let ncols2 = result_cols.len();
657 let result: Vec<Vec<Value>> = (0..nrows)
658 .map(|r| (0..ncols2).map(|c| result_cols[c][r].clone()).collect())
659 .collect();
660 return from_2d(result);
661 }
662
663 let mut seen_rows: Vec<Vec<Value>> = Vec::new();
665 let mut counts: Vec<usize> = Vec::new();
666 for row in &grid {
667 if let Some(pos) = seen_rows.iter().position(|sr| sr == row) {
668 counts[pos] += 1;
669 } else {
670 seen_rows.push(row.clone());
671 counts.push(1);
672 }
673 }
674 let result: Vec<Vec<Value>> = seen_rows
675 .into_iter()
676 .zip(counts)
677 .filter(|(_, cnt)| !exactly_once || *cnt == 1)
678 .map(|(row, _)| row)
679 .collect();
680 from_2d(result)
681}
682
683pub(crate) fn sumproduct_fn(args: &[Value]) -> Value {
686 if let Some(e) = check_arity(args, 1, usize::MAX) {
687 return e;
688 }
689 let arrays: Vec<Vec<Value>> = args.iter().map(flatten_val).collect();
690 let len = arrays[0].len();
691 for arr in &arrays[1..] {
693 if arr.len() != len {
694 return Value::Error(ErrorKind::Value);
695 }
696 }
697 let mut sum = 0.0;
698 for i in 0..len {
699 let mut prod = 1.0;
700 for arr in &arrays {
701 match to_f64(&arr[i]) {
702 Some(n) => prod *= n,
703 None => return Value::Error(ErrorKind::Value),
704 }
705 }
706 sum += prod;
707 }
708 Value::Number(sum)
709}
710
711fn sumxmy2_fn(args: &[Value]) -> Value {
714 if let Some(e) = check_arity(args, 2, 2) {
715 return e;
716 }
717 let xs = flatten_val(&args[0]);
718 let ys = flatten_val(&args[1]);
719 if xs.len() != ys.len() {
720 return Value::Error(ErrorKind::Value);
721 }
722 let mut sum = 0.0;
723 for (x, y) in xs.iter().zip(ys.iter()) {
724 let xn = match to_f64(x) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
725 let yn = match to_f64(y) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
726 sum += (xn - yn).powi(2);
727 }
728 Value::Number(sum)
729}
730
731fn sumx2my2_fn(args: &[Value]) -> Value {
734 if let Some(e) = check_arity(args, 2, 2) {
735 return e;
736 }
737 let xs = flatten_val(&args[0]);
738 let ys = flatten_val(&args[1]);
739 if xs.len() != ys.len() {
740 return Value::Error(ErrorKind::Value);
741 }
742 let mut sum = 0.0;
743 for (x, y) in xs.iter().zip(ys.iter()) {
744 let xn = match to_f64(x) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
745 let yn = match to_f64(y) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
746 sum += xn * xn - yn * yn;
747 }
748 Value::Number(sum)
749}
750
751fn sumx2py2_fn(args: &[Value]) -> Value {
754 if let Some(e) = check_arity(args, 2, 2) {
755 return e;
756 }
757 let xs = flatten_val(&args[0]);
758 let ys = flatten_val(&args[1]);
759 if xs.len() != ys.len() {
760 return Value::Error(ErrorKind::Value);
761 }
762 let mut sum = 0.0;
763 for (x, y) in xs.iter().zip(ys.iter()) {
764 let xn = match to_f64(x) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
765 let yn = match to_f64(y) { Some(n) => n, None => return Value::Error(ErrorKind::Value) };
766 sum += xn * xn + yn * yn;
767 }
768 Value::Number(sum)
769}
770
771fn mmult_fn(args: &[Value]) -> Value {
774 if let Some(e) = check_arity(args, 2, 2) {
775 return e;
776 }
777 let a = to_2d(&args[0]);
778 let b = to_2d(&args[1]);
779 let n = a.first().map(|r| r.len()).unwrap_or(0);
780 let p = b.first().map(|r| r.len()).unwrap_or(0);
781 if b.len() != n {
782 return Value::Error(ErrorKind::Value);
783 }
784 let af: Vec<Vec<f64>> = a.iter().map(|row| {
786 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
787 }).collect();
788 let bf: Vec<Vec<f64>> = b.iter().map(|row| {
789 row.iter().map(|v| to_f64(v).unwrap_or(f64::NAN)).collect()
790 }).collect();
791 if af.iter().any(|r| r.iter().any(|v| v.is_nan())) || bf.iter().any(|r| r.iter().any(|v| v.is_nan())) {
792 return Value::Error(ErrorKind::Value);
793 }
794 let result: Vec<Vec<Value>> = af.iter().map(|row_a| {
795 (0..p).map(|j| {
796 let sum: f64 = row_a.iter().enumerate().map(|(k, &av)| av * bf[k][j]).sum();
797 Value::Number(sum)
798 }).collect()
799 }).collect();
800 from_2d(result)
801}
802
803fn mdeterm_fn(args: &[Value]) -> Value {
806 if let Some(e) = check_arity(args, 1, 1) {
807 return e;
808 }
809 let grid = to_2d(&args[0]);
810 let n = grid.len();
811 if n == 0 {
812 return Value::Error(ErrorKind::Value);
813 }
814 for row in &grid {
815 if row.len() != n {
816 return Value::Error(ErrorKind::Value);
817 }
818 }
819 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
821 for row in &grid {
822 let mut r = Vec::with_capacity(n);
823 for v in row {
824 match to_f64(v) {
825 Some(x) => r.push(x),
826 None => return Value::Error(ErrorKind::Value),
827 }
828 }
829 mat.push(r);
830 }
831 Value::Number(determinant(&mat))
832}
833
834fn determinant(mat: &[Vec<f64>]) -> f64 {
835 let n = mat.len();
836 if n == 1 {
837 return mat[0][0];
838 }
839 if n == 2 {
840 return mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
841 }
842 let mut det = 0.0;
843 for c in 0..n {
844 let minor: Vec<Vec<f64>> = (1..n)
845 .map(|r| {
846 (0..n)
847 .filter(|&cc| cc != c)
848 .map(|cc| mat[r][cc])
849 .collect()
850 })
851 .collect();
852 let sign = if c % 2 == 0 { 1.0 } else { -1.0 };
853 det += sign * mat[0][c] * determinant(&minor);
854 }
855 det
856}
857
858fn minverse_fn(args: &[Value]) -> Value {
861 if let Some(e) = check_arity(args, 1, 1) {
862 return e;
863 }
864 let grid = to_2d(&args[0]);
865 let n = grid.len();
866 if n == 0 {
867 return Value::Error(ErrorKind::Value);
868 }
869 for row in &grid {
870 if row.len() != n {
871 return Value::Error(ErrorKind::Value);
872 }
873 }
874 let mut mat: Vec<Vec<f64>> = Vec::with_capacity(n);
875 for row in &grid {
876 let mut r = Vec::with_capacity(n);
877 for v in row {
878 match to_f64(v) {
879 Some(x) => r.push(x),
880 None => return Value::Error(ErrorKind::Value),
881 }
882 }
883 mat.push(r);
884 }
885 match invert_matrix(mat) {
886 Some(inv) => from_2d(inv.into_iter().map(|r| r.into_iter().map(Value::Number).collect()).collect()),
887 None => Value::Error(ErrorKind::Num),
888 }
889}
890
891fn invert_matrix(mut mat: Vec<Vec<f64>>) -> Option<Vec<Vec<f64>>> {
892 let n = mat.len();
893 let mut inv: Vec<Vec<f64>> = (0..n)
895 .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
896 .collect();
897 for col in 0..n {
898 let pivot = (col..n).max_by(|&a, &b| mat[a][col].abs().partial_cmp(&mat[b][col].abs()).unwrap_or(std::cmp::Ordering::Equal))?;
900 if mat[pivot][col].abs() < 1e-12 {
901 return None; }
903 mat.swap(col, pivot);
904 inv.swap(col, pivot);
905 let div = mat[col][col];
906 for j in 0..n {
907 mat[col][j] /= div;
908 inv[col][j] /= div;
909 }
910 for r in 0..n {
911 if r != col {
912 let factor = mat[r][col];
913 for j in 0..n {
914 mat[r][j] -= factor * mat[col][j];
915 inv[r][j] -= factor * inv[col][j];
916 }
917 }
918 }
919 }
920 Some(inv)
921}
922
923fn frequency_fn(_args: &[Value]) -> Value {
927 Value::Error(ErrorKind::Ref)
928}
929
930fn linest_fn(args: &[Value]) -> Value {
934 if let Some(e) = check_arity(args, 1, 4) {
935 return e;
936 }
937 let ys = flatten_val(&args[0]);
938 let n = ys.len();
939 let xs: Vec<f64> = if args.len() >= 2 {
940 flatten_val(&args[1]).iter().filter_map(to_f64).collect()
941 } else {
942 (1..=n).map(|i| i as f64).collect()
943 };
944 if xs.len() != n || n < 2 {
945 return Value::Error(ErrorKind::Value);
946 }
947 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
948 if y_vals.len() != n {
949 return Value::Error(ErrorKind::Value);
950 }
951 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
952 Value::Array(vec![Value::Number(slope), Value::Number(intercept)])
953}
954
955fn simple_linear_regression(xs: &[f64], ys: &[f64]) -> (f64, f64) {
956 let n = xs.len() as f64;
957 let sum_x: f64 = xs.iter().sum();
958 let sum_y: f64 = ys.iter().sum();
959 let sum_xy: f64 = xs.iter().zip(ys.iter()).map(|(x, y)| x * y).sum();
960 let sum_xx: f64 = xs.iter().map(|x| x * x).sum();
961 let denom = n * sum_xx - sum_x * sum_x;
962 if denom.abs() < 1e-15 {
963 let intercept = sum_y / n;
964 return (0.0, intercept);
965 }
966 let slope = (n * sum_xy - sum_x * sum_y) / denom;
967 let intercept = (sum_y - slope * sum_x) / n;
968 (slope, intercept)
969}
970
971fn logest_fn(args: &[Value]) -> Value {
975 if let Some(e) = check_arity(args, 1, 4) {
976 return e;
977 }
978 let ys = flatten_val(&args[0]);
979 let n = ys.len();
980 let xs: Vec<f64> = if args.len() >= 2 {
981 flatten_val(&args[1]).iter().filter_map(to_f64).collect()
982 } else {
983 (1..=n).map(|i| i as f64).collect()
984 };
985 if xs.len() != n || n < 2 {
986 return Value::Error(ErrorKind::Value);
987 }
988 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
989 if y_vals.len() != n {
990 return Value::Error(ErrorKind::Value);
991 }
992 let log_y: Vec<f64> = y_vals.iter().map(|&y| y.ln()).collect();
994 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
995 return Value::Error(ErrorKind::Num);
996 }
997 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
998 let base = log_base.exp();
999 let intercept = log_intercept.exp();
1000 Value::Array(vec![Value::Number(base), Value::Number(intercept)])
1001}
1002
1003fn trend_fn(args: &[Value]) -> Value {
1007 if let Some(e) = check_arity(args, 1, 4) {
1008 return e;
1009 }
1010 let ys = flatten_val(&args[0]);
1011 let n = ys.len();
1012 let xs: Vec<f64> = if args.len() >= 2 {
1013 flatten_val(&args[1]).iter().filter_map(to_f64).collect()
1014 } else {
1015 (1..=n).map(|i| i as f64).collect()
1016 };
1017 if xs.len() != n || n < 2 {
1018 return Value::Error(ErrorKind::Value);
1019 }
1020 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1021 if y_vals.len() != n {
1022 return Value::Error(ErrorKind::Value);
1023 }
1024 let new_xs: Vec<f64> = if args.len() >= 3 {
1025 flatten_val(&args[2]).iter().filter_map(to_f64).collect()
1026 } else {
1027 xs.clone()
1028 };
1029 let (slope, intercept) = simple_linear_regression(&xs, &y_vals);
1030 let result: Vec<Value> = new_xs.iter().map(|&x| Value::Number(slope * x + intercept)).collect();
1031 Value::Array(result)
1032}
1033
1034fn growth_fn(args: &[Value]) -> Value {
1038 if let Some(e) = check_arity(args, 1, 4) {
1039 return e;
1040 }
1041 let ys = flatten_val(&args[0]);
1042 let n = ys.len();
1043 let xs: Vec<f64> = if args.len() >= 2 {
1044 flatten_val(&args[1]).iter().filter_map(to_f64).collect()
1045 } else {
1046 (1..=n).map(|i| i as f64).collect()
1047 };
1048 if xs.len() != n || n < 2 {
1049 return Value::Error(ErrorKind::Value);
1050 }
1051 let y_vals: Vec<f64> = ys.iter().filter_map(to_f64).collect();
1052 if y_vals.len() != n {
1053 return Value::Error(ErrorKind::Value);
1054 }
1055 let log_y: Vec<f64> = y_vals.iter().map(|&y| y.ln()).collect();
1056 if log_y.iter().any(|v| v.is_nan() || v.is_infinite()) {
1057 return Value::Error(ErrorKind::Num);
1058 }
1059 let new_xs: Vec<f64> = if args.len() >= 3 && !matches!(args[2], Value::Empty) {
1060 let vals: Vec<f64> = flatten_val(&args[2]).iter().filter_map(to_f64).collect();
1061 if vals.is_empty() { xs.clone() } else { vals }
1062 } else {
1063 xs.clone()
1064 };
1065 if args.len() >= 4 {
1067 let b_false = match &args[3] {
1068 Value::Bool(b) => !b,
1069 Value::Number(n) => *n == 0.0,
1070 _ => false,
1071 };
1072 if b_false {
1073 return Value::Error(ErrorKind::Value);
1074 }
1075 }
1076 let (log_base, log_intercept) = simple_linear_regression(&xs, &log_y);
1077 let result: Vec<Value> = new_xs
1078 .iter()
1079 .map(|&x| Value::Number((log_base * x + log_intercept).exp()))
1080 .collect();
1081 Value::Array(result)
1082}
1083
1084fn apply_lambda(lambda_expr: &Expr, bound_args: &[Value], ctx: &mut EvalCtx<'_>) -> Option<Value> {
1090 match lambda_expr {
1091 Expr::FunctionCall { name, args, .. } if name == "LAMBDA" => {
1092 if args.is_empty() {
1093 return None;
1094 }
1095 let body = &args[args.len() - 1];
1096 let params = &args[..args.len() - 1];
1097 if params.len() != bound_args.len() {
1098 return None;
1099 }
1100 let mut saved: Vec<(String, Value)> = Vec::new();
1102 for (param_expr, val) in params.iter().zip(bound_args.iter()) {
1103 if let Expr::Variable(name, _) = param_expr {
1104 let old = ctx.ctx.get(name);
1105 saved.push((name.clone(), old));
1106 ctx.ctx.set(name.clone(), val.clone());
1107 } else {
1108 return None;
1109 }
1110 }
1111 let result = evaluate_expr(body, ctx);
1112 for (name, old_val) in saved {
1114 ctx.ctx.set(name, old_val);
1115 }
1116 Some(result)
1117 }
1118 _ => None,
1119 }
1120}
1121
1122pub fn byrow_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1125 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1126 return e;
1127 }
1128 let arr_val = evaluate_expr(&args[0], ctx);
1129 if matches!(arr_val, Value::Error(_)) {
1130 return arr_val;
1131 }
1132 let grid = to_2d(&arr_val);
1133 let lambda_expr = &args[1];
1134 let mut results: Vec<Value> = Vec::with_capacity(grid.len());
1135 for row in &grid {
1136 let row_val = Value::Array(row.clone());
1137 match apply_lambda(lambda_expr, &[row_val], ctx) {
1138 Some(v) => results.push(v),
1139 None => return Value::Error(ErrorKind::Value),
1140 }
1141 }
1142 let col: Vec<Vec<Value>> = results.into_iter().map(|v| vec![v]).collect();
1144 from_2d(col)
1145}
1146
1147pub fn bycol_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1150 if let Some(e) = check_arity_len(args.len(), 2, 2) {
1151 return e;
1152 }
1153 let arr_val = evaluate_expr(&args[0], ctx);
1154 if matches!(arr_val, Value::Error(_)) {
1155 return arr_val;
1156 }
1157 let grid = to_2d(&arr_val);
1158 let ncols = grid.first().map(|r| r.len()).unwrap_or(0);
1159 let columns: Vec<Vec<Value>> = (0..ncols)
1161 .map(|c| grid.iter().map(|row| row[c].clone()).collect())
1162 .collect();
1163 let lambda_expr = &args[1];
1164 let mut results: Vec<Value> = Vec::with_capacity(ncols);
1165 for col in columns {
1166 let col_val = Value::Array(col);
1168 match apply_lambda(lambda_expr, &[col_val], ctx) {
1169 Some(v) => results.push(v),
1170 None => return Value::Error(ErrorKind::Value),
1171 }
1172 }
1173 Value::Array(results)
1175}
1176
1177pub fn map_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1180 if let Some(e) = check_arity_len(args.len(), 2, usize::MAX) {
1181 return e;
1182 }
1183 let lambda_expr = &args[args.len() - 1];
1185 let arr_count = args.len() - 1;
1186 let arrays: Vec<Vec<Value>> = args[..arr_count]
1187 .iter()
1188 .map(|a| {
1189 let v = evaluate_expr(a, ctx);
1190 flatten_val(&v)
1191 })
1192 .collect();
1193 let len = arrays[0].len();
1194 for arr in &arrays[1..] {
1195 if arr.len() != len {
1196 return Value::Error(ErrorKind::Value);
1197 }
1198 }
1199 let mut results: Vec<Value> = Vec::with_capacity(len);
1200 for i in 0..len {
1201 let bound: Vec<Value> = arrays.iter().map(|a| a[i].clone()).collect();
1202 match apply_lambda(lambda_expr, &bound, ctx) {
1203 Some(v) => results.push(v),
1204 None => return Value::Error(ErrorKind::Value),
1205 }
1206 }
1207 let first_grid = to_2d(&evaluate_expr(&args[0], ctx));
1209 if first_grid.len() > 1 {
1210 let ncols = first_grid[0].len();
1212 let nrows = first_grid.len();
1213 let grid: Vec<Vec<Value>> = (0..nrows)
1214 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1215 .collect();
1216 from_2d(grid)
1217 } else {
1218 Value::Array(results)
1219 }
1220}
1221
1222pub fn reduce_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1225 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1226 return e;
1227 }
1228 let initial = evaluate_expr(&args[0], ctx);
1229 if matches!(initial, Value::Error(_)) {
1230 return initial;
1231 }
1232 let arr_val = evaluate_expr(&args[1], ctx);
1233 if matches!(arr_val, Value::Error(_)) {
1234 return arr_val;
1235 }
1236 let items = flatten_val(&arr_val);
1237 let lambda_expr = &args[2];
1238 let mut acc = initial;
1239 for item in &items {
1240 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1241 Some(v) => acc = v,
1242 None => return Value::Error(ErrorKind::Value),
1243 }
1244 }
1245 acc
1246}
1247
1248pub fn scan_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1251 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1252 return e;
1253 }
1254 let initial = evaluate_expr(&args[0], ctx);
1255 if matches!(initial, Value::Error(_)) {
1256 return initial;
1257 }
1258 let arr_val = evaluate_expr(&args[1], ctx);
1259 if matches!(arr_val, Value::Error(_)) {
1260 return arr_val;
1261 }
1262 let grid = to_2d(&arr_val);
1263 let items = flatten_val(&arr_val);
1264 let lambda_expr = &args[2];
1265 let mut acc = initial;
1266 let mut results: Vec<Value> = Vec::with_capacity(items.len());
1267 for item in &items {
1268 match apply_lambda(lambda_expr, &[acc.clone(), item.clone()], ctx) {
1269 Some(v) => {
1270 acc = v.clone();
1271 results.push(v);
1272 }
1273 None => return Value::Error(ErrorKind::Value),
1274 }
1275 }
1276 if grid.len() > 1 {
1278 let ncols = grid[0].len();
1279 let nrows = grid.len();
1280 let result_grid: Vec<Vec<Value>> = (0..nrows)
1281 .map(|r| (0..ncols).map(|c| results[r * ncols + c].clone()).collect())
1282 .collect();
1283 from_2d(result_grid)
1284 } else {
1285 Value::Array(results)
1286 }
1287}
1288
1289pub fn makearray_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
1292 if let Some(e) = check_arity_len(args.len(), 3, 3) {
1293 return e;
1294 }
1295 let rows_val = evaluate_expr(&args[0], ctx);
1296 let cols_val = evaluate_expr(&args[1], ctx);
1297 if matches!(rows_val, Value::Error(_)) {
1298 return rows_val;
1299 }
1300 if matches!(cols_val, Value::Error(_)) {
1301 return cols_val;
1302 }
1303 let nrows = match to_f64(&rows_val) {
1304 Some(n) if n >= 1.0 => n as usize,
1305 _ => return Value::Error(ErrorKind::Value),
1306 };
1307 let ncols = match to_f64(&cols_val) {
1308 Some(n) if n >= 1.0 => n as usize,
1309 _ => return Value::Error(ErrorKind::Value),
1310 };
1311 let lambda_expr = &args[2];
1312 let mut grid: Vec<Vec<Value>> = Vec::with_capacity(nrows);
1313 for r in 1..=nrows {
1314 let mut row = Vec::with_capacity(ncols);
1315 for c in 1..=ncols {
1316 let rv = Value::Number(r as f64);
1317 let cv = Value::Number(c as f64);
1318 match apply_lambda(lambda_expr, &[rv, cv], ctx) {
1319 Some(v) => row.push(v),
1320 None => return Value::Error(ErrorKind::Value),
1321 }
1322 }
1323 grid.push(row);
1324 }
1325 from_2d(grid)
1326}
1327
1328pub fn register_array(registry: &mut Registry) {
1331 registry.register_eager("ROWS", rows_fn, FunctionMeta {
1332 category: "array",
1333 signature: "ROWS(array)",
1334 description: "Returns the number of rows in an array or range",
1335 });
1336 registry.register_eager("COLUMNS", columns_fn, FunctionMeta {
1337 category: "array",
1338 signature: "COLUMNS(array)",
1339 description: "Returns the number of columns in an array or range",
1340 });
1341 registry.register_eager("INDEX", index_fn, FunctionMeta {
1342 category: "array",
1343 signature: "INDEX(array, row, [col])",
1344 description: "Returns the value at the given row and column of an array",
1345 });
1346 registry.register_eager("TRANSPOSE", transpose_fn, FunctionMeta {
1347 category: "array",
1348 signature: "TRANSPOSE(array)",
1349 description: "Transposes the rows and columns of an array",
1350 });
1351 registry.register_eager("ARRAY_CONSTRAIN", array_constrain_fn, FunctionMeta {
1352 category: "array",
1353 signature: "ARRAY_CONSTRAIN(input, num_rows, num_cols)",
1354 description: "Constrains an array to a given number of rows and columns",
1355 });
1356 registry.register_eager("CHOOSECOLS", choosecols_fn, FunctionMeta {
1357 category: "array",
1358 signature: "CHOOSECOLS(array, col_num1, ...)",
1359 description: "Returns selected columns from an array",
1360 });
1361 registry.register_eager("CHOOSEROWS", chooserows_fn, FunctionMeta {
1362 category: "array",
1363 signature: "CHOOSEROWS(array, row_num1, ...)",
1364 description: "Returns selected rows from an array",
1365 });
1366 registry.register_eager("FLATTEN", flatten_fn, FunctionMeta {
1367 category: "array",
1368 signature: "FLATTEN(array)",
1369 description: "Flattens an array into a single column",
1370 });
1371 registry.register_eager("HSTACK", hstack_fn, FunctionMeta {
1372 category: "array",
1373 signature: "HSTACK(array1, ...)",
1374 description: "Horizontally stacks arrays",
1375 });
1376 registry.register_eager("VSTACK", vstack_fn, FunctionMeta {
1377 category: "array",
1378 signature: "VSTACK(array1, ...)",
1379 description: "Vertically stacks arrays",
1380 });
1381 registry.register_eager("TOCOL", tocol_fn, FunctionMeta {
1382 category: "array",
1383 signature: "TOCOL(array, [ignore], [scan_by_col])",
1384 description: "Converts an array to a single column",
1385 });
1386 registry.register_eager("TOROW", torow_fn, FunctionMeta {
1387 category: "array",
1388 signature: "TOROW(array, [ignore], [scan_by_col])",
1389 description: "Converts an array to a single row",
1390 });
1391 registry.register_eager("WRAPCOLS", wrapcols_fn, FunctionMeta {
1392 category: "array",
1393 signature: "WRAPCOLS(vector, wrap_count, [pad_with])",
1394 description: "Wraps a vector into columns of the given length",
1395 });
1396 registry.register_eager("WRAPROWS", wraprows_fn, FunctionMeta {
1397 category: "array",
1398 signature: "WRAPROWS(vector, wrap_count, [pad_with])",
1399 description: "Wraps a vector into rows of the given length",
1400 });
1401 registry.register_eager("SORT", sort_fn, FunctionMeta {
1402 category: "array",
1403 signature: "SORT(array, [sort_index], [sort_order], [by_col])",
1404 description: "Sorts an array",
1405 });
1406 registry.register_eager("SORTBY", sortby_fn, FunctionMeta {
1407 category: "array",
1408 signature: "SORTBY(array, by_array1, [sort_order1], ...)",
1409 description: "Sorts an array based on the values in corresponding arrays",
1410 });
1411 registry.register_eager("UNIQUE", unique_fn, FunctionMeta {
1412 category: "array",
1413 signature: "UNIQUE(array, [by_col], [exactly_once])",
1414 description: "Returns unique rows or columns from an array",
1415 });
1416 registry.register_eager("SUMPRODUCT", sumproduct_fn, FunctionMeta {
1417 category: "array",
1418 signature: "SUMPRODUCT(array1, [array2], ...)",
1419 description: "Returns the sum of products of corresponding elements",
1420 });
1421 registry.register_eager("SUMXMY2", sumxmy2_fn, FunctionMeta {
1422 category: "array",
1423 signature: "SUMXMY2(array_x, array_y)",
1424 description: "Returns sum of squares of differences",
1425 });
1426 registry.register_eager("SUMX2MY2", sumx2my2_fn, FunctionMeta {
1427 category: "array",
1428 signature: "SUMX2MY2(array_x, array_y)",
1429 description: "Returns sum of (x^2 - y^2)",
1430 });
1431 registry.register_eager("SUMX2PY2", sumx2py2_fn, FunctionMeta {
1432 category: "array",
1433 signature: "SUMX2PY2(array_x, array_y)",
1434 description: "Returns sum of (x^2 + y^2)",
1435 });
1436 registry.register_eager("MMULT", mmult_fn, FunctionMeta {
1437 category: "array",
1438 signature: "MMULT(array1, array2)",
1439 description: "Returns the matrix product of two arrays",
1440 });
1441 registry.register_eager("MDETERM", mdeterm_fn, FunctionMeta {
1442 category: "array",
1443 signature: "MDETERM(array)",
1444 description: "Returns the matrix determinant",
1445 });
1446 registry.register_eager("MINVERSE", minverse_fn, FunctionMeta {
1447 category: "array",
1448 signature: "MINVERSE(array)",
1449 description: "Returns the matrix inverse",
1450 });
1451 registry.register_eager("FREQUENCY", frequency_fn, FunctionMeta {
1452 category: "array",
1453 signature: "FREQUENCY(data, bins)",
1454 description: "Calculates the frequency distribution of values",
1455 });
1456 registry.register_eager("LINEST", linest_fn, FunctionMeta {
1457 category: "array",
1458 signature: "LINEST(known_y, [known_x], [const], [stats])",
1459 description: "Returns linear regression statistics",
1460 });
1461 registry.register_eager("LOGEST", logest_fn, FunctionMeta {
1462 category: "array",
1463 signature: "LOGEST(known_y, [known_x], [const], [stats])",
1464 description: "Returns exponential regression statistics",
1465 });
1466 registry.register_eager("TREND", trend_fn, FunctionMeta {
1467 category: "array",
1468 signature: "TREND(known_y, [known_x], [new_x], [const])",
1469 description: "Returns values along a linear trend",
1470 });
1471 registry.register_eager("GROWTH", growth_fn, FunctionMeta {
1472 category: "array",
1473 signature: "GROWTH(known_y, [known_x], [new_x], [const])",
1474 description: "Returns values along an exponential trend",
1475 });
1476 registry.register_lazy("BYROW", byrow_lazy_fn, FunctionMeta {
1477 category: "array",
1478 signature: "BYROW(array, lambda)",
1479 description: "Applies a LAMBDA to each row of an array",
1480 });
1481 registry.register_lazy("BYCOL", bycol_lazy_fn, FunctionMeta {
1482 category: "array",
1483 signature: "BYCOL(array, lambda)",
1484 description: "Applies a LAMBDA to each column of an array",
1485 });
1486 registry.register_lazy("MAP", map_lazy_fn, FunctionMeta {
1487 category: "array",
1488 signature: "MAP(array1, [array2, ...], lambda)",
1489 description: "Maps a LAMBDA over one or more arrays",
1490 });
1491 registry.register_lazy("REDUCE", reduce_lazy_fn, FunctionMeta {
1492 category: "array",
1493 signature: "REDUCE(initial_value, array, lambda)",
1494 description: "Reduces an array to a single value using a LAMBDA",
1495 });
1496 registry.register_lazy("SCAN", scan_lazy_fn, FunctionMeta {
1497 category: "array",
1498 signature: "SCAN(initial_value, array, lambda)",
1499 description: "Returns running accumulation using a LAMBDA",
1500 });
1501 registry.register_lazy("MAKEARRAY", makearray_lazy_fn, FunctionMeta {
1502 category: "array",
1503 signature: "MAKEARRAY(rows, cols, lambda)",
1504 description: "Creates an array using a LAMBDA for each cell value",
1505 });
1506}
1507
1508#[cfg(test)]
1509mod tests;