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