1use std::collections::BTreeSet;
2
3#[cfg(feature = "linalg")]
4use derive_more::{Display, Error};
5use itertools::Itertools;
6#[cfg(feature = "linalg")]
7use nalgebra::DMatrix;
8#[cfg(feature = "linalg")]
9use ndarray::{Array1, Array2, ShapeBuilder};
10
11use crate::interpreter::coerce::{f64_to_i32, usize_to_f64};
12use crate::interpreter::value::*;
13#[cfg(feature = "linalg")]
14use crate::interpreter::BuiltinContext;
15#[cfg(feature = "linalg")]
16use crate::parser::ast::Expr;
17use minir_macros::builtin;
18#[cfg(feature = "linalg")]
19use minir_macros::interpreter_builtin;
20
21type DimNameVec = Vec<Option<String>>;
22type MatrixDimNames = (Option<DimNameVec>, Option<DimNameVec>);
23
24#[cfg(feature = "linalg")]
28#[derive(Debug, Display, Error)]
29pub enum MathError {
30 #[display("matrix shape error: {}", source)]
31 Shape {
32 #[error(source)]
33 source: ndarray::ShapeError,
34 },
35}
36
37#[cfg(feature = "linalg")]
38impl From<MathError> for RError {
39 fn from(e: MathError) -> Self {
40 RError::from_source(RErrorKind::Other, e)
41 }
42}
43
44#[builtin(min_args = 1)]
53fn builtin_abs(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
54 math_unary(args, f64::abs)
55}
56
57#[builtin(min_args = 1)]
62fn builtin_sqrt(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
63 math_unary(args, f64::sqrt)
64}
65
66#[builtin(min_args = 1)]
71fn builtin_exp(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
72 math_unary(args, f64::exp)
73}
74
75#[builtin(min_args = 1)]
83fn builtin_log(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
84 let base: Option<f64> = named
85 .iter()
86 .find(|(n, _)| n == "base")
87 .and_then(|(_, v)| v.as_vector()?.as_double_scalar())
88 .or_else(|| args.get(1)?.as_vector()?.as_double_scalar());
89
90 match base {
91 Some(b) => {
92 let ln_base = b.ln();
93 match args.first() {
94 Some(RValue::Vector(v)) => {
95 let result: Vec<Option<f64>> = v
96 .to_doubles()
97 .iter()
98 .map(|x| x.map(|f| f.ln() / ln_base))
99 .collect();
100 Ok(RValue::vec(Vector::Double(result.into())))
101 }
102 _ => Err(RError::new(
103 RErrorKind::Argument,
104 "non-numeric argument to mathematical function".to_string(),
105 )),
106 }
107 }
108 None => math_unary(args, f64::ln),
109 }
110}
111
112#[builtin(min_args = 1)]
117fn builtin_log2(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
118 math_unary(args, f64::log2)
119}
120
121#[builtin(min_args = 1)]
126fn builtin_log10(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
127 math_unary(args, f64::log10)
128}
129
130#[builtin(min_args = 1)]
135fn builtin_ceiling(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
136 math_unary(args, f64::ceil)
137}
138
139#[builtin(min_args = 1)]
144fn builtin_floor(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
145 math_unary(args, f64::floor)
146}
147
148#[builtin(min_args = 1)]
153fn builtin_trunc(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
154 math_unary(args, f64::trunc)
155}
156
157#[builtin(min_args = 1)]
162fn builtin_sin(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
163 math_unary(args, f64::sin)
164}
165
166#[builtin(min_args = 1)]
171fn builtin_cos(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
172 math_unary(args, f64::cos)
173}
174
175#[builtin(min_args = 1)]
180fn builtin_tan(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
181 math_unary(args, f64::tan)
182}
183
184#[builtin(min_args = 1)]
192fn builtin_sign(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
193 match args.first() {
194 Some(RValue::Vector(v)) => match &v.inner {
195 Vector::Integer(vals) => {
196 let result: Vec<Option<i64>> = vals
197 .iter()
198 .map(|x| {
199 x.map(|i| match i.cmp(&0) {
200 std::cmp::Ordering::Greater => 1i64,
201 std::cmp::Ordering::Equal => 0i64,
202 std::cmp::Ordering::Less => -1i64,
203 })
204 })
205 .collect();
206 Ok(RValue::vec(Vector::Integer(result.into())))
207 }
208 _ => {
209 let result: Vec<Option<f64>> = v
210 .to_doubles()
211 .iter()
212 .map(|x| {
213 x.map(|f| {
214 if f.is_nan() {
215 f64::NAN
216 } else if f > 0.0 {
217 1.0
218 } else if f < 0.0 {
219 -1.0
220 } else {
221 0.0
222 }
223 })
224 })
225 .collect();
226 Ok(RValue::vec(Vector::Double(result.into())))
227 }
228 },
229 _ => Err(RError::new(
230 RErrorKind::Argument,
231 "non-numeric argument to mathematical function".to_string(),
232 )),
233 }
234}
235
236use super::math_unary_op as math_unary;
237
238#[builtin(min_args = 1)]
245fn builtin_asin(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
246 math_unary(args, f64::asin)
247}
248
249#[builtin(min_args = 1)]
254fn builtin_acos(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
255 math_unary(args, f64::acos)
256}
257
258#[builtin(min_args = 1)]
263fn builtin_atan(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
264 math_unary(args, f64::atan)
265}
266
267#[builtin(min_args = 2)]
276fn builtin_atan2(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
277 math_binary(args, f64::atan2)
278}
279
280#[builtin(min_args = 1)]
289fn builtin_sinh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
290 math_unary(args, f64::sinh)
291}
292
293#[builtin(min_args = 1)]
298fn builtin_cosh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
299 math_unary(args, f64::cosh)
300}
301
302#[builtin(min_args = 1)]
307fn builtin_tanh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
308 math_unary(args, f64::tanh)
309}
310
311#[builtin(min_args = 1)]
320fn builtin_asinh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
321 math_unary(args, f64::asinh)
322}
323
324#[builtin(min_args = 1)]
329fn builtin_acosh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
330 math_unary(args, f64::acosh)
331}
332
333#[builtin(min_args = 1)]
338fn builtin_atanh(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
339 math_unary(args, f64::atanh)
340}
341
342#[builtin(min_args = 1)]
354fn builtin_expm1(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
355 math_unary(args, f64::exp_m1)
356}
357
358#[builtin(min_args = 1)]
366fn builtin_log1p(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
367 math_unary(args, f64::ln_1p)
368}
369
370#[builtin(min_args = 1)]
382fn builtin_gamma(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
383 math_unary(args, libm::tgamma)
384}
385
386#[builtin(min_args = 1)]
393fn builtin_lgamma(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
394 math_unary(args, libm::lgamma)
395}
396
397#[builtin(min_args = 2)]
403fn builtin_beta(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
404 math_binary(args, |a, b| {
405 (libm::lgamma(a) + libm::lgamma(b) - libm::lgamma(a + b)).exp()
406 })
407}
408
409#[builtin(min_args = 2)]
417fn builtin_lbeta(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
418 math_binary(args, |a, b| {
419 libm::lgamma(a) + libm::lgamma(b) - libm::lgamma(a + b)
420 })
421}
422
423#[builtin(min_args = 1)]
430fn builtin_factorial(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
431 math_unary(args, |x| libm::tgamma(x + 1.0))
432}
433
434#[builtin(min_args = 2)]
442fn builtin_choose(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
443 math_binary(args, |n, k| {
444 if k < 0.0 || k > n {
445 return 0.0;
446 }
447 (libm::lgamma(n + 1.0) - libm::lgamma(k + 1.0) - libm::lgamma(n - k + 1.0))
450 .exp()
451 .round()
452 })
453}
454
455#[builtin(min_args = 2)]
464fn builtin_combn(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
465 let pool: Vec<f64> = match args.first() {
467 Some(RValue::Vector(v)) => {
468 let doubles = v.to_doubles();
469 if doubles.len() == 1 {
470 let n = doubles[0].ok_or_else(|| {
472 RError::new(
473 RErrorKind::Argument,
474 "NA in combn first argument".to_string(),
475 )
476 })?;
477 let n_int = n as i64;
478 (1..=n_int).map(|i| i as f64).collect()
479 } else {
480 doubles
481 .into_iter()
482 .map(|x| {
483 x.ok_or_else(|| {
484 RError::new(RErrorKind::Argument, "NA in combn input".to_string())
485 })
486 })
487 .collect::<Result<Vec<f64>, RError>>()?
488 }
489 }
490 _ => {
491 return Err(RError::new(
492 RErrorKind::Argument,
493 "first argument must be numeric".to_string(),
494 ))
495 }
496 };
497
498 let m = match args.get(1) {
499 Some(RValue::Vector(v)) => v.as_integer_scalar().ok_or_else(|| {
500 RError::new(
501 RErrorKind::Argument,
502 "second argument (m) must be a single integer".to_string(),
503 )
504 })?,
505 _ => {
506 return Err(RError::new(
507 RErrorKind::Argument,
508 "second argument (m) must be a single integer".to_string(),
509 ))
510 }
511 };
512
513 let n = pool.len();
514 let m_usize = usize::try_from(m).map_err(|_| {
515 RError::new(
516 RErrorKind::Argument,
517 format!("m must be non-negative, got {m}"),
518 )
519 })?;
520
521 if m_usize > n {
522 return Err(RError::new(
523 RErrorKind::Argument,
524 format!("m ({m_usize}) must be <= n ({n}) in combn"),
525 ));
526 }
527
528 let combos: Vec<Vec<f64>> = (0..n)
530 .combinations(m_usize)
531 .map(|indices| indices.iter().map(|&i| pool[i]).collect())
532 .collect();
533
534 let ncol = combos.len();
535 let nrow = m_usize;
536
537 let mut data: Vec<Option<f64>> = Vec::with_capacity(nrow * ncol);
539 for combo in &combos {
540 for &val in combo {
541 data.push(Some(val));
542 }
543 }
544
545 let mut rv = RVector::from(Vector::Double(data.into()));
546 set_matrix_attrs(&mut rv, nrow, ncol, None, None)?;
547 Ok(RValue::Vector(rv))
548}
549
550#[builtin(min_args = 1)]
563fn builtin_digamma(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
564 math_unary(args, digamma_f64)
565}
566
567#[builtin(min_args = 1)]
576fn builtin_trigamma(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
577 math_unary(args, trigamma_f64)
578}
579
580fn digamma_f64(mut x: f64) -> f64 {
587 if x.is_nan() {
588 return f64::NAN;
589 }
590 if x == f64::NEG_INFINITY {
591 return f64::NAN;
592 }
593 if x == f64::INFINITY {
594 return f64::INFINITY;
595 }
596
597 let mut result = 0.0;
599 if x < 0.0 {
600 let pi_x = std::f64::consts::PI * x;
601 let tan_val = pi_x.tan();
602 if tan_val == 0.0 {
603 return f64::NAN; }
605 result -= std::f64::consts::PI / tan_val;
606 x = 1.0 - x;
607 }
608
609 if x == 0.0 {
611 return f64::NAN;
612 }
613
614 while x < 12.0 {
616 result -= 1.0 / x;
617 x += 1.0;
618 }
619
620 let inv_x = 1.0 / x;
626 let inv_x2 = inv_x * inv_x;
627
628 let mut series = 691.0 / 32760.0; series = series * inv_x2 + (-1.0 / 132.0); series = series * inv_x2 + (1.0 / 240.0); series = series * inv_x2 + (-1.0 / 252.0); series = series * inv_x2 + (1.0 / 120.0); series = series * inv_x2 + (-1.0 / 12.0); series *= inv_x2;
637
638 result += x.ln() - 0.5 * inv_x + series;
639
640 result
641}
642
643fn trigamma_f64(mut x: f64) -> f64 {
650 if x.is_nan() {
651 return f64::NAN;
652 }
653 if x == f64::INFINITY {
654 return 0.0;
655 }
656 if x == f64::NEG_INFINITY {
657 return f64::NAN;
658 }
659
660 let mut result = 0.0;
662 if x < 0.0 {
663 let pi_x = std::f64::consts::PI * x;
664 let sin_val = pi_x.sin();
665 if sin_val == 0.0 {
666 return f64::NAN; }
668 let pi2 = std::f64::consts::PI * std::f64::consts::PI;
669 result += pi2 / (sin_val * sin_val);
670 x = 1.0 - x;
671 return result - trigamma_f64(x);
672 }
673
674 if x == 0.0 {
676 return f64::NAN;
677 }
678
679 while x < 12.0 {
681 result += 1.0 / (x * x);
682 x += 1.0;
683 }
684
685 let inv_x = 1.0 / x;
689 let inv_x2 = inv_x * inv_x;
690
691 let mut series = -691.0 / 2730.0;
694 series = series * inv_x2 + 5.0 / 66.0;
695 series = series * inv_x2 + -1.0 / 30.0;
696 series = series * inv_x2 + 1.0 / 42.0;
697 series = series * inv_x2 + -1.0 / 30.0;
698 series = series * inv_x2 + 1.0 / 6.0;
699 series *= inv_x2 * inv_x; result += inv_x + 0.5 * inv_x2 + series;
702
703 result
704}
705
706#[builtin(name = "besselJ", min_args = 2)]
721fn builtin_bessel_j(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
722 let nu = args
723 .get(1)
724 .and_then(|v| v.as_vector())
725 .and_then(|v| v.as_double_scalar())
726 .ok_or_else(|| {
727 RError::new(
728 RErrorKind::Argument,
729 "besselJ requires a numeric 'nu' (order) argument".to_string(),
730 )
731 })?;
732 let n = f64_to_i32(nu.round())?;
733 match args.first() {
734 Some(RValue::Vector(v)) => {
735 let result: Vec<Option<f64>> = v
736 .to_doubles()
737 .iter()
738 .map(|x| x.map(|x| libm::jn(n, x)))
739 .collect();
740 Ok(RValue::vec(Vector::Double(result.into())))
741 }
742 _ => Err(RError::new(
743 RErrorKind::Argument,
744 "non-numeric argument to mathematical function".to_string(),
745 )),
746 }
747}
748
749#[builtin(name = "besselY", min_args = 2)]
759fn builtin_bessel_y(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
760 let nu = args
761 .get(1)
762 .and_then(|v| v.as_vector())
763 .and_then(|v| v.as_double_scalar())
764 .ok_or_else(|| {
765 RError::new(
766 RErrorKind::Argument,
767 "besselY requires a numeric 'nu' (order) argument".to_string(),
768 )
769 })?;
770 let n = f64_to_i32(nu.round())?;
771 match args.first() {
772 Some(RValue::Vector(v)) => {
773 let result: Vec<Option<f64>> = v
774 .to_doubles()
775 .iter()
776 .map(|x| x.map(|x| libm::yn(n, x)))
777 .collect();
778 Ok(RValue::vec(Vector::Double(result.into())))
779 }
780 _ => Err(RError::new(
781 RErrorKind::Argument,
782 "non-numeric argument to mathematical function".to_string(),
783 )),
784 }
785}
786
787#[builtin(min_args = 1)]
799fn builtin_cbrt(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
800 math_unary(args, libm::cbrt)
801}
802
803#[builtin(min_args = 2)]
812fn builtin_hypot(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
813 math_binary(args, libm::hypot)
814}
815
816fn math_binary(args: &[RValue], f: fn(f64, f64) -> f64) -> Result<RValue, RError> {
823 let (a_vec, b_vec) = match (args.first(), args.get(1)) {
824 (Some(RValue::Vector(a)), Some(RValue::Vector(b))) => (a.to_doubles(), b.to_doubles()),
825 _ => {
826 return Err(RError::new(
827 RErrorKind::Argument,
828 "non-numeric argument to mathematical function".to_string(),
829 ))
830 }
831 };
832 let len = a_vec.len().max(b_vec.len());
833 let result: Vec<Option<f64>> = (0..len)
834 .map(|i| {
835 let a = a_vec[i % a_vec.len()];
836 let b = b_vec[i % b_vec.len()];
837 match (a, b) {
838 (Some(x), Some(y)) => Some(f(x, y)),
839 _ => None,
840 }
841 })
842 .collect();
843 Ok(RValue::vec(Vector::Double(result.into())))
844}
845
846#[builtin(min_args = 1)]
856fn builtin_round(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
857 let digits = named
858 .iter()
859 .find(|(n, _)| n == "digits")
860 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
861 .or_else(|| args.get(1)?.as_vector()?.as_integer_scalar())
862 .unwrap_or(0);
863 let factor = 10f64.powi(i32::try_from(digits)?);
864 match args.first() {
865 Some(RValue::Vector(v)) => {
866 let result: Vec<Option<f64>> = v
867 .to_doubles()
868 .iter()
869 .map(|x| x.map(|f| round_half_to_even(f * factor) / factor))
870 .collect();
871 Ok(RValue::vec(Vector::Double(result.into())))
872 }
873 _ => Err(RError::new(
874 RErrorKind::Argument,
875 "non-numeric argument".to_string(),
876 )),
877 }
878}
879
880fn round_half_to_even(x: f64) -> f64 {
883 let rounded = x.round();
884 let diff = x - x.floor();
886 if (diff - 0.5).abs() < f64::EPSILON {
887 let floor = x.floor();
889 let ceil = x.ceil();
890 if floor % 2.0 == 0.0 {
891 floor
892 } else {
893 ceil
894 }
895 } else {
896 rounded
897 }
898}
899
900#[builtin(min_args = 1)]
906fn builtin_signif(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
907 let digits = named
908 .iter()
909 .find(|(n, _)| n == "digits")
910 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
911 .or_else(|| args.get(1)?.as_vector()?.as_integer_scalar())
912 .unwrap_or(6);
913 let digits = i32::try_from(digits)?;
914 match args.first() {
915 Some(RValue::Vector(v)) => {
916 let result: Vec<Option<f64>> = v
917 .to_doubles()
918 .iter()
919 .map(|x| {
920 x.map(|f| {
921 if f == 0.0 || !f.is_finite() {
922 return f;
923 }
924 let d = f64_to_i32(f.abs().log10().ceil()).unwrap_or(0);
925 let factor = 10f64.powi(digits - d);
926 (f * factor).round() / factor
927 })
928 })
929 .collect();
930 Ok(RValue::vec(Vector::Double(result.into())))
931 }
932 _ => Err(RError::new(
933 RErrorKind::Argument,
934 "non-numeric argument to signif".to_string(),
935 )),
936 }
937}
938
939#[builtin(min_args = 1)]
947fn builtin_pmin(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
948 let na_rm = named.iter().any(|(n, v)| {
949 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
950 });
951 parallel_minmax(args, na_rm, false)
952}
953
954#[builtin(min_args = 1)]
960fn builtin_pmax(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
961 let na_rm = named.iter().any(|(n, v)| {
962 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
963 });
964 parallel_minmax(args, na_rm, true)
965}
966
967fn parallel_minmax(args: &[RValue], na_rm: bool, is_max: bool) -> Result<RValue, RError> {
968 let vecs: Vec<Vec<Option<f64>>> = args
970 .iter()
971 .filter_map(|a| {
972 if let RValue::Vector(v) = a {
973 Some(v.to_doubles())
974 } else {
975 None
976 }
977 })
978 .collect();
979 if vecs.is_empty() {
980 return Err(RError::new(
981 RErrorKind::Argument,
982 "no arguments to pmin/pmax".to_string(),
983 ));
984 }
985 let max_len = vecs.iter().map(|v| v.len()).max().unwrap_or(0);
987 let mut result = Vec::with_capacity(max_len);
988 for i in 0..max_len {
989 let mut current: Option<f64> = None;
990 let mut has_na = false;
991 for vec in &vecs {
992 let val = vec[i % vec.len()];
993 match val {
994 Some(f) => {
995 current = Some(match current {
996 Some(c) => {
997 if is_max {
998 c.max(f)
999 } else {
1000 c.min(f)
1001 }
1002 }
1003 None => f,
1004 });
1005 }
1006 None => {
1007 has_na = true;
1008 }
1009 }
1010 }
1011 if has_na && !na_rm {
1012 result.push(None);
1013 } else {
1014 result.push(current);
1015 }
1016 }
1017 Ok(RValue::vec(Vector::Double(result.into())))
1018}
1019
1020#[builtin(min_args = 1)]
1027fn builtin_cumall(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1028 match args.first() {
1029 Some(RValue::Vector(v)) => {
1030 let logicals = v.to_logicals();
1031 let mut acc = true;
1032 let result: Vec<Option<bool>> = logicals
1033 .iter()
1034 .map(|x| match x {
1035 Some(b) => {
1036 acc = acc && *b;
1037 Some(acc)
1038 }
1039 None => None,
1040 })
1041 .collect();
1042 Ok(RValue::vec(Vector::Logical(result.into())))
1043 }
1044 _ => Err(RError::new(
1045 RErrorKind::Argument,
1046 "invalid argument to cumall".to_string(),
1047 )),
1048 }
1049}
1050
1051#[builtin(min_args = 1)]
1056fn builtin_cumany(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1057 match args.first() {
1058 Some(RValue::Vector(v)) => {
1059 let logicals = v.to_logicals();
1060 let mut acc = false;
1061 let result: Vec<Option<bool>> = logicals
1062 .iter()
1063 .map(|x| match x {
1064 Some(b) => {
1065 acc = acc || *b;
1066 Some(acc)
1067 }
1068 None => None,
1069 })
1070 .collect();
1071 Ok(RValue::vec(Vector::Logical(result.into())))
1072 }
1073 _ => Err(RError::new(
1074 RErrorKind::Argument,
1075 "invalid argument to cumany".to_string(),
1076 )),
1077 }
1078}
1079
1080fn bitwise_binary_op_fallible(
1083 args: &[RValue],
1084 op: impl Fn(i64, i64) -> Result<i64, RError>,
1085) -> Result<RValue, RError> {
1086 let a_ints = args
1087 .first()
1088 .and_then(|v| v.as_vector())
1089 .map(|v| v.to_integers())
1090 .ok_or_else(|| {
1091 RError::new(
1092 RErrorKind::Argument,
1093 "non-integer argument to bitwise function".to_string(),
1094 )
1095 })?;
1096 let b_ints = args
1097 .get(1)
1098 .and_then(|v| v.as_vector())
1099 .map(|v| v.to_integers())
1100 .ok_or_else(|| {
1101 RError::new(
1102 RErrorKind::Argument,
1103 "non-integer argument to bitwise function".to_string(),
1104 )
1105 })?;
1106 let max_len = a_ints.len().max(b_ints.len());
1107 let result: Result<Vec<Option<i64>>, RError> = (0..max_len)
1108 .map(|i| {
1109 let a = a_ints[i % a_ints.len()];
1110 let b = b_ints[i % b_ints.len()];
1111 match (a, b) {
1112 (Some(x), Some(y)) => Ok(Some(op(x, y)?)),
1113 _ => Ok(None),
1114 }
1115 })
1116 .collect();
1117 Ok(RValue::vec(Vector::Integer(result?.into())))
1118}
1119
1120fn bitwise_binary_op(args: &[RValue], op: fn(i64, i64) -> i64) -> Result<RValue, RError> {
1121 let a_ints = args
1122 .first()
1123 .and_then(|v| v.as_vector())
1124 .map(|v| v.to_integers())
1125 .ok_or_else(|| {
1126 RError::new(
1127 RErrorKind::Argument,
1128 "non-integer argument to bitwise function".to_string(),
1129 )
1130 })?;
1131 let b_ints = args
1132 .get(1)
1133 .and_then(|v| v.as_vector())
1134 .map(|v| v.to_integers())
1135 .ok_or_else(|| {
1136 RError::new(
1137 RErrorKind::Argument,
1138 "non-integer argument to bitwise function".to_string(),
1139 )
1140 })?;
1141 let max_len = a_ints.len().max(b_ints.len());
1142 let result: Vec<Option<i64>> = (0..max_len)
1143 .map(|i| {
1144 let a = a_ints[i % a_ints.len()];
1145 let b = b_ints[i % b_ints.len()];
1146 match (a, b) {
1147 (Some(x), Some(y)) => Some(op(x, y)),
1148 _ => None,
1149 }
1150 })
1151 .collect();
1152 Ok(RValue::vec(Vector::Integer(result.into())))
1153}
1154
1155#[builtin(name = "bitwAnd", min_args = 2)]
1161fn builtin_bitw_and(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1162 bitwise_binary_op(args, |a, b| a & b)
1163}
1164
1165#[builtin(name = "bitwOr", min_args = 2)]
1171fn builtin_bitw_or(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1172 bitwise_binary_op(args, |a, b| a | b)
1173}
1174
1175#[builtin(name = "bitwXor", min_args = 2)]
1181fn builtin_bitw_xor(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1182 bitwise_binary_op(args, |a, b| a ^ b)
1183}
1184
1185#[builtin(name = "bitwNot", min_args = 1)]
1190fn builtin_bitw_not(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1191 let ints = args
1192 .first()
1193 .and_then(|v| v.as_vector())
1194 .map(|v| v.to_integers())
1195 .ok_or_else(|| {
1196 RError::new(
1197 RErrorKind::Argument,
1198 "non-integer argument to bitwNot".to_string(),
1199 )
1200 })?;
1201 let result: Vec<Option<i64>> = ints.iter().map(|x| x.map(|i| !i)).collect();
1202 Ok(RValue::vec(Vector::Integer(result.into())))
1203}
1204
1205#[builtin(name = "bitwShiftL", min_args = 2)]
1211fn builtin_bitw_shift_l(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1212 bitwise_binary_op_fallible(args, |a, n| Ok(a << u32::try_from(n)?))
1213}
1214
1215#[builtin(name = "bitwShiftR", min_args = 2)]
1221fn builtin_bitw_shift_r(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1222 bitwise_binary_op_fallible(args, |a, n| Ok(a >> u32::try_from(n)?))
1223}
1224
1225#[builtin(name = "lower.tri", min_args = 1)]
1233fn builtin_lower_tri(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1234 let diag_incl = named
1235 .iter()
1236 .find(|(n, _)| n == "diag")
1237 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
1238 .or_else(|| args.get(1)?.as_vector()?.as_logical_scalar())
1239 .unwrap_or(false);
1240 tri_matrix(args, diag_incl, true)
1241}
1242
1243#[builtin(name = "upper.tri", min_args = 1)]
1249fn builtin_upper_tri(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1250 let diag_incl = named
1251 .iter()
1252 .find(|(n, _)| n == "diag")
1253 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
1254 .or_else(|| args.get(1)?.as_vector()?.as_logical_scalar())
1255 .unwrap_or(false);
1256 tri_matrix(args, diag_incl, false)
1257}
1258
1259fn tri_matrix(args: &[RValue], diag_incl: bool, lower: bool) -> Result<RValue, RError> {
1260 let (nrow, ncol) = match args.first() {
1261 Some(RValue::Vector(rv)) => match rv.get_attr("dim") {
1262 Some(RValue::Vector(dim_rv)) => match &dim_rv.inner {
1263 Vector::Integer(d) if d.len() >= 2 => (
1264 usize::try_from(d.get_opt(0).unwrap_or(0)).unwrap_or(0),
1265 usize::try_from(d.get_opt(1).unwrap_or(0)).unwrap_or(0),
1266 ),
1267 _ => {
1268 return Err(RError::new(
1269 RErrorKind::Argument,
1270 "argument is not a matrix".to_string(),
1271 ))
1272 }
1273 },
1274 _ => {
1275 return Err(RError::new(
1276 RErrorKind::Argument,
1277 "argument is not a matrix".to_string(),
1278 ))
1279 }
1280 },
1281 _ => {
1282 return Err(RError::new(
1283 RErrorKind::Argument,
1284 "argument is not a matrix".to_string(),
1285 ))
1286 }
1287 };
1288
1289 let mut result = Vec::with_capacity(nrow * ncol);
1291 for j in 0..ncol {
1292 for i in 0..nrow {
1293 let val = if lower {
1294 if diag_incl {
1295 i >= j
1296 } else {
1297 i > j
1298 }
1299 } else if diag_incl {
1300 i <= j
1301 } else {
1302 i < j
1303 };
1304 result.push(Some(val));
1305 }
1306 }
1307 let mut rv = RVector::from(Vector::Logical(result.into()));
1308 set_matrix_attrs(&mut rv, nrow, ncol, None, None)?;
1309 Ok(RValue::Vector(rv))
1310}
1311
1312#[builtin(min_args = 1)]
1323fn builtin_diag(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1324 match args.first() {
1325 Some(RValue::Vector(rv)) => {
1326 if let Some(RValue::Vector(dim_rv)) = rv.get_attr("dim") {
1328 if let Vector::Integer(d) = &dim_rv.inner {
1329 if d.len() >= 2 {
1330 let nrow = usize::try_from(d.get_opt(0).unwrap_or(0)).unwrap_or(0);
1331 let ncol = usize::try_from(d.get_opt(1).unwrap_or(0)).unwrap_or(0);
1332 let data = rv.to_doubles();
1333 let n = nrow.min(ncol);
1334 let result: Vec<Option<f64>> = (0..n)
1335 .map(|i| {
1336 let idx = i * nrow + i; if idx < data.len() {
1338 data[idx]
1339 } else {
1340 None
1341 }
1342 })
1343 .collect();
1344 return Ok(RValue::vec(Vector::Double(result.into())));
1345 }
1346 }
1347 }
1348
1349 let len = rv.len();
1350 if len == 1 {
1351 let n = usize::try_from(rv.as_integer_scalar().unwrap_or(1)).unwrap_or(0);
1353 let mut result = vec![Some(0.0); n * n];
1354 for i in 0..n {
1355 result[i * n + i] = Some(1.0);
1356 }
1357 let mut out = RVector::from(Vector::Double(result.into()));
1358 set_matrix_attrs(&mut out, n, n, None, None)?;
1359 Ok(RValue::Vector(out))
1360 } else {
1361 let vals = rv.to_doubles();
1363 let n = vals.len();
1364 let mut result = vec![Some(0.0); n * n];
1365 for (i, v) in vals.iter().enumerate() {
1366 result[i * n + i] = *v;
1367 }
1368 let mut out = RVector::from(Vector::Double(result.into()));
1369 set_matrix_attrs(&mut out, n, n, None, None)?;
1370 Ok(RValue::Vector(out))
1371 }
1372 }
1373 _ => Err(RError::new(
1374 RErrorKind::Argument,
1375 "'x' must be numeric".to_string(),
1376 )),
1377 }
1378}
1379
1380#[builtin(min_args = 0)]
1386fn builtin_sum(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1387 let na_rm = named.iter().any(|(n, v)| {
1388 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1389 });
1390 let mut total = 0.0;
1391 for arg in args {
1392 if let RValue::Vector(v) = arg {
1393 for x in v.to_doubles() {
1394 match x {
1395 Some(f) => total += f,
1396 None if !na_rm => return Ok(RValue::vec(Vector::Double(vec![None].into()))),
1397 None => {}
1398 }
1399 }
1400 }
1401 }
1402 Ok(RValue::vec(Vector::Double(vec![Some(total)].into())))
1403}
1404
1405#[builtin(min_args = 0)]
1411fn builtin_prod(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1412 let na_rm = named.iter().any(|(n, v)| {
1413 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1414 });
1415 let mut total = 1.0;
1416 for arg in args {
1417 if let RValue::Vector(v) = arg {
1418 for x in v.to_doubles() {
1419 match x {
1420 Some(f) => total *= f,
1421 None if !na_rm => return Ok(RValue::vec(Vector::Double(vec![None].into()))),
1422 None => {}
1423 }
1424 }
1425 }
1426 }
1427 Ok(RValue::vec(Vector::Double(vec![Some(total)].into())))
1428}
1429
1430#[builtin(min_args = 0)]
1436fn builtin_max(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1437 let na_rm = named.iter().any(|(n, v)| {
1438 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1439 });
1440 let mut result: Option<f64> = None;
1441 for arg in args {
1442 if let RValue::Vector(v) = arg {
1443 for x in v.to_doubles() {
1444 match x {
1445 Some(f) => {
1446 result = Some(match result {
1447 Some(r) => r.max(f),
1448 None => f,
1449 })
1450 }
1451 None if !na_rm => return Ok(RValue::vec(Vector::Double(vec![None].into()))),
1452 None => {}
1453 }
1454 }
1455 }
1456 }
1457 Ok(RValue::vec(Vector::Double(
1458 vec![result.or(Some(f64::NEG_INFINITY))].into(),
1459 )))
1460}
1461
1462#[builtin(min_args = 0)]
1468fn builtin_min(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1469 let na_rm = named.iter().any(|(n, v)| {
1470 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1471 });
1472 let mut result: Option<f64> = None;
1473 for arg in args {
1474 if let RValue::Vector(v) = arg {
1475 for x in v.to_doubles() {
1476 match x {
1477 Some(f) => {
1478 result = Some(match result {
1479 Some(r) => r.min(f),
1480 None => f,
1481 })
1482 }
1483 None if !na_rm => return Ok(RValue::vec(Vector::Double(vec![None].into()))),
1484 None => {}
1485 }
1486 }
1487 }
1488 }
1489 Ok(RValue::vec(Vector::Double(
1490 vec![result.or(Some(f64::INFINITY))].into(),
1491 )))
1492}
1493
1494#[builtin(min_args = 1)]
1500fn builtin_mean(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1501 let na_rm = named.iter().any(|(n, v)| {
1502 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1503 });
1504 match args.first() {
1505 Some(RValue::Vector(v)) => {
1506 let vals = v.to_doubles();
1507 let mut sum = 0.0;
1508 let mut count = 0;
1509 for x in &vals {
1510 match x {
1511 Some(f) => {
1512 sum += f;
1513 count += 1;
1514 }
1515 None if !na_rm => return Ok(RValue::vec(Vector::Double(vec![None].into()))),
1516 None => {}
1517 }
1518 }
1519 if count == 0 {
1520 return Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into())));
1521 }
1522 Ok(RValue::vec(Vector::Double(
1523 vec![Some(sum / usize_to_f64(count))].into(),
1524 )))
1525 }
1526 _ => Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into()))),
1527 }
1528}
1529
1530#[builtin(min_args = 1, namespace = "stats")]
1536fn builtin_median(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1537 let na_rm = named.iter().any(|(n, v)| {
1538 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1539 });
1540 match args.first() {
1541 Some(RValue::Vector(v)) => {
1542 let doubles = v.to_doubles();
1543 if !na_rm && doubles.iter().any(|x| x.is_none()) {
1545 return Ok(RValue::vec(Vector::Double(vec![None].into())));
1546 }
1547 let mut vals: Vec<f64> = doubles.into_iter().flatten().collect();
1548 if vals.is_empty() {
1549 return Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into())));
1550 }
1551 vals.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1552 let mid = vals.len() / 2;
1553 let median = if vals.len().is_multiple_of(2) {
1554 (vals[mid - 1] + vals[mid]) / 2.0
1555 } else {
1556 vals[mid]
1557 };
1558 Ok(RValue::vec(Vector::Double(vec![Some(median)].into())))
1559 }
1560 _ => Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into()))),
1561 }
1562}
1563
1564#[builtin(min_args = 1, namespace = "stats")]
1570fn builtin_var(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1571 let na_rm = named.iter().any(|(n, v)| {
1572 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
1573 });
1574 match args.first() {
1575 Some(RValue::Vector(v)) => {
1576 let doubles = v.to_doubles();
1577 if !na_rm && doubles.iter().any(|x| x.is_none()) {
1579 return Ok(RValue::vec(Vector::Double(vec![None].into())));
1580 }
1581 let vals: Vec<f64> = doubles.into_iter().flatten().collect();
1582 let n = usize_to_f64(vals.len());
1583 if n < 2.0 {
1584 return Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into())));
1585 }
1586 let mean = vals.iter().sum::<f64>() / n;
1587 let var = vals.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (n - 1.0);
1588 Ok(RValue::vec(Vector::Double(vec![Some(var)].into())))
1589 }
1590 _ => Ok(RValue::vec(Vector::Double(vec![Some(f64::NAN)].into()))),
1591 }
1592}
1593
1594#[builtin(min_args = 1, namespace = "stats")]
1599fn builtin_sd(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1600 match builtin_var(args, named)? {
1601 RValue::Vector(rv) => match rv.inner {
1602 Vector::Double(v) => Ok(RValue::vec(Vector::Double(
1603 v.iter_opt()
1604 .map(|x| x.map(f64::sqrt))
1605 .collect::<Vec<_>>()
1606 .into(),
1607 ))),
1608 other => Ok(RValue::vec(other)),
1609 },
1610 other => Ok(other),
1611 }
1612}
1613
1614#[builtin(min_args = 1)]
1621fn builtin_cumsum(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1622 match args.first() {
1623 Some(RValue::Vector(v)) => {
1624 let mut acc = 0.0;
1625 let mut seen_na = false;
1626 let result: Vec<Option<f64>> = v
1627 .to_doubles()
1628 .iter()
1629 .map(|x| {
1630 if seen_na {
1631 return None;
1632 }
1633 match *x {
1634 Some(f) => {
1635 acc += f;
1636 Some(acc)
1637 }
1638 None => {
1639 seen_na = true;
1640 None
1641 }
1642 }
1643 })
1644 .collect();
1645 Ok(RValue::vec(Vector::Double(result.into())))
1646 }
1647 _ => Err(RError::new(
1648 RErrorKind::Argument,
1649 "invalid argument".to_string(),
1650 )),
1651 }
1652}
1653
1654#[builtin(min_args = 1)]
1661fn builtin_cumprod(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1662 match args.first() {
1663 Some(RValue::Vector(v)) => {
1664 let mut acc = 1.0;
1665 let mut seen_na = false;
1666 let result: Vec<Option<f64>> = v
1667 .to_doubles()
1668 .iter()
1669 .map(|x| {
1670 if seen_na {
1671 return None;
1672 }
1673 match *x {
1674 Some(f) => {
1675 acc *= f;
1676 Some(acc)
1677 }
1678 None => {
1679 seen_na = true;
1680 None
1681 }
1682 }
1683 })
1684 .collect();
1685 Ok(RValue::vec(Vector::Double(result.into())))
1686 }
1687 _ => Err(RError::new(
1688 RErrorKind::Argument,
1689 "invalid argument".to_string(),
1690 )),
1691 }
1692}
1693
1694#[builtin(min_args = 1)]
1701fn builtin_cummax(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1702 match args.first() {
1703 Some(RValue::Vector(v)) => {
1704 let mut acc = f64::NEG_INFINITY;
1705 let mut seen_na = false;
1706 let result: Vec<Option<f64>> = v
1707 .to_doubles()
1708 .iter()
1709 .map(|x| {
1710 if seen_na {
1711 return None;
1712 }
1713 match *x {
1714 Some(f) => {
1715 acc = acc.max(f);
1716 Some(acc)
1717 }
1718 None => {
1719 seen_na = true;
1720 None
1721 }
1722 }
1723 })
1724 .collect();
1725 Ok(RValue::vec(Vector::Double(result.into())))
1726 }
1727 _ => Err(RError::new(
1728 RErrorKind::Argument,
1729 "invalid argument".to_string(),
1730 )),
1731 }
1732}
1733
1734#[builtin(min_args = 1)]
1741fn builtin_cummin(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1742 match args.first() {
1743 Some(RValue::Vector(v)) => {
1744 let mut acc = f64::INFINITY;
1745 let mut seen_na = false;
1746 let result: Vec<Option<f64>> = v
1747 .to_doubles()
1748 .iter()
1749 .map(|x| {
1750 if seen_na {
1751 return None;
1752 }
1753 match *x {
1754 Some(f) => {
1755 acc = acc.min(f);
1756 Some(acc)
1757 }
1758 None => {
1759 seen_na = true;
1760 None
1761 }
1762 }
1763 })
1764 .collect();
1765 Ok(RValue::vec(Vector::Double(result.into())))
1766 }
1767 _ => Err(RError::new(
1768 RErrorKind::Argument,
1769 "invalid argument".to_string(),
1770 )),
1771 }
1772}
1773
1774#[builtin(min_args = 0, names = ["seq.int"])]
1782fn builtin_seq(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1783 let from = named
1784 .iter()
1785 .find(|(n, _)| n == "from")
1786 .map(|(_, v)| v)
1787 .or(args.first())
1788 .and_then(|v| v.as_vector()?.as_double_scalar())
1789 .unwrap_or(1.0);
1790 let to = named
1791 .iter()
1792 .find(|(n, _)| n == "to")
1793 .map(|(_, v)| v)
1794 .or(args.get(1))
1795 .and_then(|v| v.as_vector()?.as_double_scalar())
1796 .unwrap_or(1.0);
1797 let by = named
1798 .iter()
1799 .find(|(n, _)| n == "by")
1800 .map(|(_, v)| v)
1801 .or(args.get(2))
1802 .and_then(|v| v.as_vector()?.as_double_scalar());
1803 let length_out = named
1804 .iter()
1805 .find(|(n, _)| n == "length.out")
1806 .map(|(_, v)| v)
1807 .and_then(|v| v.as_vector()?.as_integer_scalar());
1808
1809 if let Some(len) = length_out {
1810 let len = usize::try_from(len)?;
1811 if len == 0 {
1812 return Ok(RValue::vec(Vector::Double(vec![].into())));
1813 }
1814 if len == 1 {
1815 return Ok(RValue::vec(Vector::Double(vec![Some(from)].into())));
1816 }
1817 let step = (to - from) / usize_to_f64(len - 1);
1818 let result: Vec<Option<f64>> = (0..len)
1819 .map(|i| Some(from + step * usize_to_f64(i)))
1820 .collect();
1821 return Ok(RValue::vec(Vector::Double(result.into())));
1822 }
1823
1824 let by = by.unwrap_or(if to >= from { 1.0 } else { -1.0 });
1825 if by == 0.0 {
1826 return Err(RError::new(
1827 RErrorKind::Argument,
1828 "'by' argument must not be zero".to_string(),
1829 ));
1830 }
1831
1832 let mut result = Vec::new();
1833 let mut val = from;
1834 if by > 0.0 {
1835 while val <= to + f64::EPSILON * 100.0 {
1836 result.push(Some(val));
1837 val += by;
1838 }
1839 } else {
1840 while val >= to - f64::EPSILON * 100.0 {
1841 result.push(Some(val));
1842 val += by;
1843 }
1844 }
1845 Ok(RValue::vec(Vector::Double(result.into())))
1846}
1847
1848#[builtin(name = "seq_len", min_args = 1)]
1853fn builtin_seq_len(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1854 let n = args
1855 .first()
1856 .and_then(|v| v.as_vector()?.as_integer_scalar())
1857 .unwrap_or(0);
1858 let result: Vec<Option<i64>> = (1..=n).map(Some).collect();
1859 Ok(RValue::vec(Vector::Integer(result.into())))
1860}
1861
1862#[builtin(name = "seq_along", min_args = 1)]
1867fn builtin_seq_along(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
1868 let n = args.first().map(|v| v.length()).unwrap_or(0);
1869 let n_i64 = i64::try_from(n)?;
1870 let result: Vec<Option<i64>> = (1..=n_i64).map(Some).collect();
1871 Ok(RValue::vec(Vector::Integer(result.into())))
1872}
1873
1874#[builtin(min_args = 1)]
1882fn builtin_rep(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
1883 let each = named
1884 .iter()
1885 .find(|(n, _)| n == "each")
1886 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar());
1887
1888 let length_out = named
1889 .iter()
1890 .find(|(n, _)| n == "length.out")
1891 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar());
1892
1893 let times = named
1894 .iter()
1895 .find(|(n, _)| n == "times")
1896 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
1897 .or_else(|| {
1898 if each.is_none() {
1900 args.get(1)?.as_vector()?.as_integer_scalar()
1901 } else {
1902 None
1903 }
1904 })
1905 .unwrap_or(1);
1906 let times = usize::try_from(times)?;
1907
1908 let each_n = each.map(usize::try_from).transpose()?.unwrap_or(1);
1909
1910 match args.first() {
1911 Some(RValue::Vector(v)) => {
1912 let result = rep_vector(&v.inner, each_n, times, length_out)?;
1913 Ok(RValue::vec(result))
1914 }
1915 _ => Err(RError::new(
1916 RErrorKind::Argument,
1917 "invalid argument".to_string(),
1918 )),
1919 }
1920}
1921
1922fn rep_vector(
1927 v: &Vector,
1928 each: usize,
1929 times: usize,
1930 length_out: Option<i64>,
1931) -> Result<Vector, RError> {
1932 let result = match v {
1937 Vector::Double(vals) => {
1938 let opt_vec = vals.to_option_vec();
1939 let expanded = rep_each_then_times(opt_vec.as_slice(), each, times);
1940 Vector::Double(apply_length_out_cloneable(expanded, length_out)?.into())
1941 }
1942 Vector::Integer(vals) => {
1943 let opt_vec = vals.to_option_vec();
1944 let expanded = rep_each_then_times(opt_vec.as_slice(), each, times);
1945 Vector::Integer(apply_length_out_cloneable(expanded, length_out)?.into())
1946 }
1947 Vector::Logical(vals) => {
1948 let expanded = rep_each_then_times(vals.as_slice(), each, times);
1949 Vector::Logical(apply_length_out_cloneable(expanded, length_out)?.into())
1950 }
1951 Vector::Character(vals) => {
1952 let expanded = rep_each_then_times(vals.as_slice(), each, times);
1953 Vector::Character(apply_length_out_cloneable(expanded, length_out)?.into())
1954 }
1955 Vector::Complex(vals) => {
1956 let expanded = rep_each_then_times(vals.as_slice(), each, times);
1957 Vector::Complex(apply_length_out_cloneable(expanded, length_out)?.into())
1958 }
1959 Vector::Raw(vals) => {
1960 let expanded = rep_each_then_times_copy(vals, each, times);
1961 let final_vec = if let Some(lo) = length_out {
1962 let lo = usize::try_from(lo)?;
1963 if expanded.is_empty() {
1964 vec![]
1965 } else {
1966 expanded.iter().cycle().take(lo).copied().collect()
1967 }
1968 } else {
1969 expanded
1970 };
1971 Vector::Raw(final_vec)
1972 }
1973 };
1974 Ok(result)
1975}
1976
1977fn rep_each_then_times<T: Clone>(vals: &[T], each: usize, times: usize) -> Vec<T> {
1979 let with_each: Vec<T> = if each <= 1 {
1980 vals.to_vec()
1981 } else {
1982 vals.iter()
1983 .flat_map(|v| std::iter::repeat_n(v.clone(), each))
1984 .collect()
1985 };
1986 if times <= 1 {
1987 with_each
1988 } else {
1989 let len = with_each.len();
1990 with_each.into_iter().cycle().take(len * times).collect()
1991 }
1992}
1993
1994fn rep_each_then_times_copy<T: Copy>(vals: &[T], each: usize, times: usize) -> Vec<T> {
1996 let with_each: Vec<T> = if each <= 1 {
1997 vals.to_vec()
1998 } else {
1999 vals.iter()
2000 .flat_map(|&v| std::iter::repeat_n(v, each))
2001 .collect()
2002 };
2003 if times <= 1 {
2004 with_each
2005 } else {
2006 let len = with_each.len();
2007 with_each.into_iter().cycle().take(len * times).collect()
2008 }
2009}
2010
2011fn apply_length_out_cloneable<T: Clone>(
2013 v: Vec<T>,
2014 length_out: Option<i64>,
2015) -> Result<Vec<T>, RError> {
2016 match length_out {
2017 None => Ok(v),
2018 Some(lo) => {
2019 let lo = usize::try_from(lo)?;
2020 if v.is_empty() {
2021 return Ok(vec![]);
2022 }
2023 Ok(v.iter().cycle().take(lo).cloned().collect())
2024 }
2025 }
2026}
2027
2028#[builtin(min_args = 1)]
2033fn builtin_rev(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
2034 match args.first() {
2035 Some(RValue::Vector(v)) => {
2036 let result = match &v.inner {
2037 Vector::Raw(vals) => {
2038 let mut v = vals.clone();
2039 v.reverse();
2040 Vector::Raw(v)
2041 }
2042 Vector::Double(vals) => {
2043 let mut v = vals.clone();
2044 v.reverse();
2045 Vector::Double(v)
2046 }
2047 Vector::Integer(vals) => {
2048 let mut v = vals.clone();
2049 v.reverse();
2050 Vector::Integer(v)
2051 }
2052 Vector::Logical(vals) => {
2053 let mut v = vals.clone();
2054 v.reverse();
2055 Vector::Logical(v)
2056 }
2057 Vector::Complex(vals) => {
2058 let mut v = vals.clone();
2059 v.reverse();
2060 Vector::Complex(v)
2061 }
2062 Vector::Character(vals) => {
2063 let mut v = vals.clone();
2064 v.reverse();
2065 Vector::Character(v)
2066 }
2067 };
2068 Ok(RValue::vec(result))
2069 }
2070 _ => Ok(RValue::Null),
2071 }
2072}
2073
2074#[builtin(min_args = 1)]
2081fn builtin_sort(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2082 let decreasing = named
2083 .iter()
2084 .find(|(n, _)| n == "decreasing")
2085 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
2086 .unwrap_or(false);
2087
2088 let na_last_val = named.iter().find(|(n, _)| n == "na.last").map(|(_, v)| v);
2091 let na_last: Option<bool> = match na_last_val {
2092 Some(RValue::Vector(rv)) => rv.inner.as_logical_scalar(),
2093 Some(RValue::Null) => None,
2094 None => None, _ => None,
2096 };
2097
2098 match args.first() {
2099 Some(RValue::Vector(v)) => {
2100 let result = match &v.inner {
2101 Vector::Double(vals) => {
2102 let opt_vec = vals.to_option_vec();
2103 let (mut non_na, na_count) = partition_na_doubles(&opt_vec);
2104 non_na.sort_by(|a, b| {
2105 if decreasing {
2106 b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal)
2107 } else {
2108 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
2109 }
2110 });
2111 let result = reassemble_with_na(
2112 non_na.into_iter().map(Some).collect(),
2113 na_count,
2114 na_last,
2115 );
2116 Vector::Double(result.into())
2117 }
2118 Vector::Integer(vals) => {
2119 let opt_vec = vals.to_option_vec();
2120 let (mut non_na, na_count) = partition_na_options(&opt_vec);
2121 non_na.sort_by(|a, b| if decreasing { b.cmp(a) } else { a.cmp(b) });
2122 let result = reassemble_with_na(
2123 non_na.into_iter().map(Some).collect(),
2124 na_count,
2125 na_last,
2126 );
2127 Vector::Integer(result.into())
2128 }
2129 Vector::Character(vals) => {
2130 let (mut non_na, na_count) = partition_na_options(vals.as_slice());
2131 non_na.sort_by(|a, b| if decreasing { b.cmp(a) } else { a.cmp(b) });
2132 let result = reassemble_with_na(
2133 non_na.into_iter().map(Some).collect(),
2134 na_count,
2135 na_last,
2136 );
2137 Vector::Character(result.into())
2138 }
2139 other => other.clone(),
2140 };
2141 Ok(RValue::vec(result))
2142 }
2143 _ => Ok(RValue::Null),
2144 }
2145}
2146
2147fn partition_na_doubles(vals: &[Option<f64>]) -> (Vec<f64>, usize) {
2149 let mut non_na = Vec::with_capacity(vals.len());
2150 let mut na_count = 0;
2151 for v in vals {
2152 match v {
2153 Some(f) if !f.is_nan() => non_na.push(*f),
2154 _ => na_count += 1,
2155 }
2156 }
2157 (non_na, na_count)
2158}
2159
2160fn partition_na_options<T: Clone>(vals: &[Option<T>]) -> (Vec<T>, usize) {
2162 let mut non_na = Vec::with_capacity(vals.len());
2163 let mut na_count = 0;
2164 for v in vals {
2165 match v {
2166 Some(x) => non_na.push(x.clone()),
2167 None => na_count += 1,
2168 }
2169 }
2170 (non_na, na_count)
2171}
2172
2173fn reassemble_with_na<T: Clone>(
2179 sorted: Vec<Option<T>>,
2180 na_count: usize,
2181 na_last: Option<bool>,
2182) -> Vec<Option<T>> {
2183 match na_last {
2184 None => sorted, Some(true) => {
2186 let mut result = sorted;
2187 result.extend(std::iter::repeat_n(None, na_count));
2188 result
2189 }
2190 Some(false) => {
2191 let mut result: Vec<Option<T>> = std::iter::repeat_n(None, na_count).collect();
2192 result.extend(sorted);
2193 result
2194 }
2195 }
2196}
2197
2198#[builtin(min_args = 1)]
2203fn builtin_order(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
2204 match args.first() {
2205 Some(RValue::Vector(v)) => {
2206 let doubles = v.to_doubles();
2207 let mut indices: Vec<usize> = (0..doubles.len()).collect();
2208 indices.sort_by(|&a, &b| {
2209 let va = doubles[a].unwrap_or(f64::NAN);
2210 let vb = doubles[b].unwrap_or(f64::NAN);
2211 va.partial_cmp(&vb).unwrap_or(std::cmp::Ordering::Equal)
2212 });
2213 let result: Vec<Option<i64>> = indices
2214 .iter()
2215 .map(|&i| Some(i64::try_from(i).unwrap_or(0) + 1))
2216 .collect();
2217 Ok(RValue::vec(Vector::Integer(result.into())))
2218 }
2219 _ => Ok(RValue::Null),
2220 }
2221}
2222
2223#[builtin(min_args = 1)]
2234fn builtin_rank(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2235 let ties_method = named
2236 .iter()
2237 .find(|(n, _)| n == "ties.method")
2238 .and_then(|(_, v)| v.as_vector()?.as_character_scalar())
2239 .unwrap_or_else(|| "average".to_string());
2240
2241 let vals = match args.first() {
2242 Some(RValue::Vector(v)) => v.to_doubles(),
2243 _ => return Ok(RValue::Null),
2244 };
2245
2246 let n = vals.len();
2247 if n == 0 {
2248 return Ok(RValue::vec(Vector::Double(Vec::new().into())));
2249 }
2250
2251 let mut indexed: Vec<(Option<f64>, usize)> = vals.iter().copied().zip(0..n).collect();
2254 indexed.sort_by(|a, b| match (a.0, b.0) {
2255 (None, None) => std::cmp::Ordering::Equal,
2256 (None, _) => std::cmp::Ordering::Greater,
2257 (_, None) => std::cmp::Ordering::Less,
2258 (Some(va), Some(vb)) => va.partial_cmp(&vb).unwrap_or(std::cmp::Ordering::Equal),
2259 });
2260
2261 let mut ranks = vec![0.0f64; n];
2263 match ties_method.as_str() {
2264 "first" => {
2265 for (rank_minus_1, &(_, orig_idx)) in indexed.iter().enumerate() {
2266 ranks[orig_idx] = (rank_minus_1 + 1) as f64;
2267 }
2268 }
2269 "min" | "max" | "average" => {
2270 let mut i = 0;
2271 while i < n {
2272 let mut j = i + 1;
2274 while j < n {
2275 let same = match (indexed[i].0, indexed[j].0) {
2276 (None, None) => true,
2277 (Some(a), Some(b)) => a == b,
2278 _ => false,
2279 };
2280 if !same {
2281 break;
2282 }
2283 j += 1;
2284 }
2285 let rank = match ties_method.as_str() {
2287 "min" => (i + 1) as f64,
2288 "max" => j as f64,
2289 _ => {
2290 let sum: f64 = ((i + 1)..=j).map(|r| r as f64).sum();
2292 sum / (j - i) as f64
2293 }
2294 };
2295 for item in indexed.iter().take(j).skip(i) {
2296 ranks[item.1] = rank;
2297 }
2298 i = j;
2299 }
2300 }
2301 other => {
2302 return Err(RError::new(
2303 RErrorKind::Argument,
2304 format!(
2305 "ties.method must be one of \"average\", \"first\", \"min\", or \"max\", not {:?}",
2306 other
2307 ),
2308 ))
2309 }
2310 }
2311
2312 let result: Vec<Option<f64>> = ranks.into_iter().map(Some).collect();
2313 Ok(RValue::vec(Vector::Double(result.into())))
2314}
2315
2316#[builtin(min_args = 1)]
2321fn builtin_unique(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
2322 match args.first() {
2323 Some(RValue::Vector(v)) => {
2324 let result = match &v.inner {
2325 Vector::Raw(vals) => {
2326 let mut seen = Vec::new();
2327 let mut result = Vec::new();
2328 for &x in vals.iter() {
2329 if !seen.contains(&x) {
2330 seen.push(x);
2331 result.push(x);
2332 }
2333 }
2334 Vector::Raw(result)
2335 }
2336 Vector::Double(vals) => {
2337 let mut seen = Vec::new();
2338 let mut result = Vec::new();
2339 for x in vals.iter_opt() {
2340 let key = format!("{:?}", x);
2341 if !seen.contains(&key) {
2342 seen.push(key);
2343 result.push(x);
2344 }
2345 }
2346 Vector::Double(result.into())
2347 }
2348 Vector::Integer(vals) => {
2349 let mut seen: Vec<Option<i64>> = Vec::new();
2350 let mut result = Vec::new();
2351 for x in vals.iter_opt() {
2352 if !seen.contains(&x) {
2353 seen.push(x);
2354 result.push(x);
2355 }
2356 }
2357 Vector::Integer(result.into())
2358 }
2359 Vector::Character(vals) => {
2360 let mut seen: Vec<Option<String>> = Vec::new();
2361 let mut result = Vec::new();
2362 for x in vals.iter() {
2363 if !seen.contains(x) {
2364 seen.push(x.clone());
2365 result.push(x.clone());
2366 }
2367 }
2368 Vector::Character(result.into())
2369 }
2370 Vector::Complex(vals) => {
2371 let mut seen = Vec::new();
2372 let mut result = Vec::new();
2373 for x in vals.iter() {
2374 let key = format!("{:?}", x);
2375 if !seen.contains(&key) {
2376 seen.push(key);
2377 result.push(*x);
2378 }
2379 }
2380 Vector::Complex(result.into())
2381 }
2382 Vector::Logical(vals) => {
2383 let mut seen = Vec::new();
2384 let mut result = Vec::new();
2385 for x in vals.iter() {
2386 if !seen.contains(x) {
2387 seen.push(*x);
2388 result.push(*x);
2389 }
2390 }
2391 Vector::Logical(result.into())
2392 }
2393 };
2394 Ok(RValue::vec(result))
2395 }
2396 _ => Ok(RValue::Null),
2397 }
2398}
2399
2400#[builtin(name = "sort_unique", min_args = 1)]
2403fn builtin_sort_unique(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2404 let decreasing = named
2405 .iter()
2406 .find(|(n, _)| n == "decreasing")
2407 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
2408 .unwrap_or(false);
2409
2410 match args.first() {
2411 Some(RValue::Vector(v)) => {
2412 let result = match &v.inner {
2413 Vector::Double(vals) => {
2414 fn sort_key(v: Option<f64>) -> (bool, u64) {
2418 match v {
2419 None => (true, 0), Some(f) => {
2421 let bits = f.to_bits();
2422 let key = if bits >> 63 == 1 {
2423 !bits
2424 } else {
2425 bits ^ (1 << 63)
2426 };
2427 (false, key)
2428 }
2429 }
2430 }
2431 let set: BTreeSet<(bool, u64)> = vals.iter_opt().map(sort_key).collect();
2432 let mut result: Vec<Option<f64>> = set
2434 .into_iter()
2435 .map(|(is_na, key)| {
2436 if is_na {
2437 None
2438 } else {
2439 let bits = if key >> 63 == 0 {
2440 !key
2441 } else {
2442 key ^ (1 << 63)
2443 };
2444 Some(f64::from_bits(bits))
2445 }
2446 })
2447 .collect();
2448 if decreasing {
2449 result.reverse();
2450 }
2451 Vector::Double(result.into())
2452 }
2453 Vector::Integer(vals) => {
2454 let has_na = vals.iter_opt().any(|x| x.is_none());
2455 let set: BTreeSet<i64> = vals.iter_opt().flatten().collect();
2456 let mut result: Vec<Option<i64>> = set.into_iter().map(Some).collect();
2457 if decreasing {
2458 result.reverse();
2459 }
2460 if has_na {
2461 result.push(None); }
2463 Vector::Integer(result.into())
2464 }
2465 Vector::Character(vals) => {
2466 let has_na = vals.iter().any(|x| x.is_none());
2467 let set: BTreeSet<&str> = vals.iter().filter_map(|x| x.as_deref()).collect();
2468 let mut result: Vec<Option<String>> =
2469 set.into_iter().map(|s| Some(s.to_string())).collect();
2470 if decreasing {
2471 result.reverse();
2472 }
2473 if has_na {
2474 result.push(None); }
2476 Vector::Character(result.into())
2477 }
2478 other => other.clone(),
2479 };
2480 Ok(RValue::vec(result))
2481 }
2482 _ => Ok(RValue::Null),
2483 }
2484}
2485
2486#[builtin(min_args = 1)]
2495fn builtin_which(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2496 let arr_ind = named
2497 .iter()
2498 .find(|(n, _)| n == "arr.ind")
2499 .and_then(|(_, v)| v.as_vector().and_then(|v| v.as_logical_scalar()))
2500 .unwrap_or(false);
2501
2502 match args.first() {
2503 Some(RValue::Vector(rv)) => {
2504 let logicals = rv.to_logicals();
2505 let true_indices: Vec<i64> = logicals
2506 .iter()
2507 .enumerate()
2508 .filter_map(|(i, v)| {
2509 if *v == Some(true) {
2510 Some(i64::try_from(i).unwrap_or(0) + 1)
2511 } else {
2512 None
2513 }
2514 })
2515 .collect();
2516
2517 if arr_ind {
2519 if let Some(RValue::Vector(dim_rv)) = rv.get_attr("dim") {
2520 let dims = dim_rv.to_integers();
2521 if dims.len() >= 2 {
2522 let n = true_indices.len();
2523 let ndim = dims.len();
2524 let dim_sizes: Vec<i64> = dims.iter().map(|d| d.unwrap_or(0)).collect();
2525 let mut result: Vec<Option<i64>> = vec![None; n * ndim];
2526
2527 for (idx_pos, &linear_1based) in true_indices.iter().enumerate() {
2528 let mut linear = linear_1based - 1;
2529 for d in 0..ndim {
2530 let subscript = linear % dim_sizes[d];
2531 result[d * n + idx_pos] = Some(subscript + 1);
2532 linear /= dim_sizes[d];
2533 }
2534 }
2535
2536 let mut result_rv = RVector::from(Vector::Integer(result.into()));
2537 result_rv.set_attr(
2538 "dim".to_string(),
2539 RValue::vec(Vector::Integer(
2540 vec![Some(i64::try_from(n)?), Some(i64::try_from(ndim)?)].into(),
2541 )),
2542 );
2543 let col_names: Vec<Option<String>> = (0..ndim)
2545 .map(|d| {
2546 Some(if d == 0 {
2547 "row".to_string()
2548 } else if d == 1 {
2549 "col".to_string()
2550 } else {
2551 format!("dim{}", d + 1)
2552 })
2553 })
2554 .collect();
2555 result_rv.set_attr(
2556 "dimnames".to_string(),
2557 RValue::List(RList::new(vec![
2558 (None, RValue::Null),
2559 (None, RValue::vec(Vector::Character(col_names.into()))),
2560 ])),
2561 );
2562 return Ok(RValue::Vector(result_rv));
2563 }
2564 }
2565 }
2566
2567 let result: Vec<Option<i64>> = true_indices.into_iter().map(Some).collect();
2568 Ok(RValue::vec(Vector::Integer(result.into())))
2569 }
2570 _ => Ok(RValue::vec(Vector::Integer(vec![].into()))),
2571 }
2572}
2573
2574#[builtin(min_args = 1)]
2579fn builtin_which_min(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
2580 match args.first() {
2581 Some(RValue::Vector(v)) => {
2582 let doubles = v.to_doubles();
2583 let mut min_idx = None;
2584 let mut min_val = f64::INFINITY;
2585 for (i, x) in doubles.iter().enumerate() {
2586 if let Some(f) = x {
2587 if *f < min_val {
2588 min_val = *f;
2589 min_idx = Some(i);
2590 }
2591 }
2592 }
2593 Ok(RValue::vec(Vector::Integer(
2594 vec![min_idx.map(|i| i64::try_from(i).unwrap_or(0) + 1)].into(),
2595 )))
2596 }
2597 _ => Ok(RValue::vec(Vector::Integer(vec![].into()))),
2598 }
2599}
2600
2601#[builtin(min_args = 1)]
2606fn builtin_which_max(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
2607 match args.first() {
2608 Some(RValue::Vector(v)) => {
2609 let doubles = v.to_doubles();
2610 let mut max_idx = None;
2611 let mut max_val = f64::NEG_INFINITY;
2612 for (i, x) in doubles.iter().enumerate() {
2613 if let Some(f) = x {
2614 if *f > max_val {
2615 max_val = *f;
2616 max_idx = Some(i);
2617 }
2618 }
2619 }
2620 Ok(RValue::vec(Vector::Integer(
2621 vec![max_idx.map(|i| i64::try_from(i).unwrap_or(0) + 1)].into(),
2622 )))
2623 }
2624 _ => Ok(RValue::vec(Vector::Integer(vec![].into()))),
2625 }
2626}
2627
2628#[builtin(min_args = 2)]
2638fn builtin_append(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2639 match (args.first(), args.get(1)) {
2640 (Some(RValue::Vector(v1)), Some(RValue::Vector(v2))) => {
2641 let x_len = v1.inner.len();
2643 let after = named
2644 .iter()
2645 .find(|(k, _)| k == "after")
2646 .map(|(_, v)| v)
2647 .or(args.get(2))
2648 .and_then(|v| v.as_vector()?.as_integer_scalar())
2649 .map(|a| usize::try_from(a.max(0)).unwrap_or(0).min(x_len))
2650 .unwrap_or(x_len);
2651
2652 let has_char = matches!(v1.inner, Vector::Character(_))
2654 || matches!(v2.inner, Vector::Character(_));
2655 let has_complex =
2656 matches!(v1.inner, Vector::Complex(_)) || matches!(v2.inner, Vector::Complex(_));
2657 let has_double =
2658 matches!(v1.inner, Vector::Double(_)) || matches!(v2.inner, Vector::Double(_));
2659 let has_int =
2660 matches!(v1.inner, Vector::Integer(_)) || matches!(v2.inner, Vector::Integer(_));
2661
2662 let result = if has_char {
2663 let x = v1.to_characters();
2664 let vals = v2.to_characters();
2665 let mut out = x[..after].to_vec();
2666 out.extend(vals);
2667 out.extend_from_slice(&x[after..]);
2668 Vector::Character(out.into())
2669 } else if has_complex {
2670 let x = v1.inner.to_complex();
2671 let vals = v2.inner.to_complex();
2672 let mut out = x[..after].to_vec();
2673 out.extend(vals);
2674 out.extend_from_slice(&x[after..]);
2675 Vector::Complex(out.into())
2676 } else if has_double {
2677 let x = v1.to_doubles();
2678 let vals = v2.to_doubles();
2679 let mut out = x[..after].to_vec();
2680 out.extend(vals);
2681 out.extend_from_slice(&x[after..]);
2682 Vector::Double(out.into())
2683 } else if has_int {
2684 let x = v1.to_integers();
2685 let vals = v2.to_integers();
2686 let mut out = x[..after].to_vec();
2687 out.extend(vals);
2688 out.extend_from_slice(&x[after..]);
2689 Vector::Integer(out.into())
2690 } else {
2691 let x = v1.to_logicals();
2693 let vals = v2.to_logicals();
2694 let mut out = x[..after].to_vec();
2695 out.extend(vals);
2696 out.extend_from_slice(&x[after..]);
2697 Vector::Logical(out.into())
2698 };
2699 Ok(RValue::vec(result))
2700 }
2701 (Some(RValue::List(l1)), Some(RValue::List(l2))) => {
2702 let x_len = l1.values.len();
2703 let after = named
2704 .iter()
2705 .find(|(k, _)| k == "after")
2706 .map(|(_, v)| v)
2707 .or(args.get(2))
2708 .and_then(|v| v.as_vector()?.as_integer_scalar())
2709 .map(|a| usize::try_from(a.max(0)).unwrap_or(0).min(x_len))
2710 .unwrap_or(x_len);
2711 let mut out = l1.values[..after].to_vec();
2712 out.extend(l2.values.clone());
2713 out.extend_from_slice(&l1.values[after..]);
2714 Ok(RValue::List(RList::new(out)))
2715 }
2716 _ => Ok(args.first().cloned().unwrap_or(RValue::Null)),
2717 }
2718}
2719
2720#[builtin(min_args = 1, namespace = "utils")]
2726fn builtin_head(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2727 let n = named
2728 .iter()
2729 .find(|(k, _)| k == "n")
2730 .map(|(_, v)| v)
2731 .or(args.get(1))
2732 .and_then(|v| v.as_vector()?.as_integer_scalar())
2733 .unwrap_or(6);
2734 let n = usize::try_from(n).unwrap_or(0);
2735 match args.first() {
2736 Some(RValue::Vector(v)) => {
2737 let result = match &v.inner {
2738 Vector::Raw(vals) => Vector::Raw(vals[..n.min(vals.len())].to_vec()),
2739 Vector::Double(vals) => Vector::Double(vals.slice(0, n.min(vals.len()))),
2740 Vector::Integer(vals) => Vector::Integer(vals.slice(0, n.min(vals.len()))),
2741 Vector::Logical(vals) => Vector::Logical(vals[..n.min(vals.len())].to_vec().into()),
2742 Vector::Complex(vals) => Vector::Complex(vals[..n.min(vals.len())].to_vec().into()),
2743 Vector::Character(vals) => {
2744 Vector::Character(vals[..n.min(vals.len())].to_vec().into())
2745 }
2746 };
2747 Ok(RValue::vec(result))
2748 }
2749 _ => Ok(RValue::Null),
2750 }
2751}
2752
2753#[builtin(min_args = 1, namespace = "utils")]
2759fn builtin_tail(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2760 let n = named
2761 .iter()
2762 .find(|(k, _)| k == "n")
2763 .map(|(_, v)| v)
2764 .or(args.get(1))
2765 .and_then(|v| v.as_vector()?.as_integer_scalar())
2766 .unwrap_or(6);
2767 let n = usize::try_from(n).unwrap_or(0);
2768 match args.first() {
2769 Some(RValue::Vector(v)) => {
2770 let len = v.len();
2771 let start = len.saturating_sub(n);
2772 let result = match &v.inner {
2773 Vector::Raw(vals) => Vector::Raw(vals[start..].to_vec()),
2774 Vector::Double(vals) => Vector::Double(vals.slice(start, vals.len() - start)),
2775 Vector::Integer(vals) => Vector::Integer(vals.slice(start, vals.len() - start)),
2776 Vector::Logical(vals) => Vector::Logical(vals[start..].to_vec().into()),
2777 Vector::Complex(vals) => Vector::Complex(vals[start..].to_vec().into()),
2778 Vector::Character(vals) => Vector::Character(vals[start..].to_vec().into()),
2779 };
2780 Ok(RValue::vec(result))
2781 }
2782 _ => Ok(RValue::Null),
2783 }
2784}
2785
2786#[builtin(min_args = 0)]
2792fn builtin_range(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2793 let na_rm = named.iter().any(|(n, v)| {
2794 n == "na.rm" && v.as_vector().and_then(|v| v.as_logical_scalar()) == Some(true)
2795 });
2796 let mut min = f64::INFINITY;
2797 let mut max = f64::NEG_INFINITY;
2798 for arg in args {
2799 if let RValue::Vector(v) = arg {
2800 for x in v.to_doubles() {
2801 match x {
2802 Some(f) => {
2803 if f < min {
2804 min = f;
2805 }
2806 if f > max {
2807 max = f;
2808 }
2809 }
2810 None if !na_rm => {
2811 return Ok(RValue::vec(Vector::Double(vec![None, None].into())));
2812 }
2813 None => {}
2814 }
2815 }
2816 }
2817 }
2818 Ok(RValue::vec(Vector::Double(
2819 vec![Some(min), Some(max)].into(),
2820 )))
2821}
2822
2823#[builtin(min_args = 1)]
2830fn builtin_diff(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2831 let lag = named
2832 .iter()
2833 .find(|(n, _)| n == "lag")
2834 .map(|(_, v)| v)
2835 .or(args.get(1))
2836 .and_then(|v| v.as_vector()?.as_integer_scalar())
2837 .unwrap_or(1);
2838 let lag = usize::try_from(lag).map_err(|_| {
2839 RError::new(
2840 RErrorKind::Argument,
2841 format!("'lag' must be a positive integer, got {lag}"),
2842 )
2843 })?;
2844 if lag == 0 {
2845 return Err(RError::new(
2846 RErrorKind::Argument,
2847 "'lag' must be a positive integer, got 0".to_string(),
2848 ));
2849 }
2850
2851 let differences = named
2852 .iter()
2853 .find(|(n, _)| n == "differences")
2854 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
2855 .unwrap_or(1);
2856 let differences = usize::try_from(differences).map_err(|_| {
2857 RError::new(
2858 RErrorKind::Argument,
2859 format!("'differences' must be a positive integer, got {differences}"),
2860 )
2861 })?;
2862 if differences == 0 {
2863 return Err(RError::new(
2864 RErrorKind::Argument,
2865 "'differences' must be a positive integer, got 0".to_string(),
2866 ));
2867 }
2868
2869 match args.first() {
2870 Some(RValue::Vector(v)) => {
2871 let mut vals = v.to_doubles();
2872 for _ in 0..differences {
2873 if vals.len() <= lag {
2874 return Ok(RValue::vec(Vector::Double(vec![].into())));
2875 }
2876 vals = (lag..vals.len())
2877 .map(|i| match (vals[i - lag], vals[i]) {
2878 (Some(a), Some(b)) => Some(b - a),
2879 _ => None,
2880 })
2881 .collect();
2882 }
2883 Ok(RValue::vec(Vector::Double(vals.into())))
2884 }
2885 _ => Err(RError::new(
2886 RErrorKind::Argument,
2887 "invalid argument".to_string(),
2888 )),
2889 }
2890}
2891
2892#[builtin(min_args = 1)]
2904fn builtin_quantile(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
2905 let na_rm = named
2906 .iter()
2907 .find(|(n, _)| n == "na.rm")
2908 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
2909 .unwrap_or(false);
2910
2911 let qtype = named
2912 .iter()
2913 .find(|(n, _)| n == "type")
2914 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
2915 .unwrap_or(7);
2916 if qtype != 7 {
2917 return Err(RError::new(
2918 RErrorKind::Argument,
2919 format!(
2920 "only quantile type 7 is currently supported, got type {qtype}. \
2921 Type 7 is R's default and the most commonly used algorithm."
2922 ),
2923 ));
2924 }
2925
2926 let default_probs = vec![Some(0.0), Some(0.25), Some(0.5), Some(0.75), Some(1.0)];
2928 let probs: Vec<Option<f64>> = named
2929 .iter()
2930 .find(|(n, _)| n == "probs")
2931 .map(|(_, v)| v)
2932 .or(args.get(1))
2933 .map(|v| match v {
2934 RValue::Vector(rv) => rv.to_doubles(),
2935 RValue::Null => vec![],
2936 _ => default_probs.clone(),
2937 })
2938 .unwrap_or(default_probs);
2939
2940 let vals = match args.first() {
2942 Some(RValue::Vector(v)) => v.to_doubles(),
2943 _ => {
2944 return Err(RError::new(
2945 RErrorKind::Argument,
2946 "non-numeric argument to quantile".to_string(),
2947 ))
2948 }
2949 };
2950
2951 let mut data: Vec<f64> = if na_rm {
2953 vals.iter()
2954 .filter_map(|v| *v)
2955 .filter(|f| !f.is_nan())
2956 .collect()
2957 } else {
2958 let mut d = Vec::with_capacity(vals.len());
2960 for v in &vals {
2961 match v {
2962 Some(f) if !f.is_nan() => d.push(*f),
2963 _ => {
2964 return Err(RError::new(
2965 RErrorKind::Argument,
2966 "missing values and NaN's not allowed if 'na.rm' is FALSE".to_string(),
2967 ));
2968 }
2969 }
2970 }
2971 d
2972 };
2973
2974 data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
2975 let n = data.len();
2976
2977 let mut result_vals: Vec<Option<f64>> = Vec::with_capacity(probs.len());
2979 let mut names: Vec<Option<String>> = Vec::with_capacity(probs.len());
2980
2981 for prob in &probs {
2982 match prob {
2983 Some(p) => {
2984 if !(0.0..=1.0).contains(p) {
2985 return Err(RError::new(
2986 RErrorKind::Argument,
2987 format!("'probs' outside [0, 1], got {p}"),
2988 ));
2989 }
2990 if n == 0 {
2991 result_vals.push(None);
2992 } else if n == 1 {
2993 result_vals.push(Some(data[0]));
2994 } else {
2995 let h = (n - 1) as f64 * p;
2996 let lo = h.floor() as usize;
2997 let hi = h.ceil() as usize;
2998 let frac = h - h.floor();
2999 let val = data[lo] + frac * (data[hi] - data[lo]);
3000 result_vals.push(Some(val));
3001 }
3002 let pct = p * 100.0;
3004 let name = if pct == pct.floor() {
3005 format!("{}%", pct as i64)
3006 } else {
3007 format!("{pct}%")
3008 };
3009 names.push(Some(name));
3010 }
3011 None => {
3012 result_vals.push(None);
3013 names.push(None);
3014 }
3015 }
3016 }
3017
3018 let mut rv = RVector::from(Vector::Double(result_vals.into()));
3019 rv.set_attr(
3020 "names".to_string(),
3021 RValue::vec(Vector::Character(names.into())),
3022 );
3023 Ok(RValue::Vector(rv))
3024}
3025
3026#[builtin(name = "rep_len", min_args = 2)]
3032fn builtin_rep_len(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3033 if args.len() < 2 {
3034 return Err(RError::new(
3035 RErrorKind::Argument,
3036 "need 2 arguments".to_string(),
3037 ));
3038 }
3039 let length_out = args[1]
3040 .as_vector()
3041 .and_then(|v| v.as_integer_scalar())
3042 .unwrap_or(0);
3043 let length_out = usize::try_from(length_out).unwrap_or(0);
3044 match &args[0] {
3045 RValue::Vector(v) => {
3046 if v.is_empty() {
3047 return Ok(RValue::Vector(v.clone()));
3048 }
3049 match &v.inner {
3050 Vector::Raw(vals) => Ok(RValue::vec(Vector::Raw(
3051 vals.iter().cycle().take(length_out).copied().collect(),
3052 ))),
3053 Vector::Double(vals) => Ok(RValue::vec(Vector::Double(
3054 vals.iter_opt()
3055 .cycle()
3056 .take(length_out)
3057 .collect::<Vec<_>>()
3058 .into(),
3059 ))),
3060 Vector::Integer(vals) => Ok(RValue::vec(Vector::Integer(
3061 vals.iter_opt()
3062 .cycle()
3063 .take(length_out)
3064 .collect::<Vec<_>>()
3065 .into(),
3066 ))),
3067 Vector::Logical(vals) => Ok(RValue::vec(Vector::Logical(
3068 vals.iter()
3069 .cycle()
3070 .take(length_out)
3071 .cloned()
3072 .collect::<Vec<_>>()
3073 .into(),
3074 ))),
3075 Vector::Complex(vals) => Ok(RValue::vec(Vector::Complex(
3076 vals.iter()
3077 .cycle()
3078 .take(length_out)
3079 .cloned()
3080 .collect::<Vec<_>>()
3081 .into(),
3082 ))),
3083 Vector::Character(vals) => Ok(RValue::vec(Vector::Character(
3084 vals.iter()
3085 .cycle()
3086 .take(length_out)
3087 .cloned()
3088 .collect::<Vec<_>>()
3089 .into(),
3090 ))),
3091 }
3092 }
3093 _ => Err(RError::new(
3094 RErrorKind::Argument,
3095 "invalid argument".to_string(),
3096 )),
3097 }
3098}
3099
3100#[builtin(min_args = 2)]
3106fn builtin_rep_int(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3107 if args.len() < 2 {
3108 return Err(RError::new(
3109 RErrorKind::Argument,
3110 "need 2 arguments".to_string(),
3111 ));
3112 }
3113 let times = args[1]
3114 .as_vector()
3115 .and_then(|v| v.as_integer_scalar())
3116 .unwrap_or(1);
3117 let times = usize::try_from(times)?;
3118 match &args[0] {
3119 RValue::Vector(v) => match &v.inner {
3120 Vector::Raw(vals) => Ok(RValue::vec(Vector::Raw(
3121 vals.iter()
3122 .cycle()
3123 .take(vals.len() * times)
3124 .copied()
3125 .collect(),
3126 ))),
3127 Vector::Double(vals) => Ok(RValue::vec(Vector::Double(
3128 vals.iter_opt()
3129 .cycle()
3130 .take(vals.len() * times)
3131 .collect::<Vec<_>>()
3132 .into(),
3133 ))),
3134 Vector::Integer(vals) => Ok(RValue::vec(Vector::Integer(
3135 vals.iter_opt()
3136 .cycle()
3137 .take(vals.len() * times)
3138 .collect::<Vec<_>>()
3139 .into(),
3140 ))),
3141 Vector::Logical(vals) => Ok(RValue::vec(Vector::Logical(
3142 vals.iter()
3143 .cycle()
3144 .take(vals.len() * times)
3145 .cloned()
3146 .collect::<Vec<_>>()
3147 .into(),
3148 ))),
3149 Vector::Complex(vals) => Ok(RValue::vec(Vector::Complex(
3150 vals.iter()
3151 .cycle()
3152 .take(vals.len() * times)
3153 .cloned()
3154 .collect::<Vec<_>>()
3155 .into(),
3156 ))),
3157 Vector::Character(vals) => Ok(RValue::vec(Vector::Character(
3158 vals.iter()
3159 .cycle()
3160 .take(vals.len() * times)
3161 .cloned()
3162 .collect::<Vec<_>>()
3163 .into(),
3164 ))),
3165 },
3166 _ => Err(RError::new(
3167 RErrorKind::Argument,
3168 "invalid argument".to_string(),
3169 )),
3170 }
3171}
3172
3173#[cfg(feature = "linalg")]
3175fn rvalue_to_array2(val: &RValue) -> Result<Array2<f64>, RError> {
3176 let (data, dim_attr) = match val {
3177 RValue::Vector(rv) => (rv.to_doubles(), rv.get_attr("dim")),
3178 _ => {
3179 return Err(RError::new(
3180 RErrorKind::Type,
3181 "requires numeric matrix/vector arguments".to_string(),
3182 ))
3183 }
3184 };
3185 let (nrow, ncol) = match dim_attr {
3186 Some(RValue::Vector(rv)) => match &rv.inner {
3187 Vector::Integer(d) if d.len() >= 2 => (
3188 usize::try_from(d.get_opt(0).unwrap_or(0)).unwrap_or(0),
3189 usize::try_from(d.get_opt(1).unwrap_or(0)).unwrap_or(0),
3190 ),
3191 _ => (data.len(), 1),
3192 },
3193 _ => (data.len(), 1),
3194 };
3195 let flat: Vec<f64> = data.iter().map(|x| x.unwrap_or(f64::NAN)).collect();
3196 Array2::from_shape_vec((nrow, ncol).f(), flat)
3197 .map_err(|source| -> RError { MathError::Shape { source }.into() })
3198}
3199
3200#[cfg(feature = "linalg")]
3202fn array2_to_rvalue(arr: &Array2<f64>) -> RValue {
3203 let (nrow, ncol) = (arr.nrows(), arr.ncols());
3204 let mut result = Vec::with_capacity(nrow * ncol);
3205 for j in 0..ncol {
3206 for i in 0..nrow {
3207 result.push(Some(arr[[i, j]]));
3208 }
3209 }
3210 let mut rv = RVector::from(Vector::Double(result.into()));
3211 rv.set_attr(
3212 "dim".to_string(),
3213 RValue::vec(Vector::Integer(
3214 vec![
3215 Some(i64::try_from(nrow).unwrap_or(0)),
3216 Some(i64::try_from(ncol).unwrap_or(0)),
3217 ]
3218 .into(),
3219 )),
3220 );
3221 RValue::Vector(rv)
3222}
3223
3224#[cfg(feature = "linalg")]
3226fn rvalue_to_dmatrix(val: &RValue) -> Result<DMatrix<f64>, RError> {
3227 let (data, dim_attr) = match val {
3228 RValue::Vector(rv) => (rv.to_doubles(), rv.get_attr("dim")),
3229 _ => {
3230 return Err(RError::new(
3231 RErrorKind::Type,
3232 "requires numeric matrix/vector arguments".to_string(),
3233 ))
3234 }
3235 };
3236 let (nrow, ncol) = match dim_attr {
3237 Some(RValue::Vector(rv)) => match &rv.inner {
3238 Vector::Integer(d) if d.len() >= 2 => (
3239 usize::try_from(d.get_opt(0).unwrap_or(0)).unwrap_or(0),
3240 usize::try_from(d.get_opt(1).unwrap_or(0)).unwrap_or(0),
3241 ),
3242 _ => (data.len(), 1),
3243 },
3244 _ => (data.len(), 1),
3245 };
3246 let flat: Vec<f64> = data.iter().map(|x| x.unwrap_or(f64::NAN)).collect();
3247 Ok(DMatrix::from_vec(nrow, ncol, flat))
3249}
3250
3251#[cfg(feature = "linalg")]
3253fn dmatrix_to_rvalue(mat: &DMatrix<f64>) -> RValue {
3254 let nrow = mat.nrows();
3255 let ncol = mat.ncols();
3256 let data: Vec<Option<f64>> = mat.as_slice().iter().copied().map(Some).collect();
3258 let mut rv = RVector::from(Vector::Double(data.into()));
3259 rv.set_attr(
3260 "dim".to_string(),
3261 RValue::vec(Vector::Integer(
3262 vec![
3263 Some(i64::try_from(nrow).unwrap_or(0)),
3264 Some(i64::try_from(ncol).unwrap_or(0)),
3265 ]
3266 .into(),
3267 )),
3268 );
3269 RValue::Vector(rv)
3270}
3271
3272fn matrix_dimnames(value: &RValue) -> MatrixDimNames {
3273 let dimnames = match value {
3274 RValue::Vector(rv) => rv.get_attr("dimnames"),
3275 RValue::List(list) => list.get_attr("dimnames"),
3276 _ => None,
3277 };
3278
3279 let row_names = super::dimnames_component(dimnames, 0);
3280 let col_names = super::dimnames_component(dimnames, 1);
3281 (row_names, col_names)
3282}
3283
3284fn set_matrix_attrs(
3285 rv: &mut RVector,
3286 nrow: usize,
3287 ncol: usize,
3288 row_names: Option<DimNameVec>,
3289 col_names: Option<DimNameVec>,
3290) -> Result<(), RError> {
3291 rv.set_attr(
3292 "class".to_string(),
3293 RValue::vec(Vector::Character(
3294 vec![Some("matrix".to_string()), Some("array".to_string())].into(),
3295 )),
3296 );
3297 rv.set_attr(
3298 "dim".to_string(),
3299 RValue::vec(Vector::Integer(
3300 vec![Some(i64::try_from(nrow)?), Some(i64::try_from(ncol)?)].into(),
3301 )),
3302 );
3303 if let Some(dimnames) =
3304 super::bind_dimnames_value(row_names.unwrap_or_default(), col_names.unwrap_or_default())
3305 {
3306 rv.set_attr("dimnames".to_string(), dimnames);
3307 }
3308 Ok(())
3309}
3310
3311#[cfg(feature = "linalg")]
3313#[builtin(min_args = 1)]
3314fn builtin_crossprod(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3315 let x = rvalue_to_array2(args.first().unwrap_or(&RValue::Null))?;
3316 let y = if let Some(b) = args.get(1) {
3317 rvalue_to_array2(b)?
3318 } else {
3319 x.clone()
3320 };
3321 let xt = x.t();
3322 let result = xt.dot(&y);
3323 let mut out = match array2_to_rvalue(&result) {
3324 RValue::Vector(rv) => rv,
3325 _ => unreachable!(),
3326 };
3327 let (_, x_col_names) = matrix_dimnames(args.first().unwrap_or(&RValue::Null));
3328 let (_, y_col_names) = args.get(1).map_or((None, None), matrix_dimnames);
3329 set_matrix_attrs(
3330 &mut out,
3331 result.nrows(),
3332 result.ncols(),
3333 x_col_names,
3334 if args.get(1).is_some() {
3335 y_col_names
3336 } else {
3337 matrix_dimnames(args.first().unwrap_or(&RValue::Null)).1
3338 },
3339 )?;
3340 Ok(RValue::Vector(out))
3341}
3342
3343#[cfg(feature = "linalg")]
3345#[builtin(min_args = 1)]
3346fn builtin_tcrossprod(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3347 let x = rvalue_to_array2(args.first().unwrap_or(&RValue::Null))?;
3348 let y = if let Some(b) = args.get(1) {
3349 rvalue_to_array2(b)?
3350 } else {
3351 x.clone()
3352 };
3353 let yt = y.t();
3354 let result = x.dot(&yt);
3355 let mut out = match array2_to_rvalue(&result) {
3356 RValue::Vector(rv) => rv,
3357 _ => unreachable!(),
3358 };
3359 let (x_row_names, _) = matrix_dimnames(args.first().unwrap_or(&RValue::Null));
3360 let (y_row_names, _) = args.get(1).map_or((None, None), matrix_dimnames);
3361 set_matrix_attrs(
3362 &mut out,
3363 result.nrows(),
3364 result.ncols(),
3365 x_row_names,
3366 if args.get(1).is_some() {
3367 y_row_names
3368 } else {
3369 matrix_dimnames(args.first().unwrap_or(&RValue::Null)).0
3370 },
3371 )?;
3372 Ok(RValue::Vector(out))
3373}
3374
3375#[cfg(feature = "linalg")]
3381#[builtin(min_args = 1)]
3382fn builtin_norm(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
3383 let norm_type = named
3384 .iter()
3385 .find(|(n, _)| n == "type")
3386 .map(|(_, v)| v)
3387 .or(args.get(1))
3388 .and_then(|v| v.as_vector()?.as_character_scalar())
3389 .unwrap_or_else(|| "O".to_string());
3390
3391 let mat = rvalue_to_array2(args.first().unwrap_or(&RValue::Null))?;
3392 let nrow = mat.nrows();
3393 let ncol = mat.ncols();
3394
3395 let result = match norm_type.as_str() {
3396 "O" | "o" | "1" => {
3397 (0..ncol)
3399 .map(|j| (0..nrow).map(|i| mat[[i, j]].abs()).sum::<f64>())
3400 .fold(f64::NEG_INFINITY, f64::max)
3401 }
3402 "I" | "i" => {
3403 (0..nrow)
3405 .map(|i| (0..ncol).map(|j| mat[[i, j]].abs()).sum::<f64>())
3406 .fold(f64::NEG_INFINITY, f64::max)
3407 }
3408 "F" | "f" => {
3409 mat.iter().map(|x| x * x).sum::<f64>().sqrt()
3411 }
3412 "M" | "m" => {
3413 mat.iter()
3415 .map(|x| x.abs())
3416 .fold(f64::NEG_INFINITY, f64::max)
3417 }
3418 other => {
3419 return Err(RError::new(
3420 RErrorKind::Argument,
3421 format!(
3422 "invalid norm type '{}'. Use \"O\" (one-norm), \"I\" (infinity-norm), \
3423 \"F\" (Frobenius), or \"M\" (max modulus)",
3424 other
3425 ),
3426 ));
3427 }
3428 };
3429
3430 Ok(RValue::vec(Vector::Double(vec![Some(result)].into())))
3431}
3432
3433#[cfg(feature = "linalg")]
3440#[builtin(min_args = 1)]
3441fn builtin_solve(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
3442 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3443 let nrow = a.nrows();
3444 let ncol = a.ncols();
3445
3446 if nrow != ncol {
3447 return Err(RError::new(
3448 RErrorKind::Argument,
3449 format!(
3450 "solve() requires a square matrix, but got {}x{}. \
3451 Non-square systems need qr.solve() or a least-squares method",
3452 nrow, ncol
3453 ),
3454 ));
3455 }
3456 let n = nrow;
3457
3458 if n == 0 {
3459 return Err(RError::new(
3460 RErrorKind::Argument,
3461 "solve() requires a non-empty matrix".to_string(),
3462 ));
3463 }
3464
3465 let b_arg = named
3466 .iter()
3467 .find(|(name, _)| name == "b")
3468 .map(|(_, v)| v)
3469 .or(args.get(1));
3470
3471 let b = match b_arg {
3472 Some(val) => rvalue_to_dmatrix(val)?,
3473 None => DMatrix::identity(n, n),
3474 };
3475
3476 if b.nrows() != n {
3477 return Err(RError::new(
3478 RErrorKind::Argument,
3479 format!(
3480 "solve(a, b): nrow(a) = {} but nrow(b) = {} — they must match",
3481 n,
3482 b.nrows()
3483 ),
3484 ));
3485 }
3486
3487 let lu = a.lu();
3488 let result = lu.solve(&b).ok_or_else(|| {
3489 RError::other(
3490 "solve(): matrix is singular (or very close to singular). \
3491 Check that your matrix has full rank — its determinant is effectively zero",
3492 )
3493 })?;
3494
3495 Ok(dmatrix_to_rvalue(&result))
3496}
3497
3498#[cfg(feature = "linalg")]
3500#[builtin(min_args = 1)]
3501fn builtin_det(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3502 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3503 let n = a.nrows();
3504 if n != a.ncols() {
3505 return Err(RError::new(
3506 RErrorKind::Argument,
3507 format!("'x' must be a square matrix, got {}x{}", n, a.ncols()),
3508 ));
3509 }
3510 if n == 0 {
3511 return Ok(RValue::vec(Vector::Double(vec![Some(1.0)].into())));
3512 }
3513
3514 let det = a.lu().determinant();
3515 Ok(RValue::vec(Vector::Double(vec![Some(det)].into())))
3516}
3517
3518#[cfg(feature = "linalg")]
3522#[builtin(min_args = 1)]
3523fn builtin_chol(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3524 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3525 let n = a.nrows();
3526 if n != a.ncols() {
3527 return Err(RError::new(
3528 RErrorKind::Argument,
3529 format!("'x' must be a square matrix, got {}x{}", n, a.ncols()),
3530 ));
3531 }
3532
3533 let chol = a.cholesky().ok_or_else(|| {
3536 RError::other(
3537 "matrix is not positive definite — Cholesky decomposition failed. \
3538 Ensure the matrix is symmetric and all eigenvalues are positive",
3539 )
3540 })?;
3541 let l = chol.l();
3542 let r = l.transpose();
3543
3544 Ok(dmatrix_to_rvalue(&r))
3545}
3546
3547#[cfg(feature = "linalg")]
3557#[builtin(min_args = 1)]
3558fn builtin_qr(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3559 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3560 let m = a.nrows();
3561 let n = a.ncols();
3562
3563 let qr = a.col_piv_qr();
3564 let q = qr.q();
3565 let r = qr.r();
3566
3567 let k = m.min(n);
3569 let mut compact = DMatrix::<f64>::zeros(m, n);
3570 for i in 0..m {
3571 for j in 0..n {
3572 if i <= j {
3573 compact[(i, j)] = r[(i, j)];
3574 }
3575 }
3576 }
3577 let qr_val = dmatrix_to_rvalue(&compact);
3578
3579 let tol = f64::EPSILON * (m.max(n) as f64) * {
3581 let mut max_diag = 0.0f64;
3582 for i in 0..k {
3583 max_diag = max_diag.max(r[(i, i)].abs());
3584 }
3585 max_diag
3586 };
3587 let mut rank = 0i64;
3588 for i in 0..k {
3589 if r[(i, i)].abs() > tol {
3590 rank += 1;
3591 }
3592 }
3593
3594 let p = qr.p();
3597 let mut pivot_mat = DMatrix::<f64>::zeros(1, n);
3598 for j in 0..n {
3599 pivot_mat[(0, j)] = j as f64;
3600 }
3601 p.permute_columns(&mut pivot_mat);
3602 let pivot: Vec<Option<i64>> = (0..n).map(|j| Some(pivot_mat[(0, j)] as i64 + 1)).collect();
3603
3604 let q_val = dmatrix_to_rvalue(&q);
3605
3606 let mut list = RList::new(vec![
3607 (Some("qr".to_string()), qr_val),
3608 (
3609 Some("rank".to_string()),
3610 RValue::vec(Vector::Integer(vec![Some(rank)].into())),
3611 ),
3612 (
3613 Some("pivot".to_string()),
3614 RValue::vec(Vector::Integer(pivot.into())),
3615 ),
3616 (Some("Q".to_string()), q_val),
3617 ]);
3618 list.set_attr(
3619 "class".to_string(),
3620 RValue::vec(Vector::Character(vec![Some("qr".to_string())].into())),
3621 );
3622 Ok(RValue::List(list))
3623}
3624
3625#[cfg(feature = "linalg")]
3632#[builtin(min_args = 1)]
3633fn builtin_svd(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3634 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3635 let m = a.nrows();
3636 let n = a.ncols();
3637
3638 if m == 0 || n == 0 {
3639 return Err(RError::new(
3640 RErrorKind::Argument,
3641 "0 extent dimensions are not allowed".to_string(),
3642 ));
3643 }
3644
3645 let svd = a.svd(true, true);
3646 let k = m.min(n);
3647
3648 let d_vals: Vec<Option<f64>> = svd.singular_values.iter().copied().map(Some).collect();
3650
3651 let u_full = svd
3653 .u
3654 .ok_or_else(|| RError::other("svd(): failed to compute left singular vectors"))?;
3655 let u = u_full.columns(0, k).clone_owned();
3656
3657 let v_t_full = svd
3659 .v_t
3660 .ok_or_else(|| RError::other("svd(): failed to compute right singular vectors"))?;
3661 let v = v_t_full.rows(0, k).transpose();
3662
3663 Ok(RValue::List(RList::new(vec![
3664 (
3665 Some("d".to_string()),
3666 RValue::vec(Vector::Double(d_vals.into())),
3667 ),
3668 (Some("u".to_string()), dmatrix_to_rvalue(&u)),
3669 (Some("v".to_string()), dmatrix_to_rvalue(&v)),
3670 ])))
3671}
3672
3673#[cfg(feature = "linalg")]
3685#[builtin(min_args = 1)]
3686fn builtin_eigen(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3687 let a = rvalue_to_dmatrix(args.first().unwrap_or(&RValue::Null))?;
3688 let n = a.nrows();
3689 if n != a.ncols() {
3690 return Err(RError::new(
3691 RErrorKind::Argument,
3692 format!(
3693 "non-square matrix in 'eigen': {}x{} — eigen() requires a square matrix",
3694 n,
3695 a.ncols()
3696 ),
3697 ));
3698 }
3699
3700 if n == 0 {
3701 return Ok(RValue::List(RList::new(vec![
3702 (
3703 Some("values".to_string()),
3704 RValue::vec(Vector::Double(vec![].into())),
3705 ),
3706 (
3707 Some("vectors".to_string()),
3708 dmatrix_to_rvalue(&DMatrix::<f64>::zeros(0, 0)),
3709 ),
3710 ])));
3711 }
3712
3713 let sym_tol = 1e-10;
3715 let mut is_symmetric = true;
3716 'sym_check: for i in 0..n {
3717 for j in (i + 1)..n {
3718 if (a[(i, j)] - a[(j, i)]).abs() > sym_tol * (a[(i, j)].abs() + a[(j, i)].abs() + 1.0) {
3719 is_symmetric = false;
3720 break 'sym_check;
3721 }
3722 }
3723 }
3724
3725 if is_symmetric {
3726 let eig = a.symmetric_eigen();
3728
3729 let mut eigen_pairs: Vec<(f64, usize)> = eig
3731 .eigenvalues
3732 .iter()
3733 .copied()
3734 .enumerate()
3735 .map(|(i, v)| (v, i))
3736 .collect();
3737 eigen_pairs.sort_by(|a_pair, b_pair| {
3738 b_pair
3739 .0
3740 .partial_cmp(&a_pair.0)
3741 .unwrap_or(std::cmp::Ordering::Equal)
3742 });
3743
3744 let values: Vec<Option<f64>> = eigen_pairs.iter().map(|&(val, _)| Some(val)).collect();
3745
3746 let mut vectors = DMatrix::<f64>::zeros(n, n);
3747 for (new_j, &(_, old_j)) in eigen_pairs.iter().enumerate() {
3748 for i in 0..n {
3749 vectors[(i, new_j)] = eig.eigenvectors[(i, old_j)];
3750 }
3751 }
3752
3753 Ok(RValue::List(RList::new(vec![
3754 (
3755 Some("values".to_string()),
3756 RValue::vec(Vector::Double(values.into())),
3757 ),
3758 (Some("vectors".to_string()), dmatrix_to_rvalue(&vectors)),
3759 ])))
3760 } else {
3761 let schur = a.schur();
3765 let (u_mat, t_mat) = schur.unpack();
3766
3767 let mut values = Vec::new();
3769 let mut has_complex = false;
3770 let mut i = 0;
3771 while i < n {
3772 if i + 1 < n && t_mat[(i + 1, i)].abs() > 1e-10 {
3773 has_complex = true;
3775 i += 2;
3776 } else {
3777 values.push(t_mat[(i, i)]);
3778 i += 1;
3779 }
3780 }
3781
3782 if has_complex {
3783 return Err(RError::other(
3784 "non-symmetric matrix has complex eigenvalues — \
3785 complex eigenvalue support is not yet implemented. \
3786 If the matrix should be symmetric, consider using (x + t(x))/2 \
3787 to symmetrize it.",
3788 ));
3789 }
3790
3791 values.sort_by(|a_val, b_val| {
3793 b_val
3794 .partial_cmp(a_val)
3795 .unwrap_or(std::cmp::Ordering::Equal)
3796 });
3797
3798 let vals: Vec<Option<f64>> = values.iter().copied().map(Some).collect();
3799
3800 let vectors = u_mat;
3802
3803 Ok(RValue::List(RList::new(vec![
3804 (
3805 Some("values".to_string()),
3806 RValue::vec(Vector::Double(vals.into())),
3807 ),
3808 (Some("vectors".to_string()), dmatrix_to_rvalue(&vectors)),
3809 ])))
3810 }
3811}
3812
3813#[builtin(min_args = 1)]
3817fn builtin_t(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3818 let x = args.first().unwrap_or(&RValue::Null);
3819 let rv = match x {
3820 RValue::Vector(rv) => rv,
3821 _ => {
3822 return Err(RError::new(
3823 RErrorKind::Type,
3824 "argument is not a matrix".to_string(),
3825 ))
3826 }
3827 };
3828
3829 let (nrow, ncol) = match rv.get_attr("dim") {
3830 Some(RValue::Vector(dim_rv)) => {
3831 let dims = dim_rv.to_integers();
3832 if dims.len() >= 2 {
3833 (
3834 usize::try_from(dims[0].unwrap_or(0))?,
3835 usize::try_from(dims[1].unwrap_or(0))?,
3836 )
3837 } else {
3838 (rv.inner.len(), 1)
3840 }
3841 }
3842 _ => (rv.inner.len(), 1), };
3844
3845 let indices: Vec<usize> = (0..nrow)
3847 .flat_map(|i| (0..ncol).map(move |j| j * nrow + i))
3848 .collect();
3849 let transposed = rv.inner.select_indices(&indices);
3850
3851 let mut out = RVector::from(transposed);
3852
3853 let (row_names, col_names) = matrix_dimnames(x);
3855 set_matrix_attrs(&mut out, ncol, nrow, col_names, row_names)?;
3856 Ok(RValue::Vector(out))
3857}
3858
3859#[builtin(name = "complex")]
3870fn builtin_complex(_args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
3871 let real = named
3872 .iter()
3873 .find(|(n, _)| n == "real")
3874 .and_then(|(_, v)| v.as_vector())
3875 .map(|v| v.to_doubles())
3876 .unwrap_or_default();
3877
3878 let imaginary = named
3879 .iter()
3880 .find(|(n, _)| n == "imaginary")
3881 .and_then(|(_, v)| v.as_vector())
3882 .map(|v| v.to_doubles())
3883 .unwrap_or_default();
3884
3885 let length_out = match named
3886 .iter()
3887 .find(|(n, _)| n == "length.out")
3888 .and_then(|(_, v)| v.as_vector()?.as_integer_scalar())
3889 {
3890 Some(n) => usize::try_from(n)?,
3891 None => real.len().max(imaginary.len()),
3892 };
3893
3894 let result: Vec<Option<num_complex::Complex64>> = (0..length_out)
3895 .map(|i| {
3896 let re = real
3897 .get(i % real.len().max(1))
3898 .copied()
3899 .flatten()
3900 .unwrap_or(0.0);
3901 let im = imaginary
3902 .get(i % imaginary.len().max(1))
3903 .copied()
3904 .flatten()
3905 .unwrap_or(0.0);
3906 Some(num_complex::Complex64::new(re, im))
3907 })
3908 .collect();
3909
3910 Ok(RValue::vec(Vector::Complex(result.into())))
3911}
3912
3913#[builtin(name = "Re", min_args = 1)]
3918fn builtin_re(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3919 let v = args
3920 .first()
3921 .and_then(|v| v.as_vector())
3922 .ok_or_else(|| RError::new(RErrorKind::Type, "non-numeric argument to Re".to_string()))?;
3923 match v {
3924 Vector::Complex(vals) => {
3925 let result: Vec<Option<f64>> = vals.iter().map(|x| x.map(|c| c.re)).collect();
3926 Ok(RValue::vec(Vector::Double(result.into())))
3927 }
3928 _ => {
3929 Ok(RValue::vec(Vector::Double(v.to_doubles().into())))
3931 }
3932 }
3933}
3934
3935#[builtin(name = "Im", min_args = 1)]
3940fn builtin_im(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3941 let v = args
3942 .first()
3943 .and_then(|v| v.as_vector())
3944 .ok_or_else(|| RError::new(RErrorKind::Type, "non-numeric argument to Im".to_string()))?;
3945 match v {
3946 Vector::Complex(vals) => {
3947 let result: Vec<Option<f64>> = vals.iter().map(|x| x.map(|c| c.im)).collect();
3948 Ok(RValue::vec(Vector::Double(result.into())))
3949 }
3950 _ => {
3951 let result: Vec<Option<f64>> = vec![Some(0.0); v.len()];
3953 Ok(RValue::vec(Vector::Double(result.into())))
3954 }
3955 }
3956}
3957
3958#[builtin(name = "Mod", min_args = 1)]
3963fn builtin_mod_complex(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3964 let v = args
3965 .first()
3966 .and_then(|v| v.as_vector())
3967 .ok_or_else(|| RError::new(RErrorKind::Type, "non-numeric argument to Mod".to_string()))?;
3968 match v {
3969 Vector::Complex(vals) => {
3970 let result: Vec<Option<f64>> = vals.iter().map(|x| x.map(|c| c.norm())).collect();
3971 Ok(RValue::vec(Vector::Double(result.into())))
3972 }
3973 _ => {
3974 let result: Vec<Option<f64>> = v.to_doubles().iter().map(|x| x.map(f64::abs)).collect();
3976 Ok(RValue::vec(Vector::Double(result.into())))
3977 }
3978 }
3979}
3980
3981#[builtin(name = "Arg", min_args = 1)]
3986fn builtin_arg(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
3987 let v = args
3988 .first()
3989 .and_then(|v| v.as_vector())
3990 .ok_or_else(|| RError::new(RErrorKind::Type, "non-numeric argument to Arg".to_string()))?;
3991 match v {
3992 Vector::Complex(vals) => {
3993 let result: Vec<Option<f64>> = vals.iter().map(|x| x.map(|c| c.arg())).collect();
3994 Ok(RValue::vec(Vector::Double(result.into())))
3995 }
3996 _ => {
3997 let result: Vec<Option<f64>> = v
3999 .to_doubles()
4000 .iter()
4001 .map(|x| x.map(|f| if f >= 0.0 { 0.0 } else { std::f64::consts::PI }))
4002 .collect();
4003 Ok(RValue::vec(Vector::Double(result.into())))
4004 }
4005 }
4006}
4007
4008#[builtin(name = "Conj", min_args = 1)]
4013fn builtin_conj(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4014 let v = args
4015 .first()
4016 .and_then(|v| v.as_vector())
4017 .ok_or_else(|| RError::new(RErrorKind::Type, "non-numeric argument to Conj".to_string()))?;
4018 match v {
4019 Vector::Complex(vals) => {
4020 let result: Vec<Option<num_complex::Complex64>> =
4021 vals.iter().map(|x| x.map(|c| c.conj())).collect();
4022 Ok(RValue::vec(Vector::Complex(result.into())))
4023 }
4024 _ => {
4025 Ok(args[0].clone())
4027 }
4028 }
4029}
4030
4031#[builtin(name = "is.complex", min_args = 1)]
4036fn builtin_is_complex(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4037 let result = matches!(
4038 args.first(),
4039 Some(RValue::Vector(rv)) if matches!(rv.inner, Vector::Complex(_))
4040 );
4041 Ok(RValue::vec(Vector::Logical(vec![Some(result)].into())))
4042}
4043
4044#[builtin(name = "as.complex", min_args = 1)]
4049fn builtin_as_complex(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4050 let v = args
4051 .first()
4052 .and_then(|v| v.as_vector())
4053 .ok_or_else(|| RError::new(RErrorKind::Type, "cannot coerce to complex".to_string()))?;
4054 match v {
4055 Vector::Complex(vals) => Ok(RValue::vec(Vector::Complex(vals.clone()))),
4056 _ => {
4057 let result: Vec<Option<num_complex::Complex64>> = v
4058 .to_doubles()
4059 .iter()
4060 .map(|x| x.map(|f| num_complex::Complex64::new(f, 0.0)))
4061 .collect();
4062 Ok(RValue::vec(Vector::Complex(result.into())))
4063 }
4064 }
4065}
4066#[cfg(feature = "linalg")]
4071fn df_column_doubles(list: &RList, name: &str) -> Result<Vec<Option<f64>>, RError> {
4073 for (col_name, col_val) in &list.values {
4074 if col_name.as_deref() == Some(name) {
4075 return match col_val {
4076 RValue::Vector(rv) => Ok(rv.to_doubles()),
4077 _ => Err(RError::new(
4078 RErrorKind::Type,
4079 format!(
4080 "column '{}' is not a numeric vector — lm() requires numeric columns",
4081 name
4082 ),
4083 )),
4084 };
4085 }
4086 }
4087 Err(RError::new(
4088 RErrorKind::Name,
4089 format!(
4090 "column '{}' not found in data frame. Available columns: {}",
4091 name,
4092 list.values
4093 .iter()
4094 .filter_map(|(n, _)| n.as_deref())
4095 .join(", ")
4096 ),
4097 ))
4098}
4099
4100#[cfg(feature = "linalg")]
4101fn parse_formula_terms(expr: &Expr) -> Result<(String, Vec<String>), RError> {
4106 match expr {
4107 Expr::Formula { lhs, rhs } => {
4108 let response = lhs
4109 .as_ref()
4110 .and_then(|e| match e.as_ref() {
4111 Expr::Symbol(s) => Some(s.clone()),
4112 _ => None,
4113 })
4114 .ok_or_else(|| {
4115 RError::new(
4116 RErrorKind::Argument,
4117 "lm() formula must have a response variable on the left side of ~"
4118 .to_string(),
4119 )
4120 })?;
4121 let rhs_expr = rhs.as_ref().ok_or_else(|| {
4122 RError::new(
4123 RErrorKind::Argument,
4124 "lm() formula must have predictor(s) on the right side of ~".to_string(),
4125 )
4126 })?;
4127 let mut predictors = Vec::new();
4128 collect_additive_terms(rhs_expr, &mut predictors)?;
4129 if predictors.is_empty() {
4130 return Err(RError::new(
4131 RErrorKind::Argument,
4132 "lm() formula must have at least one predictor on the right side of ~"
4133 .to_string(),
4134 ));
4135 }
4136 Ok((response, predictors))
4137 }
4138 _ => Err(RError::new(
4139 RErrorKind::Argument,
4140 "lm() requires a formula as its first argument (e.g. y ~ x)".to_string(),
4141 )),
4142 }
4143}
4144
4145#[cfg(feature = "linalg")]
4146fn collect_additive_terms(expr: &Expr, out: &mut Vec<String>) -> Result<(), RError> {
4148 match expr {
4149 Expr::Symbol(s) => {
4150 out.push(s.clone());
4151 Ok(())
4152 }
4153 Expr::BinaryOp {
4154 op: crate::parser::ast::BinaryOp::Add,
4155 lhs,
4156 rhs,
4157 } => {
4158 collect_additive_terms(lhs, out)?;
4159 collect_additive_terms(rhs, out)?;
4160 Ok(())
4161 }
4162 _ => Err(RError::new(
4163 RErrorKind::Argument,
4164 format!(
4165 "lm() formula terms must be symbols or sums of symbols, got: {:?}",
4166 expr
4167 ),
4168 )),
4169 }
4170}
4171
4172#[cfg(feature = "linalg")]
4183#[interpreter_builtin(min_args = 1, namespace = "stats")]
4184fn interp_lm(
4185 args: &[RValue],
4186 named: &[(String, RValue)],
4187 _context: &BuiltinContext,
4188) -> Result<RValue, RError> {
4189 let formula_val = super::find_arg(args, named, "formula", 0).ok_or_else(|| {
4191 RError::new(
4192 RErrorKind::Argument,
4193 "lm() requires a formula argument".to_string(),
4194 )
4195 })?;
4196
4197 let formula_expr = match formula_val {
4198 RValue::Language(lang) => &*lang.inner,
4199 _ => {
4200 return Err(RError::new(
4201 RErrorKind::Type,
4202 "lm() first argument must be a formula (e.g. y ~ x), got a non-formula value"
4203 .to_string(),
4204 ))
4205 }
4206 };
4207
4208 let (response_name, predictor_names) = parse_formula_terms(formula_expr)?;
4209
4210 let data_val = super::find_arg(args, named, "data", 1).ok_or_else(|| {
4212 RError::new(
4213 RErrorKind::Argument,
4214 "lm() requires a 'data' argument — pass a data frame containing the model variables"
4215 .to_string(),
4216 )
4217 })?;
4218
4219 let data_list = match data_val {
4220 RValue::List(list) => list,
4221 _ => {
4222 return Err(RError::new(
4223 RErrorKind::Type,
4224 "lm() 'data' argument must be a data frame".to_string(),
4225 ))
4226 }
4227 };
4228
4229 let y_raw = df_column_doubles(data_list, &response_name)?;
4231 let n = y_raw.len();
4232 if n == 0 {
4233 return Err(RError::new(
4234 RErrorKind::Argument,
4235 "lm() requires at least one observation".to_string(),
4236 ));
4237 }
4238
4239 let p = predictor_names.len(); let y: Vec<f64> = y_raw
4243 .into_iter()
4244 .map(|v| {
4245 v.ok_or_else(|| {
4246 RError::new(
4247 RErrorKind::Argument,
4248 format!(
4249 "NA values in response variable '{}' — lm() does not yet support na.action",
4250 response_name
4251 ),
4252 )
4253 })
4254 })
4255 .collect::<Result<Vec<_>, _>>()?;
4256
4257 let ncol = p + 1;
4259 let mut x_data = vec![0.0; n * ncol];
4260 for item in x_data.iter_mut().take(n) {
4262 *item = 1.0; }
4264 for (j, pred_name) in predictor_names.iter().enumerate() {
4266 let col_raw = df_column_doubles(data_list, pred_name)?;
4267 if col_raw.len() != n {
4268 return Err(RError::new(
4269 RErrorKind::Argument,
4270 format!(
4271 "predictor '{}' has {} observations but response '{}' has {} — they must match",
4272 pred_name,
4273 col_raw.len(),
4274 response_name,
4275 n
4276 ),
4277 ));
4278 }
4279 for (i, val) in col_raw.into_iter().enumerate() {
4280 let v = val.ok_or_else(|| {
4281 RError::new(
4282 RErrorKind::Argument,
4283 format!(
4284 "NA values in predictor '{}' — lm() does not yet support na.action",
4285 pred_name
4286 ),
4287 )
4288 })?;
4289 x_data[i + (j + 1) * n] = v; }
4291 }
4292
4293 let x = Array2::from_shape_vec((n, ncol).f(), x_data)
4294 .map_err(|source| -> RError { MathError::Shape { source }.into() })?;
4295 let y_arr = Array1::from_vec(y);
4296
4297 let xt = x.t();
4299 let xtx = xt.dot(&x);
4300 let xty = xt.dot(&y_arr);
4301
4302 let beta = solve_linear_system(&xtx, &xty)?;
4304
4305 let fitted: Vec<f64> = (0..n)
4307 .map(|i| {
4308 let mut val = 0.0;
4309 for j in 0..ncol {
4310 val += x[[i, j]] * beta[j];
4311 }
4312 val
4313 })
4314 .collect();
4315
4316 let residuals: Vec<f64> = (0..n).map(|i| y_arr[i] - fitted[i]).collect();
4317
4318 let mut coef_names: Vec<Option<String>> = Vec::with_capacity(ncol);
4320 coef_names.push(Some("(Intercept)".to_string()));
4321 for name in &predictor_names {
4322 coef_names.push(Some(name.clone()));
4323 }
4324
4325 let coef_doubles: Vec<Option<f64>> = beta.iter().copied().map(Some).collect();
4327 let mut coef_rv = RVector::from(Vector::Double(coef_doubles.into()));
4328 coef_rv.set_attr(
4329 "names".to_string(),
4330 RValue::vec(Vector::Character(coef_names.into())),
4331 );
4332
4333 let fitted_doubles: Vec<Option<f64>> = fitted.into_iter().map(Some).collect();
4335
4336 let residual_doubles: Vec<Option<f64>> = residuals.into_iter().map(Some).collect();
4338
4339 let mut result = RList::new(vec![
4341 (Some("coefficients".to_string()), RValue::Vector(coef_rv)),
4342 (
4343 Some("residuals".to_string()),
4344 RValue::vec(Vector::Double(residual_doubles.into())),
4345 ),
4346 (
4347 Some("fitted.values".to_string()),
4348 RValue::vec(Vector::Double(fitted_doubles.into())),
4349 ),
4350 (Some("call".to_string()), RValue::Null),
4351 ]);
4352 result.set_attr(
4353 "class".to_string(),
4354 RValue::vec(Vector::Character(vec![Some("lm".to_string())].into())),
4355 );
4356
4357 Ok(RValue::List(result))
4358}
4359
4360#[cfg(feature = "linalg")]
4361fn solve_linear_system(a: &Array2<f64>, b: &Array1<f64>) -> Result<Vec<f64>, RError> {
4364 let n = a.nrows();
4365 if n != a.ncols() || n != b.len() {
4366 return Err(RError::new(
4367 RErrorKind::Other,
4368 "internal error: solve_linear_system dimension mismatch".to_string(),
4369 ));
4370 }
4371 if n == 0 {
4372 return Ok(vec![]);
4373 }
4374
4375 let mut aug = Array2::<f64>::zeros((n, n + 1));
4377 for i in 0..n {
4378 for j in 0..n {
4379 aug[[i, j]] = a[[i, j]];
4380 }
4381 aug[[i, n]] = b[i];
4382 }
4383
4384 for col in 0..n {
4386 let mut max_row = col;
4388 let mut max_val = aug[[col, col]].abs();
4389 for row in (col + 1)..n {
4390 let val = aug[[row, col]].abs();
4391 if val > max_val {
4392 max_val = val;
4393 max_row = row;
4394 }
4395 }
4396
4397 if max_val < 1e-12 {
4398 return Err(RError::new(
4399 RErrorKind::Other,
4400 "lm() design matrix is singular or nearly singular — \
4401 check for collinear predictors or constant columns"
4402 .to_string(),
4403 ));
4404 }
4405
4406 if max_row != col {
4408 for k in 0..=n {
4409 let tmp = aug[[col, k]];
4410 aug[[col, k]] = aug[[max_row, k]];
4411 aug[[max_row, k]] = tmp;
4412 }
4413 }
4414
4415 for row in (col + 1)..n {
4417 let factor = aug[[row, col]] / aug[[col, col]];
4418 for k in col..=n {
4419 aug[[row, k]] -= factor * aug[[col, k]];
4420 }
4421 }
4422 }
4423
4424 let mut x = vec![0.0; n];
4426 for i in (0..n).rev() {
4427 let mut sum = aug[[i, n]];
4428 for j in (i + 1)..n {
4429 sum -= aug[[i, j]] * x[j];
4430 }
4431 x[i] = sum / aug[[i, i]];
4432 }
4433
4434 Ok(x)
4435}
4436
4437#[cfg(feature = "linalg")]
4444#[interpreter_builtin(name = "summary.lm", min_args = 1, namespace = "stats")]
4445fn interp_summary_lm(
4446 args: &[RValue],
4447 _named: &[(String, RValue)],
4448 context: &BuiltinContext,
4449) -> Result<RValue, RError> {
4450 let obj = args.first().ok_or_else(|| {
4451 RError::new(
4452 RErrorKind::Argument,
4453 "summary.lm() requires an lm object".to_string(),
4454 )
4455 })?;
4456
4457 let list = match obj {
4458 RValue::List(l) => l,
4459 _ => {
4460 return Err(RError::new(
4461 RErrorKind::Type,
4462 "summary.lm() requires an lm object (a list with class 'lm')".to_string(),
4463 ))
4464 }
4465 };
4466
4467 let coefs = list
4469 .values
4470 .iter()
4471 .find(|(n, _)| n.as_deref() == Some("coefficients"))
4472 .map(|(_, v)| v);
4473
4474 context.write("Call:\nlm(formula = ...)\n\n");
4475
4476 if let Some(RValue::Vector(rv)) = coefs {
4477 let values = rv.to_doubles();
4478 let names: Vec<String> = rv
4479 .get_attr("names")
4480 .and_then(|n| match n {
4481 RValue::Vector(nv) => match &nv.inner {
4482 Vector::Character(c) => {
4483 Some(c.iter().map(|s| s.clone().unwrap_or_default()).collect())
4484 }
4485 _ => None,
4486 },
4487 _ => None,
4488 })
4489 .unwrap_or_default();
4490
4491 context.write("Coefficients:\n");
4492 let max_name_len = names.iter().map(|n| n.len()).max().unwrap_or(0).max(8);
4493 context.write(&format!("{:>width$} Estimate\n", "", width = max_name_len));
4494 for (i, val) in values.iter().enumerate() {
4495 let name = names.get(i).map(|s| s.as_str()).unwrap_or("???");
4496 let estimate = val.map_or("NA".to_string(), |v| format!("{:.6}", v));
4497 context.write(&format!(
4498 "{:>width$} {}\n",
4499 name,
4500 estimate,
4501 width = max_name_len
4502 ));
4503 }
4504 }
4505
4506 let residuals = list
4508 .values
4509 .iter()
4510 .find(|(n, _)| n.as_deref() == Some("residuals"))
4511 .map(|(_, v)| v);
4512
4513 if let Some(RValue::Vector(rv)) = residuals {
4514 let vals: Vec<f64> = rv.to_doubles().into_iter().flatten().collect();
4515 if !vals.is_empty() {
4516 let mut sorted = vals.clone();
4517 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
4518 let min = sorted[0];
4519 let max = sorted[sorted.len() - 1];
4520 let median = if sorted.len().is_multiple_of(2) {
4521 (sorted[sorted.len() / 2 - 1] + sorted[sorted.len() / 2]) / 2.0
4522 } else {
4523 sorted[sorted.len() / 2]
4524 };
4525 context.write(&format!(
4526 "\nResiduals: Min = {:.4}, Median = {:.4}, Max = {:.4}\n",
4527 min, median, max
4528 ));
4529 }
4530 }
4531
4532 Ok(obj.clone())
4533}
4534
4535#[cfg(feature = "linalg")]
4542#[builtin(min_args = 1, namespace = "stats")]
4543fn builtin_coef(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4544 let obj = args.first().ok_or_else(|| {
4545 RError::new(
4546 RErrorKind::Argument,
4547 "coef() requires a model object".to_string(),
4548 )
4549 })?;
4550
4551 match obj {
4552 RValue::List(list) => {
4553 for (name, val) in &list.values {
4554 if name.as_deref() == Some("coefficients") {
4555 return Ok(val.clone());
4556 }
4557 }
4558 Err(RError::new(
4559 RErrorKind::Name,
4560 "object does not have a 'coefficients' component".to_string(),
4561 ))
4562 }
4563 _ => Err(RError::new(
4564 RErrorKind::Type,
4565 "coef() requires a list-like model object (e.g. result of lm())".to_string(),
4566 )),
4567 }
4568}
4569
4570#[builtin(name = "arrayInd", min_args = 2)]
4583fn builtin_array_ind(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4584 let indices = match args.first() {
4585 Some(RValue::Vector(rv)) => rv.to_doubles(),
4586 _ => {
4587 return Err(RError::new(
4588 RErrorKind::Argument,
4589 "'ind' must be a numeric vector".to_string(),
4590 ))
4591 }
4592 };
4593 let dims = match args.get(1) {
4594 Some(RValue::Vector(rv)) => rv.to_doubles(),
4595 _ => {
4596 return Err(RError::new(
4597 RErrorKind::Argument,
4598 "'.dim' must be a numeric vector".to_string(),
4599 ))
4600 }
4601 };
4602
4603 if dims.len() < 2 {
4604 return Err(RError::new(
4605 RErrorKind::Argument,
4606 "'.dim' must have at least 2 elements for matrix subscripts".to_string(),
4607 ));
4608 }
4609
4610 let dim_sizes: Vec<i64> = dims
4611 .iter()
4612 .map(|d| match d {
4613 Some(v) => Ok(*v as i64),
4614 None => Err(RError::new(
4615 RErrorKind::Argument,
4616 "NA in '.dim'".to_string(),
4617 )),
4618 })
4619 .collect::<Result<_, _>>()?;
4620
4621 let ndim = dims.len();
4622 let n = indices.len();
4623 let mut result: Vec<Option<i64>> = vec![None; n * ndim];
4625
4626 for (idx_pos, ind_val) in indices.iter().enumerate() {
4627 match ind_val {
4628 Some(v) => {
4629 let mut linear = *v as i64 - 1; for d in 0..ndim {
4631 let subscript = linear % dim_sizes[d];
4632 result[d * n + idx_pos] = Some(subscript + 1); linear /= dim_sizes[d];
4634 }
4635 }
4636 None => {
4637 for d in 0..ndim {
4638 result[d * n + idx_pos] = None;
4639 }
4640 }
4641 }
4642 }
4643
4644 let mut rv = RVector::from(Vector::Integer(result.into()));
4645 rv.set_attr(
4646 "dim".to_string(),
4647 RValue::vec(Vector::Integer(
4648 vec![
4649 Some(i64::try_from(n).unwrap_or(0)),
4650 Some(i64::try_from(ndim).unwrap_or(0)),
4651 ]
4652 .into(),
4653 )),
4654 );
4655
4656 Ok(RValue::Vector(rv))
4657}
4658
4659fn matrix_dims_and_data(args: &[RValue]) -> Option<(usize, usize, Vec<Option<f64>>)> {
4665 let v = match args.first()? {
4666 RValue::Vector(v) => v,
4667 RValue::List(list) => {
4668 let ncol = list.values.len();
4670 if ncol == 0 {
4671 return Some((0, 0, vec![]));
4672 }
4673 let nrow = list
4674 .values
4675 .first()
4676 .map(|(_, v)| match v {
4677 RValue::Vector(rv) => rv.len(),
4678 _ => 1,
4679 })
4680 .unwrap_or(0);
4681 let mut data = Vec::with_capacity(nrow * ncol);
4682 for (_, val) in &list.values {
4683 if let Some(v) = val.as_vector() {
4684 data.extend(v.to_doubles());
4685 } else {
4686 data.extend(std::iter::repeat_n(None, nrow));
4687 }
4688 }
4689 return Some((nrow, ncol, data));
4690 }
4691 _ => return None,
4692 };
4693 let dim = v.get_attr("dim")?;
4694 let dim_vec = dim.as_vector()?;
4695 let dims = dim_vec.to_integers();
4696 if dims.len() != 2 {
4697 return None;
4698 }
4699 let nrow = dims[0].unwrap_or(0) as usize;
4700 let ncol = dims[1].unwrap_or(0) as usize;
4701 Some((nrow, ncol, v.to_doubles()))
4702}
4703
4704#[builtin(name = "rowSums", min_args = 1)]
4711fn builtin_row_sums(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
4712 let (nrow, ncol, data) = matrix_dims_and_data(args).ok_or_else(|| {
4713 RError::new(
4714 RErrorKind::Argument,
4715 "'x' must be a matrix or data frame".to_string(),
4716 )
4717 })?;
4718 let na_rm = named
4719 .iter()
4720 .find(|(n, _)| n == "na.rm")
4721 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
4722 .unwrap_or(false);
4723 let mut result = Vec::with_capacity(nrow);
4724 for i in 0..nrow {
4725 let mut sum = 0.0;
4726 let mut has_na = false;
4727 for j in 0..ncol {
4728 match data.get(j * nrow + i).copied().flatten() {
4729 Some(v) => sum += v,
4730 None if !na_rm => {
4731 has_na = true;
4732 break;
4733 }
4734 _ => {}
4735 }
4736 }
4737 result.push(if has_na { None } else { Some(sum) });
4738 }
4739 Ok(RValue::vec(Vector::Double(result.into())))
4740}
4741
4742#[builtin(name = "colSums", min_args = 1)]
4749fn builtin_col_sums(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
4750 let (nrow, ncol, data) = matrix_dims_and_data(args).ok_or_else(|| {
4751 RError::new(
4752 RErrorKind::Argument,
4753 "'x' must be a matrix or data frame".to_string(),
4754 )
4755 })?;
4756 let na_rm = named
4757 .iter()
4758 .find(|(n, _)| n == "na.rm")
4759 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
4760 .unwrap_or(false);
4761 let mut result = Vec::with_capacity(ncol);
4762 for j in 0..ncol {
4763 let mut sum = 0.0;
4764 let mut has_na = false;
4765 for i in 0..nrow {
4766 match data.get(j * nrow + i).copied().flatten() {
4767 Some(v) => sum += v,
4768 None if !na_rm => {
4769 has_na = true;
4770 break;
4771 }
4772 _ => {}
4773 }
4774 }
4775 result.push(if has_na { None } else { Some(sum) });
4776 }
4777 Ok(RValue::vec(Vector::Double(result.into())))
4778}
4779
4780#[builtin(name = "rowMeans", min_args = 1)]
4787fn builtin_row_means(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
4788 let (nrow, ncol, data) = matrix_dims_and_data(args).ok_or_else(|| {
4789 RError::new(
4790 RErrorKind::Argument,
4791 "'x' must be a matrix or data frame".to_string(),
4792 )
4793 })?;
4794 let na_rm = named
4795 .iter()
4796 .find(|(n, _)| n == "na.rm")
4797 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
4798 .unwrap_or(false);
4799 let mut result = Vec::with_capacity(nrow);
4800 for i in 0..nrow {
4801 let mut sum = 0.0;
4802 let mut count = 0usize;
4803 let mut has_na = false;
4804 for j in 0..ncol {
4805 match data.get(j * nrow + i).copied().flatten() {
4806 Some(v) => {
4807 sum += v;
4808 count += 1;
4809 }
4810 None if !na_rm => {
4811 has_na = true;
4812 break;
4813 }
4814 _ => {}
4815 }
4816 }
4817 result.push(if has_na || count == 0 {
4818 None
4819 } else {
4820 Some(sum / count as f64)
4821 });
4822 }
4823 Ok(RValue::vec(Vector::Double(result.into())))
4824}
4825
4826#[builtin(name = "colMeans", min_args = 1)]
4833fn builtin_col_means(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
4834 let (nrow, ncol, data) = matrix_dims_and_data(args).ok_or_else(|| {
4835 RError::new(
4836 RErrorKind::Argument,
4837 "'x' must be a matrix or data frame".to_string(),
4838 )
4839 })?;
4840 let na_rm = named
4841 .iter()
4842 .find(|(n, _)| n == "na.rm")
4843 .and_then(|(_, v)| v.as_vector()?.as_logical_scalar())
4844 .unwrap_or(false);
4845 let mut result = Vec::with_capacity(ncol);
4846 for j in 0..ncol {
4847 let mut sum = 0.0;
4848 let mut count = 0usize;
4849 let mut has_na = false;
4850 for i in 0..nrow {
4851 match data.get(j * nrow + i).copied().flatten() {
4852 Some(v) => {
4853 sum += v;
4854 count += 1;
4855 }
4856 None if !na_rm => {
4857 has_na = true;
4858 break;
4859 }
4860 _ => {}
4861 }
4862 }
4863 result.push(if has_na || count == 0 {
4864 None
4865 } else {
4866 Some(sum / count as f64)
4867 });
4868 }
4869 Ok(RValue::vec(Vector::Double(result.into())))
4870}
4871
4872#[builtin(min_args = 1)]
4878fn builtin_droplevels(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
4879 match args.first() {
4880 Some(RValue::Vector(v)) => {
4881 let codes = v.inner.to_integers();
4882 let levels = match v.get_attr("levels") {
4883 Some(RValue::Vector(lv)) => lv.to_characters(),
4884 _ => return Ok(RValue::Vector(v.clone())),
4885 };
4886 let used: std::collections::BTreeSet<usize> = codes
4887 .iter()
4888 .filter_map(|c| c.and_then(|i| usize::try_from(i).ok()))
4889 .collect();
4890 let mut new_levels = Vec::new();
4891 let mut old_to_new = std::collections::HashMap::new();
4892 for (old_idx, level) in levels.iter().enumerate() {
4893 if used.contains(&(old_idx + 1)) {
4894 old_to_new.insert(old_idx + 1, new_levels.len() + 1);
4895 new_levels.push(level.clone());
4896 }
4897 }
4898 let new_codes: Vec<Option<i64>> = codes
4899 .iter()
4900 .map(|c| c.and_then(|i| old_to_new.get(&(i as usize)).map(|&new| new as i64)))
4901 .collect();
4902 let mut rv = RVector::from(Vector::Integer(new_codes.into()));
4903 rv.set_attr(
4904 "levels".to_string(),
4905 RValue::vec(Vector::Character(new_levels.into())),
4906 );
4907 rv.set_attr(
4908 "class".to_string(),
4909 RValue::vec(Vector::Character(vec![Some("factor".to_string())].into())),
4910 );
4911 Ok(RValue::Vector(rv))
4912 }
4913 _ => Ok(args.first().cloned().unwrap_or(RValue::Null)),
4914 }
4915}
4916
4917#[builtin(min_args = 3)]
4928fn builtin_sweep(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
4929 let (nrow, ncol, data) = matrix_dims_and_data(args).ok_or_else(|| {
4930 RError::new(
4931 RErrorKind::Argument,
4932 "'x' must be a matrix or data frame".to_string(),
4933 )
4934 })?;
4935
4936 let margin_val = named
4938 .iter()
4939 .find(|(n, _)| n == "MARGIN")
4940 .map(|(_, v)| v)
4941 .or_else(|| args.get(1));
4942 let margin = match margin_val {
4943 Some(RValue::Vector(v)) => v
4944 .as_double_scalar()
4945 .or_else(|| v.as_integer_scalar().map(|i| i as f64))
4946 .ok_or_else(|| {
4947 RError::new(
4948 RErrorKind::Argument,
4949 "MARGIN must be 1 (rows) or 2 (columns)".to_string(),
4950 )
4951 })? as usize,
4952 _ => {
4953 return Err(RError::new(
4954 RErrorKind::Argument,
4955 "MARGIN must be 1 (rows) or 2 (columns)".to_string(),
4956 ))
4957 }
4958 };
4959 if margin != 1 && margin != 2 {
4960 return Err(RError::new(
4961 RErrorKind::Argument,
4962 format!("MARGIN must be 1 (rows) or 2 (columns), got {}", margin),
4963 ));
4964 }
4965
4966 let stats_val = named
4968 .iter()
4969 .find(|(n, _)| n == "STATS")
4970 .map(|(_, v)| v)
4971 .or_else(|| args.get(2));
4972 let stats: Vec<f64> = match stats_val {
4973 Some(RValue::Vector(v)) => v
4974 .to_doubles()
4975 .into_iter()
4976 .map(|d| d.unwrap_or(f64::NAN))
4977 .collect(),
4978 _ => {
4979 return Err(RError::new(
4980 RErrorKind::Argument,
4981 "STATS must be a numeric vector".to_string(),
4982 ))
4983 }
4984 };
4985
4986 let expected_len = if margin == 1 { nrow } else { ncol };
4988 if stats.len() != expected_len {
4989 return Err(RError::new(
4990 RErrorKind::Argument,
4991 format!(
4992 "STATS has length {} but MARGIN {} has {} {}",
4993 stats.len(),
4994 margin,
4995 expected_len,
4996 if margin == 1 { "rows" } else { "columns" }
4997 ),
4998 ));
4999 }
5000
5001 let fun_val = named
5003 .iter()
5004 .find(|(n, _)| n == "FUN")
5005 .map(|(_, v)| v)
5006 .or_else(|| args.get(3));
5007 let fun_str = match fun_val {
5008 Some(RValue::Vector(v)) => v.as_character_scalar().unwrap_or_else(|| "-".to_string()),
5009 None => "-".to_string(),
5010 _ => "-".to_string(),
5011 };
5012
5013 let apply_fn: fn(f64, f64) -> f64 = match fun_str.as_str() {
5014 "-" => |a, b| a - b,
5015 "+" => |a, b| a + b,
5016 "*" => |a, b| a * b,
5017 "/" => |a, b| a / b,
5018 other => {
5019 return Err(RError::new(
5020 RErrorKind::Argument,
5021 format!(
5022 "FUN must be one of \"-\", \"+\", \"*\", \"/\", got {:?}",
5023 other
5024 ),
5025 ))
5026 }
5027 };
5028
5029 let mut result: Vec<Option<f64>> = Vec::with_capacity(nrow * ncol);
5031 for j in 0..ncol {
5032 for i in 0..nrow {
5033 let idx = j * nrow + i;
5034 let val = data[idx];
5035 let stat = if margin == 1 { stats[i] } else { stats[j] };
5036 result.push(val.map(|v| apply_fn(v, stat)));
5037 }
5038 }
5039
5040 let (row_names, col_names) = matrix_dimnames(args.first().unwrap_or(&RValue::Null));
5041 let mut rv = RVector::from(Vector::Double(result.into()));
5042 set_matrix_attrs(&mut rv, nrow, ncol, row_names, col_names)?;
5043 Ok(RValue::Vector(rv))
5044}
5045
5046#[builtin(min_args = 2)]
5059fn builtin_kronecker(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
5060 let fun_val = named
5062 .iter()
5063 .find(|(n, _)| n == "FUN")
5064 .map(|(_, v)| v)
5065 .or_else(|| args.get(2));
5066 let fun_str = match fun_val {
5067 Some(RValue::Vector(v)) => v.as_character_scalar().unwrap_or_else(|| "*".to_string()),
5068 None => "*".to_string(),
5069 _ => "*".to_string(),
5070 };
5071
5072 let apply_fn: fn(f64, f64) -> f64 = match fun_str.as_str() {
5073 "*" => |a, b| a * b,
5074 "+" => |a, b| a + b,
5075 "-" => |a, b| a - b,
5076 "/" => |a, b| a / b,
5077 other => {
5078 return Err(RError::new(
5079 RErrorKind::Argument,
5080 format!(
5081 "FUN must be one of \"*\", \"+\", \"-\", \"/\", got {:?}",
5082 other
5083 ),
5084 ))
5085 }
5086 };
5087
5088 eval_kronecker_with_fn(
5089 args.first().unwrap_or(&RValue::Null),
5090 args.get(1).unwrap_or(&RValue::Null),
5091 apply_fn,
5092 )
5093}
5094
5095fn kronecker_dims_and_data(val: &RValue) -> Result<(usize, usize, Vec<Option<f64>>), RError> {
5097 match val {
5098 RValue::Vector(v) => {
5099 let data = v.to_doubles();
5100 match v.get_attr("dim") {
5101 Some(dim_val) => {
5102 let dim_vec = dim_val.as_vector().ok_or_else(|| {
5103 RError::new(RErrorKind::Type, "invalid dim attribute".to_string())
5104 })?;
5105 let dims = dim_vec.to_integers();
5106 if dims.len() != 2 {
5107 return Err(RError::new(
5108 RErrorKind::Argument,
5109 "kronecker() requires matrix arguments (2-d dim)".to_string(),
5110 ));
5111 }
5112 let nrow = usize::try_from(dims[0].unwrap_or(0))?;
5113 let ncol = usize::try_from(dims[1].unwrap_or(0))?;
5114 Ok((nrow, ncol, data))
5115 }
5116 None => {
5118 let n = data.len();
5119 Ok((n, 1, data))
5120 }
5121 }
5122 }
5123 _ => Err(RError::new(
5124 RErrorKind::Argument,
5125 "kronecker() requires numeric matrix/vector arguments".to_string(),
5126 )),
5127 }
5128}
5129
5130fn eval_kronecker_with_fn(
5131 a: &RValue,
5132 b: &RValue,
5133 fun: fn(f64, f64) -> f64,
5134) -> Result<RValue, RError> {
5135 let (a_nrow, a_ncol, a_data) = kronecker_dims_and_data(a)?;
5136 let (b_nrow, b_ncol, b_data) = kronecker_dims_and_data(b)?;
5137
5138 let out_nrow = a_nrow * b_nrow;
5139 let out_ncol = a_ncol * b_ncol;
5140 let mut result: Vec<Option<f64>> = Vec::with_capacity(out_nrow * out_ncol);
5141
5142 for j_a in 0..a_ncol {
5146 for j_b in 0..b_ncol {
5147 for i_a in 0..a_nrow {
5148 for i_b in 0..b_nrow {
5149 let a_val = a_data[j_a * a_nrow + i_a];
5150 let b_val = b_data[j_b * b_nrow + i_b];
5151 result.push(match (a_val, b_val) {
5152 (Some(av), Some(bv)) => Some(fun(av, bv)),
5153 _ => None,
5154 });
5155 }
5156 }
5157 }
5158 }
5159
5160 let mut rv = RVector::from(Vector::Double(result.into()));
5161 set_matrix_attrs(&mut rv, out_nrow, out_ncol, None, None)?;
5162 Ok(RValue::Vector(rv))
5163}
5164
5165pub fn eval_kronecker(left: &RValue, right: &RValue) -> Result<RValue, RError> {
5169 eval_kronecker_with_fn(left, right, |a, b| a * b)
5170}
5171
5172fn matrix_dims(arg: &RValue) -> Option<(usize, usize)> {
5178 match arg {
5179 RValue::Vector(v) => {
5180 let dim = v.get_attr("dim")?;
5181 let dim_vec = dim.as_vector()?;
5182 let dims = dim_vec.to_integers();
5183 if dims.len() == 2 {
5184 Some((
5185 usize::try_from(dims[0]?).ok()?,
5186 usize::try_from(dims[1]?).ok()?,
5187 ))
5188 } else {
5189 None
5190 }
5191 }
5192 RValue::List(l) => {
5193 if let Some(dim) = l.get_attr("dim") {
5194 let dim_vec = dim.as_vector()?;
5195 let dims = dim_vec.to_integers();
5196 if dims.len() == 2 {
5197 return Some((
5198 usize::try_from(dims[0]?).ok()?,
5199 usize::try_from(dims[1]?).ok()?,
5200 ));
5201 }
5202 }
5203 let ncol = l.values.len();
5205 let nrow = l
5206 .values
5207 .first()
5208 .map(|(_, v)| match v {
5209 RValue::Vector(rv) => rv.len(),
5210 _ => 1,
5211 })
5212 .unwrap_or(0);
5213 Some((nrow, ncol))
5214 }
5215 _ => None,
5216 }
5217}
5218
5219#[builtin(min_args = 1)]
5225fn builtin_row(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
5226 let (nrow, ncol) = matrix_dims(args.first().unwrap_or(&RValue::Null)).ok_or_else(|| {
5227 RError::new(
5228 RErrorKind::Argument,
5229 "a matrix-like object is required as argument to 'row'".to_string(),
5230 )
5231 })?;
5232 let mut data: Vec<Option<i64>> = Vec::with_capacity(nrow * ncol);
5234 for _j in 0..ncol {
5235 for i in 0..nrow {
5236 data.push(Some(i64::try_from(i)? + 1));
5237 }
5238 }
5239 let mut rv = RVector::from(Vector::Integer(data.into()));
5240 rv.set_attr(
5241 "dim".to_string(),
5242 RValue::vec(Vector::Integer(
5243 vec![Some(i64::try_from(nrow)?), Some(i64::try_from(ncol)?)].into(),
5244 )),
5245 );
5246 Ok(RValue::Vector(rv))
5247}
5248
5249#[builtin(min_args = 1)]
5255fn builtin_col(args: &[RValue], _: &[(String, RValue)]) -> Result<RValue, RError> {
5256 let (nrow, ncol) = matrix_dims(args.first().unwrap_or(&RValue::Null)).ok_or_else(|| {
5257 RError::new(
5258 RErrorKind::Argument,
5259 "a matrix-like object is required as argument to 'col'".to_string(),
5260 )
5261 })?;
5262 let mut data: Vec<Option<i64>> = Vec::with_capacity(nrow * ncol);
5263 for j in 0..ncol {
5264 for _i in 0..nrow {
5265 data.push(Some(i64::try_from(j)? + 1));
5266 }
5267 }
5268 let mut rv = RVector::from(Vector::Integer(data.into()));
5269 rv.set_attr(
5270 "dim".to_string(),
5271 RValue::vec(Vector::Integer(
5272 vec![Some(i64::try_from(nrow)?), Some(i64::try_from(ncol)?)].into(),
5273 )),
5274 );
5275 Ok(RValue::Vector(rv))
5276}
5277
5278#[builtin(name = "slice.index", min_args = 2)]
5288fn builtin_slice_index(args: &[RValue], named: &[(String, RValue)]) -> Result<RValue, RError> {
5289 let x = args
5290 .first()
5291 .ok_or_else(|| RError::new(RErrorKind::Argument, "'x' must be an array".to_string()))?;
5292
5293 let margin_val = named
5294 .iter()
5295 .find(|(n, _)| n == "MARGIN")
5296 .map(|(_, v)| v)
5297 .or(args.get(1))
5298 .ok_or_else(|| {
5299 RError::new(
5300 RErrorKind::Argument,
5301 "'MARGIN' argument is required".to_string(),
5302 )
5303 })?;
5304
5305 let margin = margin_val
5306 .as_vector()
5307 .and_then(|v| v.as_integer_scalar())
5308 .ok_or_else(|| {
5309 RError::new(
5310 RErrorKind::Argument,
5311 "'MARGIN' must be an integer scalar".to_string(),
5312 )
5313 })?;
5314
5315 let dim_attr = match x {
5316 RValue::Vector(v) => v.get_attr("dim"),
5317 RValue::List(l) => l.get_attr("dim"),
5318 _ => None,
5319 };
5320
5321 let dims: Vec<i64> = dim_attr
5322 .and_then(|d| d.as_vector())
5323 .map(|v| {
5324 v.to_integers()
5325 .into_iter()
5326 .map(|d| d.unwrap_or(0))
5327 .collect()
5328 })
5329 .ok_or_else(|| {
5330 RError::new(
5331 RErrorKind::Argument,
5332 "'x' must be an array (needs dim attribute)".to_string(),
5333 )
5334 })?;
5335
5336 let ndim = dims.len();
5337 let margin_idx = usize::try_from(margin - 1).map_err(|_| {
5338 RError::new(
5339 RErrorKind::Argument,
5340 format!("'MARGIN' = {} is out of range [1, {}]", margin, ndim),
5341 )
5342 })?;
5343
5344 if margin_idx >= ndim {
5345 return Err(RError::new(
5346 RErrorKind::Argument,
5347 format!("'MARGIN' = {} is out of range [1, {}]", margin, ndim),
5348 ));
5349 }
5350
5351 let total: usize = dims
5352 .iter()
5353 .map(|&d| usize::try_from(d).unwrap_or(0))
5354 .product();
5355 let mut data: Vec<Option<i64>> = Vec::with_capacity(total);
5356
5357 let stride: usize = dims[..margin_idx]
5359 .iter()
5360 .map(|&d| usize::try_from(d).unwrap_or(0))
5361 .product();
5362 let dim_size = usize::try_from(dims[margin_idx]).unwrap_or(0);
5363
5364 for linear in 0..total {
5365 let idx_along_margin = (linear / stride) % dim_size;
5367 data.push(Some(i64::try_from(idx_along_margin)? + 1));
5368 }
5369
5370 let dim_values: Vec<Option<i64>> = dims.into_iter().map(Some).collect();
5371 let mut rv = RVector::from(Vector::Integer(data.into()));
5372 rv.set_attr(
5373 "dim".to_string(),
5374 RValue::vec(Vector::Integer(dim_values.into())),
5375 );
5376 Ok(RValue::Vector(rv))
5377}
5378
5379