strafe_testing/
r.rs

1// "Whatever you do, work at it with all your heart, as working for the Lord,
2// not for human masters, since you know that you will receive an inheritance
3// from the Lord as a reward. It is the Lord Christ you are serving."
4// (Col 3:23-24)
5
6use std::{
7    fmt::{Debug, Display, Formatter, Result as FmtResult},
8    ops::Add,
9    process::{Command, Stdio},
10    str::FromStr,
11};
12
13use approx::assert_relative_eq;
14use nalgebra::{DMatrix, Dim, IsContiguous, Matrix, Storage};
15use num::{complex::Complex64, Float, ToPrimitive};
16
17#[macro_export]
18macro_rules! r_assert_relative_equal {
19    ($lhs:expr, $rhs:expr, $max_relative:expr) => {{
20        $crate::r::r_assert_relative_equal_($lhs, $rhs, $max_relative)
21    }};
22    ($lhs:expr, $rhs:expr) => {{
23        r_assert_relative_equal!($lhs, $rhs, 1e-3)
24    }};
25}
26
27pub fn r_assert_relative_equal_(mut lhs: f64, mut rhs: f64, max_relative: f64) {
28    if lhs < 1e-30 {
29        lhs = 0.0;
30    }
31    if rhs < 1e-30 {
32        rhs = 0.0;
33    }
34    if lhs.is_finite() && rhs.is_finite() {
35        if (lhs - rhs).abs() / lhs > max_relative && (lhs - rhs).abs() / rhs > max_relative {
36            panic!(
37                "Test Failed: r_assert_relative_equal\n\n\tleft          : {}\n\tright         : {}\n\tdiff          : {:e}\n\tmax_relative  : {:e}\n\n",
38                lhs, rhs, (lhs - rhs).abs(), max_relative
39            )
40        }
41    } else if lhs.is_nan() && rhs.is_nan()
42        // Sometimes Rust can calculate things that R cannot
43        || lhs.is_nan() && !rhs.is_nan()
44    {
45    } else if (lhs.is_finite() && !rhs.is_finite())
46        || (!lhs.is_finite() && rhs.is_finite())
47        || (lhs.is_sign_negative() && rhs.is_sign_positive())
48        || (lhs.is_sign_positive() && rhs.is_sign_negative())
49    {
50        panic!(
51            "Test Failed: r_assert_relative_equal\n\n\tleft  : {}\n\tright : {}\n\n",
52            lhs, rhs
53        )
54    }
55}
56
57#[macro_export]
58macro_rules! r_assert_relative_equal_result {
59    ($lhs:expr, $rhs:expr, $max_relative:expr) => {{
60        $crate::r::r_assert_relative_equal_result_(
61            $lhs,
62            Ok(num_traits::ToPrimitive::to_f64($rhs).unwrap()),
63            $max_relative,
64        )
65    }};
66    ($lhs:expr, $rhs:expr) => {{
67        r_assert_relative_equal_result!($lhs, $rhs, 1e-3)
68    }};
69}
70
71pub fn r_assert_relative_equal_result_(
72    lhs: Result<f64, Box<dyn std::error::Error>>,
73    rhs: Result<f64, Box<dyn std::error::Error>>,
74    max_relative: f64,
75) {
76    match (lhs, rhs) {
77        (Ok(o1), Ok(o2)) => {
78            if !(o1.is_nan() && o2.is_nan()) {
79                assert_relative_eq!(o1, o2, max_relative = max_relative)
80            }
81        }
82        (Err(e), Ok(o)) => {
83            if !o.is_nan() {
84                panic!("Test Failed: r_assert_relative_equal_result\n\n\tleft  : Err({})\n\tright : Ok({})\n\n", e, o)
85            }
86        }
87        r => panic!("Unexpected: {:?}", r),
88    }
89}
90
91#[derive(Copy, Clone, Debug)]
92pub enum RTesterError {
93    NotFinished,
94}
95
96impl Display for RTesterError {
97    fn fmt(&self, f: &mut Formatter) -> FmtResult {
98        match self {
99            RTesterError::NotFinished => write!(f, "R Tester stopped before finishing"),
100        }
101    }
102}
103
104impl std::error::Error for RTesterError {}
105
106#[derive(Clone, Debug)]
107pub struct RTester {
108    seed: Option<RString>,
109    rand_type: Option<RString>,
110    libraries: RString,
111    script: RString,
112    display: RString,
113    done: RString,
114}
115
116#[derive(Clone, Debug)]
117pub struct RString(String);
118
119impl Display for RString {
120    fn fmt(&self, f: &mut Formatter) -> FmtResult {
121        write!(f, "{}", self.0)
122    }
123}
124
125impl Add for RString {
126    type Output = Self;
127
128    fn add(self, rhs: Self) -> Self::Output {
129        Self(self.0 + rhs.0.as_str())
130    }
131}
132
133impl FromStr for RString {
134    type Err = ();
135
136    fn from_str(s: &str) -> Result<Self, Self::Err> {
137        Ok(Self(s.to_string()))
138    }
139}
140
141impl RString {
142    pub fn from_string(string: String) -> Self {
143        Self(string)
144    }
145
146    pub fn empty() -> Self {
147        Self(String::new())
148    }
149
150    pub fn push(self, string: &str) -> Self {
151        Self(self.0 + string)
152    }
153
154    pub fn as_f64(&self) -> Result<f64, Box<dyn std::error::Error>> {
155        let string = self.0.as_str();
156
157        let string = if string.contains(']') {
158            string
159                .trim()
160                .split("] ")
161                .last()
162                .unwrap()
163                .replace(['<', '>'], "")
164        } else {
165            string.replace(['<', '>'], "")
166        };
167        match string.as_str() {
168            "Inf" => Ok(f64::infinity()),
169            "-Inf" => Ok(f64::NEG_INFINITY),
170            "NaN" | "NA" => Ok(f64::nan()),
171            _ => Ok(string.parse()?),
172        }
173    }
174
175    pub fn as_complex64(&self) -> Result<Complex64, Box<dyn std::error::Error>> {
176        let string = self.0.as_str();
177
178        let string = if string.contains(']') {
179            string
180                .trim()
181                .split("] ")
182                .last()
183                .unwrap()
184                .replace(['<', '>'], "")
185        } else {
186            string.replace(['<', '>'], "")
187        }
188        .replace('i', "");
189
190        let split_index = string
191            .rfind('+')
192            .filter(|i| *i != 0)
193            .unwrap_or(string.rfind('-').filter(|i| *i != 0).unwrap_or(0));
194        let split_string = string.split_at(split_index);
195
196        let real = RString::from_str(split_string.0).unwrap().as_f64()?;
197        let imaginary = RString::from_str(split_string.1).unwrap().as_f64()?;
198
199        Ok(Complex64::new(real, imaginary))
200    }
201
202    pub fn as_complex64_vec(&self) -> Result<Vec<Complex64>, Box<dyn std::error::Error>> {
203        let string = self.0.as_str();
204
205        Ok(string
206            .split_whitespace()
207            .filter(|i| !i.contains('['))
208            .filter_map(|s| RString::from_str(s).unwrap().as_complex64().ok())
209            .collect())
210    }
211
212    pub fn as_f64_vec(&self) -> Result<Vec<f64>, Box<dyn std::error::Error>> {
213        let string = self.0.as_str();
214
215        Ok(string
216            .split_whitespace()
217            .filter_map(|s| RString::from_str(s).unwrap().as_f64().ok())
218            .collect())
219    }
220
221    pub fn as_f64_matrix(&self) -> Result<DMatrix<f64>, Box<dyn std::error::Error>> {
222        let string = self.0.as_str();
223        let mut num_cols = 0;
224        string.lines().take(1).for_each(|s| {
225            num_cols = s.split_ascii_whitespace().count();
226        });
227        let num_rows = string.lines().skip(1).count();
228        let mut ret = DMatrix::from_iterator(
229            num_rows,
230            num_cols,
231            vec![0.0; num_rows * num_cols].into_iter(),
232        );
233        for (row, line) in string.lines().skip(1).enumerate() {
234            for (col, val) in line.split_ascii_whitespace().skip(1).enumerate() {
235                if let Some(x) = ret.get_mut((row, col)) {
236                    *x = RString::from_str(val.trim()).unwrap().as_f64().unwrap();
237                }
238            }
239        }
240
241        Ok(ret)
242    }
243
244    pub fn as_bool(&self) -> Result<bool, Box<String>> {
245        let str = self.0.as_str();
246
247        if str.contains("TRUE") {
248            Ok(true)
249        } else if str.contains("FALSE") {
250            Ok(false)
251        } else {
252            Err(Box::new(format!("{str} is not a bool")))
253        }
254    }
255
256    pub fn as_lm_regression(&self) -> LMRegression {
257        let summary = self.0.as_str();
258
259        let mut coefficient_lines = Vec::new();
260        let mut r_squared_line = String::new();
261        let mut significance_line = String::new();
262
263        for line in summary
264            .trim()
265            .split('\n')
266            .skip_while(|line| !line.contains("Coefficients:"))
267            .skip(2)
268            .take_while(|line| !line.contains("---") && !line.is_empty())
269        {
270            coefficient_lines.push(line);
271        }
272
273        for line in summary.trim().split('\n') {
274            if line.starts_with("Multiple R-squared") {
275                r_squared_line = line.to_string();
276            }
277            if line.starts_with("F-statistic") {
278                significance_line = line.to_string();
279            }
280        }
281
282        let coefs = lm_summary_coefficients(&coefficient_lines);
283        let r_squared = r_r_squared(&r_squared_line);
284        let significance = r_significance(&significance_line);
285
286        LMRegression {
287            coefs: coefs
288                .into_iter()
289                .map(|(estimate, std_error, t, p)| LMCoefs {
290                    estimate,
291                    std_error,
292                    t,
293                    p,
294                })
295                .collect(),
296            r_squared,
297            f: significance.0,
298            p: significance.1,
299        }
300    }
301
302    pub fn as_rlm_regression(&self) -> RLMRegression {
303        let summary = self.0.as_str();
304
305        let mut coefficient_lines = Vec::new();
306
307        let mut on_coefficients = false;
308        for line in summary.trim().split('\n') {
309            if line.contains("Coefficients:") {
310                on_coefficients = true;
311            } else if line.contains("---") || line.is_empty() {
312                on_coefficients = false;
313            } else if on_coefficients {
314                coefficient_lines.push(line);
315            }
316        }
317
318        let coefs = rlm_summary_coefficients(&coefficient_lines);
319
320        RLMRegression {
321            coefs: coefs
322                .into_iter()
323                .map(|(estimate, std_error, t)| RLMCoefs {
324                    estimate,
325                    std_error,
326                    t,
327                })
328                .collect(),
329        }
330    }
331
332    pub fn as_glm_regression(&self) -> GLMRegression {
333        let summary = self.0.as_str();
334
335        let mut coefficient_lines = Vec::new();
336        let mut null_dev_line = String::new();
337        let mut resid_dev_line = String::new();
338        let mut aic_line = String::new();
339
340        let mut on_coefficients = false;
341        for line in summary.trim().split('\n') {
342            if line.contains("Estimate") {
343                on_coefficients = true;
344            } else if line.is_empty() {
345                on_coefficients = false;
346            } else if on_coefficients {
347                coefficient_lines.push(line);
348            }
349        }
350
351        for line in summary.trim().split('\n') {
352            if line.trim().starts_with("Null deviance") {
353                null_dev_line = line.to_string();
354            }
355            if line.starts_with("Residual deviance") {
356                resid_dev_line = line.to_string();
357            }
358            if line.starts_with("AIC") {
359                aic_line = line.to_string();
360            }
361        }
362
363        let coefs = lm_summary_coefficients(&coefficient_lines);
364        let null_dev = r_null_deviance(&null_dev_line);
365        let resid_dev = r_residual_deviance(&resid_dev_line);
366        let aic = r_aic(&aic_line);
367
368        GLMRegression {
369            coefs: coefs
370                .into_iter()
371                .map(|(estimate, std_error, z, p)| GLMCoefs {
372                    estimate,
373                    std_error,
374                    z,
375                    p,
376                })
377                .collect(),
378            null_dev,
379            resid_dev,
380            aic,
381        }
382    }
383
384    pub fn as_r_fit(&self) -> RFitRegression {
385        let summary = self.0.as_str();
386
387        let mut coefficient_lines = Vec::new();
388        let mut r_squared_line = String::new();
389        let mut significance_line = String::new();
390        let mut wald_line = String::new();
391
392        for line in summary
393            .trim()
394            .split('\n')
395            .skip_while(|line| !line.contains("Coefficients:"))
396            .skip(2)
397            .take_while(|line| !line.contains("---") && !line.is_empty())
398        {
399            coefficient_lines.push(line);
400        }
401
402        for line in summary.trim().split('\n') {
403            if line.starts_with("Multiple R-squared (Robust)") {
404                r_squared_line = line.to_string();
405            }
406            if line.starts_with("Reduction in Dispersion") {
407                significance_line = line.to_string();
408            }
409            if line.starts_with("Overall Wald Test") {
410                wald_line = line.to_string();
411            }
412        }
413
414        let coefs = lm_summary_coefficients(&coefficient_lines);
415        let r_squared_robust = r_r_squared(&r_squared_line);
416        let red_dispersion = r_red_dispersion(&significance_line);
417        let wald_test = r_wald_test(&wald_line);
418
419        RFitRegression {
420            coefs: coefs
421                .into_iter()
422                .map(|(estimate, std_error, t, p)| LMCoefs {
423                    estimate,
424                    std_error,
425                    t,
426                    p,
427                })
428                .collect(),
429            r_squared_robust,
430            red_disp_test: red_dispersion.0,
431            disp_p: red_dispersion.1,
432            wald_test: wald_test.0,
433            wald_p: wald_test.1,
434        }
435    }
436
437    pub fn as_one_way_r_fit(&self) -> Vec<OneWayRFit> {
438        let summary = self.0.as_str();
439
440        let mut ret = Vec::new();
441        for line in summary
442            .trim()
443            .split('\n')
444            .skip_while(|line| !line.contains("Estimate"))
445            .skip(1)
446        {
447            let split = line.split_whitespace().collect::<Vec<_>>();
448            ret.push(OneWayRFit {
449                i: split[1].to_string(),
450                j: split[2].to_string(),
451                estimate: RString::from_str(split[3]).unwrap().as_f64().unwrap(),
452                std_err: RString::from_str(split[4]).unwrap().as_f64().unwrap(),
453                confidence_interval: (
454                    RString::from_str(split[5]).unwrap().as_f64().unwrap(),
455                    RString::from_str(split[6]).unwrap().as_f64().unwrap(),
456                ),
457            });
458        }
459
460        ret
461    }
462
463    pub fn as_raov(&self) -> Vec<RAOV> {
464        let summary = self.0.as_str();
465
466        let mut ret = Vec::new();
467        for line in summary
468            .trim()
469            .split('\n')
470            .skip_while(|line| !line.contains("Mean RD"))
471            .skip(1)
472        {
473            let mut line = line.to_string();
474            for i in 1..=20 {
475                line = line.replace(&format!("x[, {i}]"), &format!("x[,{i}]"));
476            }
477            let split = line.split_whitespace().collect::<Vec<_>>();
478            ret.push(RAOV {
479                name: split[0].to_string(),
480                df: RString::from_str(split[1]).unwrap().as_f64().unwrap(),
481                rd: RString::from_str(split[2]).unwrap().as_f64().unwrap(),
482                mean_rd: RString::from_str(split[3]).unwrap().as_f64().unwrap(),
483                f: RString::from_str(split[4]).unwrap().as_f64().unwrap(),
484                p: RString::from_str(split[5]).unwrap().as_f64().unwrap(),
485            });
486        }
487
488        ret
489    }
490
491    pub fn from_f64_slice(v: &[f64]) -> Self {
492        let length = v.len();
493
494        let mut ret = "c(".to_string();
495
496        for (i, v_i) in v.iter().take(length).enumerate() {
497            ret += &v_i.to_string();
498            if i < length - 1 {
499                ret += ", "
500            }
501        }
502
503        ret += ")";
504        Self(ret)
505    }
506
507    pub fn from_complex64_slice(v: &[Complex64]) -> Self {
508        let length = v.len();
509
510        let mut ret = "c(".to_string();
511
512        for (i, v_i) in v.iter().take(length).enumerate() {
513            ret += &v_i.re.to_string();
514            ret += "+";
515            ret += &v_i.re.to_string();
516            ret += "i";
517
518            if i < length - 1 {
519                ret += ", "
520            }
521        }
522
523        ret += ")";
524        Self(ret)
525    }
526
527    pub fn from_f64_matrix<R, C, S>(m: &Matrix<f64, R, C, S>) -> Self
528    where
529        R: Dim,
530        C: Dim,
531        S: Storage<f64, R, C> + IsContiguous,
532    {
533        let (row_size, col_size) = m.shape();
534        let slice_string = RString::from_f64_slice(m.as_slice());
535        let mut ret = "matrix(".to_string();
536        ret += &slice_string.to_string();
537        ret += &format!(", nrow={}, ncol={})", row_size, col_size);
538        Self(ret)
539    }
540
541    pub fn from_bool(b: bool) -> Self {
542        Self(match b {
543            true => "TRUE".to_string(),
544            false => "FALSE".to_string(),
545        })
546    }
547
548    pub fn from_f64<F: ToPrimitive + std::fmt::Debug>(num: F) -> Self {
549        let num = num.to_f64().unwrap_or(f64::nan());
550        Self(if num.is_infinite() && num.is_sign_positive() {
551            "Inf".to_string()
552        } else if num.is_infinite() && num.is_sign_negative() {
553            "-Inf".to_string()
554        } else if num.is_nan() {
555            "NaN".to_string()
556        } else {
557            format!("{}", num)
558        })
559    }
560
561    pub fn from_library_name(lib_name: &str) -> Self {
562        Self(String::from("library(\"") + lib_name + "\")")
563    }
564}
565
566pub fn print_mat<R, C, S>(x: &Matrix<f64, R, C, S>)
567where
568    R: Dim,
569    C: Dim,
570    S: Storage<f64, R, C>,
571{
572    print!("     ");
573    for i in 0..x.ncols() {
574        print!("{:4} ", i);
575    }
576    println!();
577    for i in 0..x.nrows() {
578        print!("{:4} ", i);
579        for j in 0..x.ncols() {
580            print!("{:4} ", x[(i, j)]);
581        }
582        println!();
583    }
584}
585
586const RSCRIPT: Option<&str> = option_env!("Rscript");
587
588impl Default for RTester {
589    fn default() -> Self {
590        Self {
591            seed: None,
592            rand_type: None,
593            libraries: RString::empty(),
594            script: RString::empty(),
595            display: RString::empty(),
596            done: RString::from_str("R_TESTER_DONE").unwrap(),
597        }
598    }
599}
600
601impl RTester {
602    pub fn new() -> Self {
603        Self::default()
604    }
605
606    pub fn add_library(mut self, library: &RString) -> Self {
607        self.libraries.0 += library.0.as_str();
608        self.libraries.0 += ";";
609        self
610    }
611
612    pub fn set_seed(mut self, seed: u16) -> Self {
613        self.seed = Some(RString::from_f64(seed));
614        self
615    }
616
617    pub fn set_rand_type(mut self, rand_type: &str) -> Self {
618        self.rand_type = Some(RString::from_str(rand_type).unwrap());
619        self
620    }
621
622    pub fn set_script(mut self, script: &RString) -> Self {
623        self.script = script.clone();
624        self
625    }
626
627    pub fn set_display(mut self, display: &RString) -> Self {
628        self.display = display.clone();
629        self
630    }
631
632    pub fn run(&self) -> Result<RString, Box<dyn std::error::Error>> {
633        let seed_piece = match (self.seed.clone(), self.rand_type.clone()) {
634            (Some(seed), Some(rand_type)) => {
635                RString::from_string(format!("set.seed({}, '{}');", seed, rand_type))
636            }
637            (None, Some(rand_type)) => {
638                RString::from_string(format!("set.seed(NULL, '{}');", rand_type))
639            }
640            (Some(seed), None) => {
641                RString::from_string(format!("set.seed({}, 'Marsaglia-Multicarry');", seed))
642            }
643            (None, None) => RString::from_str("").unwrap(),
644        };
645
646        let script = format!(
647            "options(warn=-1, show.error.messages=FALSE, digits=14, width=1e4, scipen=1);{}{}{}print({}, digits=14, dig.tst=14);cat('{}');",
648            self.libraries.0, seed_piece.0, self.script.0, self.display.0, self.done.0,
649        );
650        let mut ret_string = String::from_utf8(
651            Command::new(
652                RSCRIPT
653                    .ok_or("")
654                    .expect("Environment variable `Rscript` not set"),
655            )
656            .args(["-e", &script])
657            .stdout(Stdio::piped())
658            .spawn()?
659            .wait_with_output()?
660            .stdout,
661        )?;
662
663        let done_message = (0..self.done.0.len())
664            .filter_map(|_| ret_string.pop())
665            .collect::<String>()
666            .chars()
667            .rev()
668            .collect::<String>();
669        if done_message == self.done.0 {
670            Ok(RString(ret_string))
671        } else {
672            let error_script = format!(
673                "options(digits=14, width=1e4);{}{}{}print({});cat('{}');",
674                self.libraries.0, seed_piece.0, self.script.0, self.display.0, self.done.0,
675            );
676            let ret = Command::new(
677                RSCRIPT
678                    .ok_or("")
679                    .expect("Environment variable `Rscript` not set"),
680            )
681            .args(["-e", &error_script])
682            .stdout(Stdio::piped())
683            .stderr(Stdio::piped())
684            .spawn()?
685            .wait_with_output()?;
686            let output = String::from_utf8(ret.stdout)?;
687            let error = String::from_utf8(ret.stderr)?;
688            println!("Input:\n{}", script);
689            println!("Output:\n{}", output);
690            println!("Error:\n{}", error);
691            Err(Box::new(RTesterError::NotFinished))
692        }
693    }
694}
695
696fn lm_summary_coefficients(lines: &[&str]) -> Vec<(f64, f64, f64, f64)> {
697    lines
698        .iter()
699        .take_while(|s| !s.trim().is_empty())
700        .filter_map(|line| {
701            let nums = line
702                .split_whitespace()
703                .filter_map(|s| RString::from_str(s).unwrap().as_f64().ok())
704                .collect::<Vec<_>>();
705            if nums.len() == 4 {
706                Some((nums[0], nums[1], nums[2], nums[3]))
707            } else {
708                None
709            }
710        })
711        .collect()
712}
713
714fn rlm_summary_coefficients(lines: &[&str]) -> Vec<(f64, f64, f64)> {
715    lines
716        .iter()
717        .skip(1)
718        .filter(|l| l.split_ascii_whitespace().count() == 4)
719        .map(|line| {
720            let nums = line
721                .split_whitespace()
722                .filter_map(|s| RString::from_str(s).unwrap().as_f64().ok())
723                .collect::<Vec<_>>();
724            (nums[0], nums[1], nums[2])
725        })
726        .collect()
727}
728
729fn r_r_squared(line: &str) -> f64 {
730    RString::from_str(line.split_whitespace().last().unwrap_or("0.0"))
731        .unwrap()
732        .as_f64()
733        .expect("R^2 error")
734}
735
736fn r_significance(line: &str) -> (f64, f64) {
737    let split_line = line.split_whitespace().collect::<Vec<_>>();
738    (
739        RString::from_str(split_line[1])
740            .unwrap()
741            .as_f64()
742            .expect("F-statistic error"),
743        RString::from_str(split_line.last().unwrap())
744            .unwrap()
745            .as_f64()
746            .expect("P-value error"),
747    )
748}
749
750fn r_null_deviance(line: &str) -> f64 {
751    let split_line = line.split_whitespace().collect::<Vec<_>>();
752    RString::from_str(split_line[2])
753        .unwrap()
754        .as_f64()
755        .expect("Null deviance error")
756}
757
758fn r_residual_deviance(line: &str) -> f64 {
759    let split_line = line.split_whitespace().collect::<Vec<_>>();
760    RString::from_str(split_line[2])
761        .unwrap()
762        .as_f64()
763        .expect("Residual deviance error")
764}
765
766fn r_aic(line: &str) -> f64 {
767    let split_line = line.split_whitespace().collect::<Vec<_>>();
768    RString::from_str(split_line[1])
769        .unwrap()
770        .as_f64()
771        .expect("AIC deviance error")
772}
773
774fn r_red_dispersion(line: &str) -> (f64, f64) {
775    let split_line = line.split_whitespace().collect::<Vec<_>>();
776    (
777        RString::from_str(split_line.get(4).unwrap_or(&"0.0"))
778            .unwrap()
779            .as_f64()
780            .expect("Reduction in Dispersion Test"),
781        RString::from_str(split_line.last().unwrap_or(&"0.0"))
782            .unwrap()
783            .as_f64()
784            .expect("P Value"),
785    )
786}
787
788fn r_wald_test(line: &str) -> (f64, f64) {
789    let split_line = line.split_whitespace().collect::<Vec<_>>();
790    (
791        RString::from_str(split_line.get(3).unwrap_or(&"0.0"))
792            .unwrap()
793            .as_f64()
794            .expect("Overall Wald Test"),
795        RString::from_str(split_line.last().unwrap_or(&"0.0"))
796            .unwrap()
797            .as_f64()
798            .expect("P Value"),
799    )
800}
801
802#[derive(Copy, Clone, Debug, Default)]
803pub struct RGenerator {
804    seed: u16,
805}
806
807impl RGenerator {
808    pub fn new() -> Self {
809        Self::default()
810    }
811
812    pub fn set_seed(self, seed: u16) -> Self {
813        Self { seed, ..self }
814    }
815
816    pub fn generate_uniform<T1: ToPrimitive + Debug, T2: ToPrimitive + Debug>(
817        &self,
818        len: T1,
819        (lower_bound, upper_bound): (T2, T2),
820    ) -> Vec<f64> {
821        RTester::new()
822            .set_seed(self.seed)
823            .set_script(&RString::from_string(format!(
824                "ret = runif({}, {}, {});",
825                RString::from_f64(len),
826                RString::from_f64(lower_bound),
827                RString::from_f64(upper_bound),
828            )))
829            .set_display(&RString::from_str("ret").unwrap())
830            .run()
831            .expect("R running error")
832            .as_f64_vec()
833            .expect("R running error")
834    }
835
836    pub fn generate_uniform_matrix<
837        T1: ToPrimitive + Debug,
838        T2: ToPrimitive + Debug,
839        T3: ToPrimitive + Debug + Clone,
840    >(
841        &self,
842        num_rows: T1,
843        num_columns: T2,
844        (lower_bound, upper_bound): (T3, T3),
845    ) -> DMatrix<f64> {
846        let mut x = RTester::new()
847            .set_seed(self.seed)
848            .set_display(&RString::from_string(format!(
849                "matrix(runif({0}*{1}, {2}, {3}), nrow={0}, ncol={1})",
850                RString::from_f64(num_rows),
851                RString::from_f64(num_columns),
852                RString::from_f64(lower_bound.clone()),
853                RString::from_f64(upper_bound.clone()),
854            )))
855            .run()
856            .expect("R error")
857            .as_f64_matrix()
858            .expect("R array error");
859        x.iter_mut().for_each(|x_i| {
860            if x_i.is_nan() {
861                *x_i = lower_bound.to_f64().unwrap()
862                    + ((upper_bound.clone().to_f64().unwrap()
863                        - lower_bound.clone().to_f64().unwrap())
864                        / 2.0)
865            }
866        });
867        x
868    }
869
870    pub fn generate_errors<
871        T1: ToPrimitive + Debug,
872        T2: ToPrimitive + Debug + Clone,
873        T3: ToPrimitive + Debug,
874    >(
875        &self,
876        len: T1,
877        (lower_bound, upper_bound): (T2, T2),
878        fuzz_amount: T3,
879    ) -> Vec<f64> {
880        let mut errors = RTester::new()
881            .set_seed(self.seed)
882            .set_display(&RString::from_string(format!(
883                "rnorm({0}, {1}, {2}) + runif({0}, -{3}, {3})",
884                RString::from_f64(len),
885                RString::from_f64(lower_bound.clone()),
886                RString::from_f64(upper_bound.clone()),
887                RString::from_f64(fuzz_amount)
888            )))
889            .run()
890            .expect("R error")
891            .as_f64_vec()
892            .expect("R array error");
893        errors.iter_mut().for_each(|e| {
894            if e.is_nan() {
895                *e = lower_bound.clone().to_f64().unwrap()
896                    + ((upper_bound.to_f64().unwrap() - lower_bound.clone().to_f64().unwrap())
897                        / 2.0)
898            }
899        });
900        errors
901    }
902}
903
904#[derive(Clone, Debug)]
905pub struct LMCoefs {
906    pub estimate: f64,
907    pub std_error: f64,
908    pub t: f64,
909    pub p: f64,
910}
911
912#[derive(Clone, Debug)]
913pub struct RLMCoefs {
914    pub estimate: f64,
915    pub std_error: f64,
916    pub t: f64,
917}
918
919#[derive(Clone, Debug)]
920pub struct GLMCoefs {
921    pub estimate: f64,
922    pub std_error: f64,
923    pub z: f64,
924    pub p: f64,
925}
926
927#[derive(Clone, Debug)]
928pub struct LMRegression {
929    pub coefs: Vec<LMCoefs>,
930    pub r_squared: f64,
931    pub f: f64,
932    pub p: f64,
933}
934
935#[derive(Clone, Debug)]
936pub struct RLMRegression {
937    pub coefs: Vec<RLMCoefs>,
938}
939
940#[derive(Clone, Debug)]
941pub struct GLMRegression {
942    pub coefs: Vec<GLMCoefs>,
943    pub null_dev: f64,
944    pub resid_dev: f64,
945    pub aic: f64,
946}
947
948#[derive(Clone, Debug)]
949pub struct RFitRegression {
950    pub coefs: Vec<LMCoefs>,
951    pub r_squared_robust: f64,
952    pub red_disp_test: f64,
953    pub disp_p: f64,
954    pub wald_test: f64,
955    pub wald_p: f64,
956}
957
958#[derive(Clone, Debug)]
959pub struct OneWayRFit {
960    pub i: String,
961    pub j: String,
962    pub estimate: f64,
963    pub std_err: f64,
964    pub confidence_interval: (f64, f64),
965}
966
967#[derive(Clone, Debug)]
968pub struct RAOV {
969    pub name: String,
970    pub df: f64,
971    pub rd: f64,
972    pub mean_rd: f64,
973    pub f: f64,
974    pub p: f64,
975}