1use std::collections::HashMap;
7use std::fmt::Write;
8use std::fs;
9use std::process::Command;
10
11pub struct RIntegration {
13 #[allow(dead_code)]
14 r_home: Option<String>,
15 #[allow(dead_code)]
16 library_paths: Vec<String>,
17 loaded_packages: Vec<String>,
18 workspace_variables: HashMap<String, RValue>,
19}
20
21#[derive(Debug, Clone)]
23pub enum RValue {
24 Null,
25 Logical(bool),
26 Integer(i32),
27 Double(f64),
28 Character(String),
29 IntegerVector(Vec<i32>),
30 DoubleVector(Vec<f64>),
31 CharacterVector(Vec<String>),
32 LogicalVector(Vec<bool>),
33 Matrix {
34 data: Vec<f64>,
35 nrows: usize,
36 ncols: usize,
37 },
38 DataFrame {
39 columns: HashMap<String, RValue>,
40 nrows: usize,
41 },
42 List(HashMap<String, RValue>),
43}
44
45#[derive(Debug, Clone)]
47pub struct RDataFrame {
48 pub columns: HashMap<String, RValue>,
49 pub nrows: usize,
50 pub column_names: Vec<String>,
51}
52
53#[derive(Debug, Clone)]
55pub struct RMatrix {
56 pub data: Vec<f64>,
57 pub nrows: usize,
58 pub ncols: usize,
59 pub row_names: Option<Vec<String>>,
60 pub col_names: Option<Vec<String>>,
61}
62
63pub struct RScriptBuilder {
65 script_lines: Vec<String>,
66 variables: HashMap<String, RValue>,
67 packages: Vec<String>,
68}
69
70pub struct RPackageManager {
72 installed_packages: Vec<String>,
73 #[allow(dead_code)]
74 available_packages: HashMap<String, String>, cran_mirror: String,
76}
77
78pub struct RStatisticalFunctions;
80
81impl RIntegration {
82 pub fn new() -> Result<Self, RError> {
84 let r_home = Self::detect_r_installation()?;
85
86 Ok(Self {
87 r_home: Some(r_home),
88 library_paths: vec![],
89 loaded_packages: ["base", "stats", "utils", "graphics", "grDevices"]
90 .iter()
91 .map(|s| s.to_string())
92 .collect(),
93 workspace_variables: HashMap::new(),
94 })
95 }
96
97 fn detect_r_installation() -> Result<String, RError> {
99 let output = Command::new("R")
101 .args(["--slave", "--vanilla", "-e", "cat(R.home())"])
102 .output()
103 .map_err(|_| RError::RNotFound)?;
104
105 if output.status.success() {
106 String::from_utf8(output.stdout).map_err(|_| RError::InvalidOutput)
107 } else {
108 Err(RError::RNotFound)
109 }
110 }
111
112 pub fn execute_script(&mut self, script: &str) -> Result<String, RError> {
114 let temp_file = "/tmp/sklears_r_script.R";
116 fs::write(temp_file, script).map_err(|e| RError::IoError(e.to_string()))?;
117
118 let output = Command::new("Rscript")
120 .arg(temp_file)
121 .output()
122 .map_err(|e| RError::ExecutionError(e.to_string()))?;
123
124 let _ = fs::remove_file(temp_file);
126
127 if output.status.success() {
128 String::from_utf8(output.stdout).map_err(|_| RError::InvalidOutput)
129 } else {
130 let error_msg =
131 String::from_utf8(output.stderr).unwrap_or_else(|_| "Unknown R error".to_string());
132 Err(RError::RScriptError(error_msg))
133 }
134 }
135
136 pub fn load_package(&mut self, package_name: &str) -> Result<(), RError> {
138 let script = format!("library({package_name})");
139 self.execute_script(&script)?;
140 self.loaded_packages.push(package_name.to_string());
141 Ok(())
142 }
143
144 pub fn array_to_r_vector(&self, data: &[f64]) -> RValue {
146 RValue::DoubleVector(data.to_vec())
147 }
148
149 pub fn matrix_to_r_matrix(&self, data: &[f64], nrows: usize, ncols: usize) -> RValue {
151 RValue::Matrix {
152 data: data.to_vec(),
153 nrows,
154 ncols,
155 }
156 }
157
158 pub fn r_vector_to_array(&self, r_value: &RValue) -> Result<Vec<f64>, RError> {
160 match r_value {
161 RValue::DoubleVector(vec) => Ok(vec.clone()),
162 RValue::IntegerVector(vec) => Ok(vec.iter().map(|&x| x as f64).collect()),
163 _ => Err(RError::TypeMismatch),
164 }
165 }
166
167 pub fn create_dataframe(&self, columns: HashMap<String, RValue>) -> Result<RDataFrame, RError> {
169 let mut nrows = 0;
171 let mut column_names = Vec::new();
172
173 for (name, value) in &columns {
174 let length = match value {
175 RValue::IntegerVector(v) => v.len(),
176 RValue::DoubleVector(v) => v.len(),
177 RValue::CharacterVector(v) => v.len(),
178 RValue::LogicalVector(v) => v.len(),
179 _ => return Err(RError::InvalidDataFrame),
180 };
181
182 if nrows == 0 {
183 nrows = length;
184 } else if nrows != length {
185 return Err(RError::InvalidDataFrame);
186 }
187
188 column_names.push(name.clone());
189 }
190
191 Ok(RDataFrame {
192 columns,
193 nrows,
194 column_names,
195 })
196 }
197
198 pub fn call_r_function(
200 &mut self,
201 function_name: &str,
202 args: &[RValue],
203 ) -> Result<RValue, RError> {
204 let mut script = String::new();
205
206 for (i, arg) in args.iter().enumerate() {
208 let var_name = format!("arg{i}");
209 let r_code = self.r_value_to_r_code(arg)?;
210 writeln!(script, "{var_name} <- {r_code}")
211 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
212 }
213
214 let arg_names: Vec<String> = (0..args.len()).map(|i| format!("arg{i}")).collect();
216 writeln!(
217 script,
218 "result <- {}({})",
219 function_name,
220 arg_names.join(", ")
221 )
222 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
223
224 writeln!(script, "cat(paste(result, collapse=','))")
226 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
227
228 let output = self.execute_script(&script)?;
229 self.parse_r_output(&output)
230 }
231
232 fn r_value_to_r_code(&self, value: &RValue) -> Result<String, RError> {
234 match value {
235 RValue::Null => Ok("NULL".to_string()),
236 RValue::Logical(b) => Ok(if *b { "TRUE" } else { "FALSE" }.to_string()),
237 RValue::Integer(i) => Ok(format!("{i}L")),
238 RValue::Double(d) => Ok(d.to_string()),
239 RValue::Character(s) => Ok(format!("\"{}\"", s.replace("\"", "\\\""))),
240 RValue::IntegerVector(vec) => Ok(format!(
241 "c({})",
242 vec.iter()
243 .map(|x| format!("{x}L"))
244 .collect::<Vec<_>>()
245 .join(", ")
246 )),
247 RValue::DoubleVector(vec) => Ok(format!(
248 "c({})",
249 vec.iter()
250 .map(|x| x.to_string())
251 .collect::<Vec<_>>()
252 .join(", ")
253 )),
254 RValue::CharacterVector(vec) => Ok(format!(
255 "c({})",
256 vec.iter()
257 .map(|s| format!("\"{}\"", s.replace("\"", "\\\"")))
258 .collect::<Vec<_>>()
259 .join(", ")
260 )),
261 RValue::LogicalVector(vec) => Ok(format!(
262 "c({})",
263 vec.iter()
264 .map(|b| if *b { "TRUE" } else { "FALSE" })
265 .collect::<Vec<_>>()
266 .join(", ")
267 )),
268 RValue::Matrix { data, nrows, ncols } => Ok(format!(
269 "matrix(c({}), nrow={}, ncol={})",
270 data.iter()
271 .map(|x| x.to_string())
272 .collect::<Vec<_>>()
273 .join(", "),
274 nrows,
275 ncols
276 )),
277 _ => Err(RError::UnsupportedType),
278 }
279 }
280
281 fn parse_r_output(&self, output: &str) -> Result<RValue, RError> {
283 let trimmed = output.trim();
284
285 if trimmed.is_empty() {
286 return Ok(RValue::Null);
287 }
288
289 if trimmed.contains(',') {
291 let values: Result<Vec<f64>, _> = trimmed
292 .split(',')
293 .map(|s| s.trim().parse::<f64>())
294 .collect();
295
296 if let Ok(vec) = values {
297 return Ok(RValue::DoubleVector(vec));
298 }
299 }
300
301 if let Ok(value) = trimmed.parse::<f64>() {
303 return Ok(RValue::Double(value));
304 }
305
306 if let Ok(value) = trimmed.parse::<i32>() {
307 return Ok(RValue::Integer(value));
308 }
309
310 if trimmed == "TRUE" {
311 return Ok(RValue::Logical(true));
312 }
313
314 if trimmed == "FALSE" {
315 return Ok(RValue::Logical(false));
316 }
317
318 Ok(RValue::Character(trimmed.to_string()))
320 }
321
322 pub fn get_loaded_packages(&self) -> &[String] {
324 &self.loaded_packages
325 }
326
327 pub fn is_package_available(&mut self, package_name: &str) -> Result<bool, RError> {
329 let script = format!("cat(is.element('{package_name}', installed.packages()[,1]))");
330 let output = self.execute_script(&script)?;
331 Ok(output.trim() == "TRUE")
332 }
333
334 pub fn install_package(&mut self, package_name: &str) -> Result<(), RError> {
336 let script =
337 format!("install.packages('{package_name}', repos='https://cran.r-project.org')");
338 self.execute_script(&script)?;
339 Ok(())
340 }
341
342 pub fn save_variable(&mut self, name: &str, value: RValue) {
344 self.workspace_variables.insert(name.to_string(), value);
345 }
346
347 pub fn get_variable(&self, name: &str) -> Option<&RValue> {
349 self.workspace_variables.get(name)
350 }
351
352 pub fn clear_workspace(&mut self) {
354 self.workspace_variables.clear();
355 }
356}
357
358impl RScriptBuilder {
359 pub fn new() -> Self {
361 Self {
362 script_lines: Vec::new(),
363 variables: HashMap::new(),
364 packages: Vec::new(),
365 }
366 }
367
368 pub fn require_package(&mut self, package: &str) -> &mut Self {
370 self.packages.push(package.to_string());
371 self
372 }
373
374 pub fn assign_variable(&mut self, name: &str, value: RValue) -> &mut Self {
376 self.variables.insert(name.to_string(), value);
377 self
378 }
379
380 pub fn add_line(&mut self, line: &str) -> &mut Self {
382 self.script_lines.push(line.to_string());
383 self
384 }
385
386 pub fn add_comment(&mut self, comment: &str) -> &mut Self {
388 self.script_lines.push(format!("# {comment}"));
389 self
390 }
391
392 pub fn build(&self) -> Result<String, RError> {
394 let mut script = String::new();
395
396 for package in &self.packages {
398 writeln!(script, "library({package})")
399 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
400 }
401
402 if !self.packages.is_empty() {
403 writeln!(script).map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
404 }
405
406 for (name, value) in &self.variables {
408 let r_code = self.r_value_to_r_code(value)?;
409 writeln!(script, "{name} <- {r_code}")
410 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
411 }
412
413 if !self.variables.is_empty() {
414 writeln!(script).map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
415 }
416
417 for line in &self.script_lines {
419 writeln!(script, "{line}").map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
420 }
421
422 Ok(script)
423 }
424
425 fn r_value_to_r_code(&self, value: &RValue) -> Result<String, RError> {
427 match value {
428 RValue::Null => Ok("NULL".to_string()),
429 RValue::Logical(b) => Ok(if *b { "TRUE" } else { "FALSE" }.to_string()),
430 RValue::Integer(i) => Ok(format!("{i}L")),
431 RValue::Double(d) => Ok(d.to_string()),
432 RValue::Character(s) => Ok(format!("\"{}\"", s.replace("\"", "\\\""))),
433 RValue::DoubleVector(vec) => Ok(format!(
434 "c({})",
435 vec.iter()
436 .map(|x| x.to_string())
437 .collect::<Vec<_>>()
438 .join(", ")
439 )),
440 _ => Err(RError::UnsupportedType),
441 }
442 }
443}
444
445impl RPackageManager {
446 pub fn new() -> Self {
448 Self {
449 installed_packages: Vec::new(),
450 available_packages: HashMap::new(),
451 cran_mirror: "https://cran.r-project.org".to_string(),
452 }
453 }
454
455 pub fn refresh(&mut self) -> Result<(), RError> {
457 let script = "cat(paste(installed.packages()[,1], collapse=','))";
459 let output = Command::new("Rscript")
460 .args(["-e", script])
461 .output()
462 .map_err(|e| RError::ExecutionError(e.to_string()))?;
463
464 if output.status.success() {
465 let packages_str =
466 String::from_utf8(output.stdout).map_err(|_| RError::InvalidOutput)?;
467
468 self.installed_packages = packages_str
469 .trim()
470 .split(',')
471 .map(|s| s.trim().to_string())
472 .filter(|s| !s.is_empty())
473 .collect();
474 }
475
476 Ok(())
477 }
478
479 pub fn is_installed(&self, package_name: &str) -> bool {
481 self.installed_packages.contains(&package_name.to_string())
482 }
483
484 pub fn install(&mut self, package_name: &str) -> Result<(), RError> {
486 let script = format!(
487 "install.packages('{}', repos='{}')",
488 package_name, self.cran_mirror
489 );
490
491 let output = Command::new("Rscript")
492 .args(["-e", &script])
493 .output()
494 .map_err(|e| RError::ExecutionError(e.to_string()))?;
495
496 if output.status.success() {
497 self.installed_packages.push(package_name.to_string());
498 Ok(())
499 } else {
500 let error_msg = String::from_utf8(output.stderr)
501 .unwrap_or_else(|_| "Package installation failed".to_string());
502 Err(RError::PackageInstallationError(error_msg))
503 }
504 }
505
506 pub fn remove(&mut self, package_name: &str) -> Result<(), RError> {
508 let script = format!("remove.packages('{package_name}')");
509
510 let output = Command::new("Rscript")
511 .args(["-e", &script])
512 .output()
513 .map_err(|e| RError::ExecutionError(e.to_string()))?;
514
515 if output.status.success() {
516 self.installed_packages.retain(|p| p != package_name);
517 Ok(())
518 } else {
519 let error_msg = String::from_utf8(output.stderr)
520 .unwrap_or_else(|_| "Package removal failed".to_string());
521 Err(RError::PackageRemovalError(error_msg))
522 }
523 }
524
525 pub fn get_installed_packages(&self) -> &[String] {
527 &self.installed_packages
528 }
529
530 pub fn set_cran_mirror(&mut self, mirror_url: &str) {
532 self.cran_mirror = mirror_url.to_string();
533 }
534}
535
536impl RStatisticalFunctions {
537 pub fn mean(r: &mut RIntegration, data: &[f64]) -> Result<f64, RError> {
539 let r_vector = r.array_to_r_vector(data);
540 let result = r.call_r_function("mean", &[r_vector])?;
541
542 match result {
543 RValue::Double(mean_val) => Ok(mean_val),
544 _ => Err(RError::TypeMismatch),
545 }
546 }
547
548 pub fn sd(r: &mut RIntegration, data: &[f64]) -> Result<f64, RError> {
550 let r_vector = r.array_to_r_vector(data);
551 let result = r.call_r_function("sd", &[r_vector])?;
552
553 match result {
554 RValue::Double(sd_val) => Ok(sd_val),
555 _ => Err(RError::TypeMismatch),
556 }
557 }
558
559 pub fn t_test(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<RValue, RError> {
561 r.load_package("stats")?;
562
563 let x_vector = r.array_to_r_vector(x);
564 let y_vector = r.array_to_r_vector(y);
565
566 r.call_r_function("t.test", &[x_vector, y_vector])
567 }
568
569 pub fn lm(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<RValue, RError> {
571 r.load_package("stats")?;
572
573 let mut columns = HashMap::new();
575 columns.insert("x".to_string(), r.array_to_r_vector(x));
576 columns.insert("y".to_string(), r.array_to_r_vector(y));
577
578 let script = "
579data <- data.frame(x=arg0, y=arg1)
580model <- lm(y ~ x, data=data)
581coefficients <- coef(model)
582cat(paste(coefficients, collapse=','))
583";
584
585 let mut script_with_args = String::new();
586 writeln!(
587 script_with_args,
588 "arg0 <- {}",
589 r.r_value_to_r_code(&columns["x"])?
590 )
591 .unwrap();
592 writeln!(
593 script_with_args,
594 "arg1 <- {}",
595 r.r_value_to_r_code(&columns["y"])?
596 )
597 .unwrap();
598 script_with_args.push_str(script);
599
600 let output = r.execute_script(&script_with_args)?;
601 r.parse_r_output(&output)
602 }
603
604 pub fn cor(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<f64, RError> {
606 let x_vector = r.array_to_r_vector(x);
607 let y_vector = r.array_to_r_vector(y);
608 let result = r.call_r_function("cor", &[x_vector, y_vector])?;
609
610 match result {
611 RValue::Double(cor_val) => Ok(cor_val),
612 _ => Err(RError::TypeMismatch),
613 }
614 }
615
616 pub fn anova(r: &mut RIntegration, groups: &[Vec<f64>]) -> Result<RValue, RError> {
618 r.load_package("stats")?;
619
620 let mut script = String::new();
622 let mut all_values = Vec::new();
623 let mut group_labels = Vec::new();
624
625 for (i, group) in groups.iter().enumerate() {
626 for &value in group {
627 all_values.push(value);
628 let group_num = i + 1;
629 group_labels.push(format!("Group{group_num}"));
630 }
631 }
632
633 writeln!(
634 script,
635 "values <- c({})",
636 all_values
637 .iter()
638 .map(|x| x.to_string())
639 .collect::<Vec<_>>()
640 .join(", ")
641 )
642 .unwrap();
643 writeln!(
644 script,
645 "groups <- factor(c({}))",
646 group_labels
647 .iter()
648 .map(|s| format!("\"{s}\""))
649 .collect::<Vec<_>>()
650 .join(", ")
651 )
652 .unwrap();
653 writeln!(script, "result <- aov(values ~ groups)").unwrap();
654 writeln!(script, "summary_result <- summary(result)").unwrap();
655 writeln!(script, "cat('ANOVA completed')").unwrap();
656
657 let output = r.execute_script(&script)?;
658 Ok(RValue::Character(output))
659 }
660
661 pub fn plot(r: &mut RIntegration, x: &[f64], y: &[f64], filename: &str) -> Result<(), RError> {
663 let script = format!(
664 "
665x <- c({})
666y <- c({})
667png('{}')
668plot(x, y, main='Scatter Plot', xlab='X', ylab='Y')
669dev.off()
670",
671 x.iter()
672 .map(|v| v.to_string())
673 .collect::<Vec<_>>()
674 .join(", "),
675 y.iter()
676 .map(|v| v.to_string())
677 .collect::<Vec<_>>()
678 .join(", "),
679 filename
680 );
681
682 r.execute_script(&script)?;
683 Ok(())
684 }
685}
686
687#[derive(Debug, thiserror::Error)]
689pub enum RError {
690 #[error("R installation not found")]
691 RNotFound,
692 #[error("Invalid R output")]
693 InvalidOutput,
694 #[error("I/O error: {0}")]
695 IoError(String),
696 #[error("R script execution error: {0}")]
697 ExecutionError(String),
698 #[error("R script error: {0}")]
699 RScriptError(String),
700 #[error("Type mismatch")]
701 TypeMismatch,
702 #[error("Invalid data frame")]
703 InvalidDataFrame,
704 #[error("Unsupported type")]
705 UnsupportedType,
706 #[error("Script generation error: {0}")]
707 ScriptGenerationError(String),
708 #[error("Package installation error: {0}")]
709 PackageInstallationError(String),
710 #[error("Package removal error: {0}")]
711 PackageRemovalError(String),
712}
713
714impl Default for RIntegration {
715 fn default() -> Self {
716 Self::new().unwrap_or_else(|_| Self {
717 r_home: None,
718 library_paths: Vec::new(),
719 loaded_packages: Vec::new(),
720 workspace_variables: HashMap::new(),
721 })
722 }
723}
724
725impl Default for RScriptBuilder {
726 fn default() -> Self {
727 Self::new()
728 }
729}
730
731impl Default for RPackageManager {
732 fn default() -> Self {
733 Self::new()
734 }
735}
736
737#[allow(non_snake_case)]
738#[cfg(test)]
739mod tests {
740 use super::*;
741
742 #[test]
743 fn test_r_script_builder() {
744 let mut builder = RScriptBuilder::new();
745
746 builder
747 .require_package("stats")
748 .assign_variable("x", RValue::DoubleVector(vec![1.0, 2.0, 3.0]))
749 .add_comment("Calculate mean")
750 .add_line("mean_x <- mean(x)")
751 .add_line("cat(mean_x)");
752
753 let script = builder.build().unwrap();
754 assert!(script.contains("library(stats)"));
755 assert!(script.contains("x <- c(1, 2, 3)"));
756 assert!(script.contains("# Calculate mean"));
757 assert!(script.contains("mean_x <- mean(x)"));
758 }
759
760 #[test]
761 fn test_r_value_conversions() {
762 let r = RIntegration::default();
763
764 let data = vec![1.0, 2.0, 3.0, 4.0];
766 let r_vector = r.array_to_r_vector(&data);
767
768 match r_vector {
769 RValue::DoubleVector(ref vec) => assert_eq!(vec, &data),
770 _ => panic!("Expected DoubleVector"),
771 }
772
773 let array = r.r_vector_to_array(&r_vector).unwrap();
775 assert_eq!(array, data);
776 }
777
778 #[test]
779 fn test_dataframe_creation() {
780 let r = RIntegration::default();
781
782 let mut columns = HashMap::new();
783 columns.insert("x".to_string(), RValue::DoubleVector(vec![1.0, 2.0, 3.0]));
784 columns.insert("y".to_string(), RValue::DoubleVector(vec![4.0, 5.0, 6.0]));
785
786 let df = r.create_dataframe(columns).unwrap();
787 assert_eq!(df.nrows, 3);
788 assert_eq!(df.column_names.len(), 2);
789 }
790
791 #[test]
792 fn test_r_code_generation() {
793 let r = RIntegration::default();
794
795 assert_eq!(
797 r.r_value_to_r_code(&RValue::Double(std::f64::consts::PI))
798 .unwrap(),
799 format!("{}", std::f64::consts::PI)
800 );
801 assert_eq!(r.r_value_to_r_code(&RValue::Integer(42)).unwrap(), "42L");
802 assert_eq!(r.r_value_to_r_code(&RValue::Logical(true)).unwrap(), "TRUE");
803 assert_eq!(
804 r.r_value_to_r_code(&RValue::Character("test".to_string()))
805 .unwrap(),
806 "\"test\""
807 );
808
809 let vec_result = r
810 .r_value_to_r_code(&RValue::DoubleVector(vec![1.0, 2.0, 3.0]))
811 .unwrap();
812 assert_eq!(vec_result, "c(1, 2, 3)");
813
814 let matrix_result = r
815 .r_value_to_r_code(&RValue::Matrix {
816 data: vec![1.0, 2.0, 3.0, 4.0],
817 nrows: 2,
818 ncols: 2,
819 })
820 .unwrap();
821 assert_eq!(matrix_result, "matrix(c(1, 2, 3, 4), nrow=2, ncol=2)");
822 }
823
824 #[test]
825 fn test_package_manager() {
826 let mut manager = RPackageManager::new();
827
828 assert_eq!(manager.get_installed_packages().len(), 0);
830 assert!(!manager.is_installed("ggplot2"));
831
832 manager.installed_packages.push("base".to_string());
834 manager.installed_packages.push("stats".to_string());
835
836 assert!(manager.is_installed("base"));
837 assert!(manager.is_installed("stats"));
838 assert!(!manager.is_installed("ggplot2"));
839 }
840
841 #[test]
842 fn test_workspace_variables() {
843 let mut r = RIntegration::default();
844
845 let value = RValue::Double(42.0);
846 r.save_variable("test_var", value.clone());
847
848 let retrieved = r.get_variable("test_var");
849 assert!(retrieved.is_some());
850
851 match retrieved.unwrap() {
852 RValue::Double(val) => assert_eq!(*val, 42.0),
853 _ => panic!("Expected Double value"),
854 }
855
856 r.clear_workspace();
857 assert!(r.get_variable("test_var").is_none());
858 }
859
860 #[test]
861 fn test_output_parsing() {
862 let r = RIntegration::default();
863
864 let result = r.parse_r_output("42.5").unwrap();
866 match result {
867 RValue::Double(val) => assert_eq!(val, 42.5),
868 _ => panic!("Expected Double"),
869 }
870
871 let result = r.parse_r_output("1,2,3,4").unwrap();
872 match result {
873 RValue::DoubleVector(vec) => assert_eq!(vec, vec![1.0, 2.0, 3.0, 4.0]),
874 _ => panic!("Expected DoubleVector"),
875 }
876
877 let result = r.parse_r_output("TRUE").unwrap();
878 match result {
879 RValue::Logical(val) => assert!(val),
880 _ => panic!("Expected Logical"),
881 }
882
883 let result = r.parse_r_output("test string").unwrap();
884 match result {
885 RValue::Character(val) => assert_eq!(val, "test string"),
886 _ => panic!("Expected Character"),
887 }
888 }
889
890 #[test]
891 fn test_matrix_operations() {
892 let r = RIntegration::default();
893
894 let matrix = r.matrix_to_r_matrix(&[1.0, 2.0, 3.0, 4.0], 2, 2);
895
896 match matrix {
897 RValue::Matrix { data, nrows, ncols } => {
898 assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0]);
899 assert_eq!(nrows, 2);
900 assert_eq!(ncols, 2);
901 }
902 _ => panic!("Expected Matrix"),
903 }
904 }
905
906 }