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_path = std::env::temp_dir().join("sklears_r_script.R");
116 let temp_file = temp_path.to_string_lossy().into_owned();
117 fs::write(&temp_file, script).map_err(|e| RError::IoError(e.to_string()))?;
118
119 let output = Command::new("Rscript")
121 .arg(&temp_file)
122 .output()
123 .map_err(|e| RError::ExecutionError(e.to_string()))?;
124
125 let _ = fs::remove_file(&temp_file);
127
128 if output.status.success() {
129 String::from_utf8(output.stdout).map_err(|_| RError::InvalidOutput)
130 } else {
131 let error_msg =
132 String::from_utf8(output.stderr).unwrap_or_else(|_| "Unknown R error".to_string());
133 Err(RError::RScriptError(error_msg))
134 }
135 }
136
137 pub fn load_package(&mut self, package_name: &str) -> Result<(), RError> {
139 let script = format!("library({package_name})");
140 self.execute_script(&script)?;
141 self.loaded_packages.push(package_name.to_string());
142 Ok(())
143 }
144
145 pub fn array_to_r_vector(&self, data: &[f64]) -> RValue {
147 RValue::DoubleVector(data.to_vec())
148 }
149
150 pub fn matrix_to_r_matrix(&self, data: &[f64], nrows: usize, ncols: usize) -> RValue {
152 RValue::Matrix {
153 data: data.to_vec(),
154 nrows,
155 ncols,
156 }
157 }
158
159 pub fn r_vector_to_array(&self, r_value: &RValue) -> Result<Vec<f64>, RError> {
161 match r_value {
162 RValue::DoubleVector(vec) => Ok(vec.clone()),
163 RValue::IntegerVector(vec) => Ok(vec.iter().map(|&x| x as f64).collect()),
164 _ => Err(RError::TypeMismatch),
165 }
166 }
167
168 pub fn create_dataframe(&self, columns: HashMap<String, RValue>) -> Result<RDataFrame, RError> {
170 let mut nrows = 0;
172 let mut column_names = Vec::new();
173
174 for (name, value) in &columns {
175 let length = match value {
176 RValue::IntegerVector(v) => v.len(),
177 RValue::DoubleVector(v) => v.len(),
178 RValue::CharacterVector(v) => v.len(),
179 RValue::LogicalVector(v) => v.len(),
180 _ => return Err(RError::InvalidDataFrame),
181 };
182
183 if nrows == 0 {
184 nrows = length;
185 } else if nrows != length {
186 return Err(RError::InvalidDataFrame);
187 }
188
189 column_names.push(name.clone());
190 }
191
192 Ok(RDataFrame {
193 columns,
194 nrows,
195 column_names,
196 })
197 }
198
199 pub fn call_r_function(
201 &mut self,
202 function_name: &str,
203 args: &[RValue],
204 ) -> Result<RValue, RError> {
205 let mut script = String::new();
206
207 for (i, arg) in args.iter().enumerate() {
209 let var_name = format!("arg{i}");
210 let r_code = self.r_value_to_r_code(arg)?;
211 writeln!(script, "{var_name} <- {r_code}")
212 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
213 }
214
215 let arg_names: Vec<String> = (0..args.len()).map(|i| format!("arg{i}")).collect();
217 writeln!(
218 script,
219 "result <- {}({})",
220 function_name,
221 arg_names.join(", ")
222 )
223 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
224
225 writeln!(script, "cat(paste(result, collapse=','))")
227 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
228
229 let output = self.execute_script(&script)?;
230 self.parse_r_output(&output)
231 }
232
233 fn r_value_to_r_code(&self, value: &RValue) -> Result<String, RError> {
235 match value {
236 RValue::Null => Ok("NULL".to_string()),
237 RValue::Logical(b) => Ok(if *b { "TRUE" } else { "FALSE" }.to_string()),
238 RValue::Integer(i) => Ok(format!("{i}L")),
239 RValue::Double(d) => Ok(d.to_string()),
240 RValue::Character(s) => Ok(format!("\"{}\"", s.replace("\"", "\\\""))),
241 RValue::IntegerVector(vec) => Ok(format!(
242 "c({})",
243 vec.iter()
244 .map(|x| format!("{x}L"))
245 .collect::<Vec<_>>()
246 .join(", ")
247 )),
248 RValue::DoubleVector(vec) => Ok(format!(
249 "c({})",
250 vec.iter()
251 .map(|x| x.to_string())
252 .collect::<Vec<_>>()
253 .join(", ")
254 )),
255 RValue::CharacterVector(vec) => Ok(format!(
256 "c({})",
257 vec.iter()
258 .map(|s| format!("\"{}\"", s.replace("\"", "\\\"")))
259 .collect::<Vec<_>>()
260 .join(", ")
261 )),
262 RValue::LogicalVector(vec) => Ok(format!(
263 "c({})",
264 vec.iter()
265 .map(|b| if *b { "TRUE" } else { "FALSE" })
266 .collect::<Vec<_>>()
267 .join(", ")
268 )),
269 RValue::Matrix { data, nrows, ncols } => Ok(format!(
270 "matrix(c({}), nrow={}, ncol={})",
271 data.iter()
272 .map(|x| x.to_string())
273 .collect::<Vec<_>>()
274 .join(", "),
275 nrows,
276 ncols
277 )),
278 _ => Err(RError::UnsupportedType),
279 }
280 }
281
282 fn parse_r_output(&self, output: &str) -> Result<RValue, RError> {
284 let trimmed = output.trim();
285
286 if trimmed.is_empty() {
287 return Ok(RValue::Null);
288 }
289
290 if trimmed.contains(',') {
292 let values: Result<Vec<f64>, _> = trimmed
293 .split(',')
294 .map(|s| s.trim().parse::<f64>())
295 .collect();
296
297 if let Ok(vec) = values {
298 return Ok(RValue::DoubleVector(vec));
299 }
300 }
301
302 if let Ok(value) = trimmed.parse::<f64>() {
304 return Ok(RValue::Double(value));
305 }
306
307 if let Ok(value) = trimmed.parse::<i32>() {
308 return Ok(RValue::Integer(value));
309 }
310
311 if trimmed == "TRUE" {
312 return Ok(RValue::Logical(true));
313 }
314
315 if trimmed == "FALSE" {
316 return Ok(RValue::Logical(false));
317 }
318
319 Ok(RValue::Character(trimmed.to_string()))
321 }
322
323 pub fn get_loaded_packages(&self) -> &[String] {
325 &self.loaded_packages
326 }
327
328 pub fn is_package_available(&mut self, package_name: &str) -> Result<bool, RError> {
330 let script = format!("cat(is.element('{package_name}', installed.packages()[,1]))");
331 let output = self.execute_script(&script)?;
332 Ok(output.trim() == "TRUE")
333 }
334
335 pub fn install_package(&mut self, package_name: &str) -> Result<(), RError> {
337 let script =
338 format!("install.packages('{package_name}', repos='https://cran.r-project.org')");
339 self.execute_script(&script)?;
340 Ok(())
341 }
342
343 pub fn save_variable(&mut self, name: &str, value: RValue) {
345 self.workspace_variables.insert(name.to_string(), value);
346 }
347
348 pub fn get_variable(&self, name: &str) -> Option<&RValue> {
350 self.workspace_variables.get(name)
351 }
352
353 pub fn clear_workspace(&mut self) {
355 self.workspace_variables.clear();
356 }
357}
358
359impl RScriptBuilder {
360 pub fn new() -> Self {
362 Self {
363 script_lines: Vec::new(),
364 variables: HashMap::new(),
365 packages: Vec::new(),
366 }
367 }
368
369 pub fn require_package(&mut self, package: &str) -> &mut Self {
371 self.packages.push(package.to_string());
372 self
373 }
374
375 pub fn assign_variable(&mut self, name: &str, value: RValue) -> &mut Self {
377 self.variables.insert(name.to_string(), value);
378 self
379 }
380
381 pub fn add_line(&mut self, line: &str) -> &mut Self {
383 self.script_lines.push(line.to_string());
384 self
385 }
386
387 pub fn add_comment(&mut self, comment: &str) -> &mut Self {
389 self.script_lines.push(format!("# {comment}"));
390 self
391 }
392
393 pub fn build(&self) -> Result<String, RError> {
395 let mut script = String::new();
396
397 for package in &self.packages {
399 writeln!(script, "library({package})")
400 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
401 }
402
403 if !self.packages.is_empty() {
404 writeln!(script).map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
405 }
406
407 for (name, value) in &self.variables {
409 let r_code = self.r_value_to_r_code(value)?;
410 writeln!(script, "{name} <- {r_code}")
411 .map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
412 }
413
414 if !self.variables.is_empty() {
415 writeln!(script).map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
416 }
417
418 for line in &self.script_lines {
420 writeln!(script, "{line}").map_err(|e| RError::ScriptGenerationError(e.to_string()))?;
421 }
422
423 Ok(script)
424 }
425
426 fn r_value_to_r_code(&self, value: &RValue) -> Result<String, RError> {
428 match value {
429 RValue::Null => Ok("NULL".to_string()),
430 RValue::Logical(b) => Ok(if *b { "TRUE" } else { "FALSE" }.to_string()),
431 RValue::Integer(i) => Ok(format!("{i}L")),
432 RValue::Double(d) => Ok(d.to_string()),
433 RValue::Character(s) => Ok(format!("\"{}\"", s.replace("\"", "\\\""))),
434 RValue::DoubleVector(vec) => Ok(format!(
435 "c({})",
436 vec.iter()
437 .map(|x| x.to_string())
438 .collect::<Vec<_>>()
439 .join(", ")
440 )),
441 _ => Err(RError::UnsupportedType),
442 }
443 }
444}
445
446impl RPackageManager {
447 pub fn new() -> Self {
449 Self {
450 installed_packages: Vec::new(),
451 available_packages: HashMap::new(),
452 cran_mirror: "https://cran.r-project.org".to_string(),
453 }
454 }
455
456 pub fn refresh(&mut self) -> Result<(), RError> {
458 let script = "cat(paste(installed.packages()[,1], collapse=','))";
460 let output = Command::new("Rscript")
461 .args(["-e", script])
462 .output()
463 .map_err(|e| RError::ExecutionError(e.to_string()))?;
464
465 if output.status.success() {
466 let packages_str =
467 String::from_utf8(output.stdout).map_err(|_| RError::InvalidOutput)?;
468
469 self.installed_packages = packages_str
470 .trim()
471 .split(',')
472 .map(|s| s.trim().to_string())
473 .filter(|s| !s.is_empty())
474 .collect();
475 }
476
477 Ok(())
478 }
479
480 pub fn is_installed(&self, package_name: &str) -> bool {
482 self.installed_packages.contains(&package_name.to_string())
483 }
484
485 pub fn install(&mut self, package_name: &str) -> Result<(), RError> {
487 let script = format!(
488 "install.packages('{}', repos='{}')",
489 package_name, self.cran_mirror
490 );
491
492 let output = Command::new("Rscript")
493 .args(["-e", &script])
494 .output()
495 .map_err(|e| RError::ExecutionError(e.to_string()))?;
496
497 if output.status.success() {
498 self.installed_packages.push(package_name.to_string());
499 Ok(())
500 } else {
501 let error_msg = String::from_utf8(output.stderr)
502 .unwrap_or_else(|_| "Package installation failed".to_string());
503 Err(RError::PackageInstallationError(error_msg))
504 }
505 }
506
507 pub fn remove(&mut self, package_name: &str) -> Result<(), RError> {
509 let script = format!("remove.packages('{package_name}')");
510
511 let output = Command::new("Rscript")
512 .args(["-e", &script])
513 .output()
514 .map_err(|e| RError::ExecutionError(e.to_string()))?;
515
516 if output.status.success() {
517 self.installed_packages.retain(|p| p != package_name);
518 Ok(())
519 } else {
520 let error_msg = String::from_utf8(output.stderr)
521 .unwrap_or_else(|_| "Package removal failed".to_string());
522 Err(RError::PackageRemovalError(error_msg))
523 }
524 }
525
526 pub fn get_installed_packages(&self) -> &[String] {
528 &self.installed_packages
529 }
530
531 pub fn set_cran_mirror(&mut self, mirror_url: &str) {
533 self.cran_mirror = mirror_url.to_string();
534 }
535}
536
537impl RStatisticalFunctions {
538 pub fn mean(r: &mut RIntegration, data: &[f64]) -> Result<f64, RError> {
540 let r_vector = r.array_to_r_vector(data);
541 let result = r.call_r_function("mean", &[r_vector])?;
542
543 match result {
544 RValue::Double(mean_val) => Ok(mean_val),
545 _ => Err(RError::TypeMismatch),
546 }
547 }
548
549 pub fn sd(r: &mut RIntegration, data: &[f64]) -> Result<f64, RError> {
551 let r_vector = r.array_to_r_vector(data);
552 let result = r.call_r_function("sd", &[r_vector])?;
553
554 match result {
555 RValue::Double(sd_val) => Ok(sd_val),
556 _ => Err(RError::TypeMismatch),
557 }
558 }
559
560 pub fn t_test(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<RValue, RError> {
562 r.load_package("stats")?;
563
564 let x_vector = r.array_to_r_vector(x);
565 let y_vector = r.array_to_r_vector(y);
566
567 r.call_r_function("t.test", &[x_vector, y_vector])
568 }
569
570 pub fn lm(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<RValue, RError> {
572 r.load_package("stats")?;
573
574 let mut columns = HashMap::new();
576 columns.insert("x".to_string(), r.array_to_r_vector(x));
577 columns.insert("y".to_string(), r.array_to_r_vector(y));
578
579 let script = "
580data <- data.frame(x=arg0, y=arg1)
581model <- lm(y ~ x, data=data)
582coefficients <- coef(model)
583cat(paste(coefficients, collapse=','))
584";
585
586 let mut script_with_args = String::new();
587 writeln!(
588 script_with_args,
589 "arg0 <- {}",
590 r.r_value_to_r_code(&columns["x"])?
591 )
592 .expect("operation should succeed");
593 writeln!(
594 script_with_args,
595 "arg1 <- {}",
596 r.r_value_to_r_code(&columns["y"])?
597 )
598 .expect("operation should succeed");
599 script_with_args.push_str(script);
600
601 let output = r.execute_script(&script_with_args)?;
602 r.parse_r_output(&output)
603 }
604
605 pub fn cor(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<f64, RError> {
607 let x_vector = r.array_to_r_vector(x);
608 let y_vector = r.array_to_r_vector(y);
609 let result = r.call_r_function("cor", &[x_vector, y_vector])?;
610
611 match result {
612 RValue::Double(cor_val) => Ok(cor_val),
613 _ => Err(RError::TypeMismatch),
614 }
615 }
616
617 pub fn anova(r: &mut RIntegration, groups: &[Vec<f64>]) -> Result<RValue, RError> {
619 r.load_package("stats")?;
620
621 let mut script = String::new();
623 let mut all_values = Vec::new();
624 let mut group_labels = Vec::new();
625
626 for (i, group) in groups.iter().enumerate() {
627 for &value in group {
628 all_values.push(value);
629 let group_num = i + 1;
630 group_labels.push(format!("Group{group_num}"));
631 }
632 }
633
634 writeln!(
635 script,
636 "values <- c({})",
637 all_values
638 .iter()
639 .map(|x| x.to_string())
640 .collect::<Vec<_>>()
641 .join(", ")
642 )
643 .expect("operation should succeed");
644 writeln!(
645 script,
646 "groups <- factor(c({}))",
647 group_labels
648 .iter()
649 .map(|s| format!("\"{s}\""))
650 .collect::<Vec<_>>()
651 .join(", ")
652 )
653 .expect("operation should succeed");
654 writeln!(script, "result <- aov(values ~ groups)").expect("operation should succeed");
655 writeln!(script, "summary_result <- summary(result)").expect("operation should succeed");
656 writeln!(script, "cat('ANOVA completed')").expect("operation should succeed");
657
658 let output = r.execute_script(&script)?;
659 Ok(RValue::Character(output))
660 }
661
662 pub fn plot(r: &mut RIntegration, x: &[f64], y: &[f64], filename: &str) -> Result<(), RError> {
664 let script = format!(
665 "
666x <- c({})
667y <- c({})
668png('{}')
669plot(x, y, main='Scatter Plot', xlab='X', ylab='Y')
670dev.off()
671",
672 x.iter()
673 .map(|v| v.to_string())
674 .collect::<Vec<_>>()
675 .join(", "),
676 y.iter()
677 .map(|v| v.to_string())
678 .collect::<Vec<_>>()
679 .join(", "),
680 filename
681 );
682
683 r.execute_script(&script)?;
684 Ok(())
685 }
686}
687
688#[derive(Debug, thiserror::Error)]
690pub enum RError {
691 #[error("R installation not found")]
692 RNotFound,
693 #[error("Invalid R output")]
694 InvalidOutput,
695 #[error("I/O error: {0}")]
696 IoError(String),
697 #[error("R script execution error: {0}")]
698 ExecutionError(String),
699 #[error("R script error: {0}")]
700 RScriptError(String),
701 #[error("Type mismatch")]
702 TypeMismatch,
703 #[error("Invalid data frame")]
704 InvalidDataFrame,
705 #[error("Unsupported type")]
706 UnsupportedType,
707 #[error("Script generation error: {0}")]
708 ScriptGenerationError(String),
709 #[error("Package installation error: {0}")]
710 PackageInstallationError(String),
711 #[error("Package removal error: {0}")]
712 PackageRemovalError(String),
713}
714
715impl Default for RIntegration {
716 fn default() -> Self {
717 Self::new().unwrap_or_else(|_| Self {
718 r_home: None,
719 library_paths: Vec::new(),
720 loaded_packages: Vec::new(),
721 workspace_variables: HashMap::new(),
722 })
723 }
724}
725
726impl Default for RScriptBuilder {
727 fn default() -> Self {
728 Self::new()
729 }
730}
731
732impl Default for RPackageManager {
733 fn default() -> Self {
734 Self::new()
735 }
736}
737
738#[allow(non_snake_case)]
739#[cfg(test)]
740mod tests {
741 use super::*;
742
743 #[test]
744 fn test_r_script_builder() {
745 let mut builder = RScriptBuilder::new();
746
747 builder
748 .require_package("stats")
749 .assign_variable("x", RValue::DoubleVector(vec![1.0, 2.0, 3.0]))
750 .add_comment("Calculate mean")
751 .add_line("mean_x <- mean(x)")
752 .add_line("cat(mean_x)");
753
754 let script = builder.build().expect("operation should succeed");
755 assert!(script.contains("library(stats)"));
756 assert!(script.contains("x <- c(1, 2, 3)"));
757 assert!(script.contains("# Calculate mean"));
758 assert!(script.contains("mean_x <- mean(x)"));
759 }
760
761 #[test]
762 fn test_r_value_conversions() {
763 let r = RIntegration::default();
764
765 let data = vec![1.0, 2.0, 3.0, 4.0];
767 let r_vector = r.array_to_r_vector(&data);
768
769 match r_vector {
770 RValue::DoubleVector(ref vec) => assert_eq!(vec, &data),
771 _ => panic!("Expected DoubleVector"),
772 }
773
774 let array = r
776 .r_vector_to_array(&r_vector)
777 .expect("operation should succeed");
778 assert_eq!(array, data);
779 }
780
781 #[test]
782 fn test_dataframe_creation() {
783 let r = RIntegration::default();
784
785 let mut columns = HashMap::new();
786 columns.insert("x".to_string(), RValue::DoubleVector(vec![1.0, 2.0, 3.0]));
787 columns.insert("y".to_string(), RValue::DoubleVector(vec![4.0, 5.0, 6.0]));
788
789 let df = r
790 .create_dataframe(columns)
791 .expect("operation should succeed");
792 assert_eq!(df.nrows, 3);
793 assert_eq!(df.column_names.len(), 2);
794 }
795
796 #[test]
797 fn test_r_code_generation() {
798 let r = RIntegration::default();
799
800 assert_eq!(
802 r.r_value_to_r_code(&RValue::Double(std::f64::consts::PI))
803 .expect("operation should succeed"),
804 format!("{}", std::f64::consts::PI)
805 );
806 assert_eq!(
807 r.r_value_to_r_code(&RValue::Integer(42))
808 .expect("operation should succeed"),
809 "42L"
810 );
811 assert_eq!(
812 r.r_value_to_r_code(&RValue::Logical(true))
813 .expect("operation should succeed"),
814 "TRUE"
815 );
816 assert_eq!(
817 r.r_value_to_r_code(&RValue::Character("test".to_string()))
818 .expect("operation should succeed"),
819 "\"test\""
820 );
821
822 let vec_result = r
823 .r_value_to_r_code(&RValue::DoubleVector(vec![1.0, 2.0, 3.0]))
824 .expect("operation should succeed");
825 assert_eq!(vec_result, "c(1, 2, 3)");
826
827 let matrix_result = r
828 .r_value_to_r_code(&RValue::Matrix {
829 data: vec![1.0, 2.0, 3.0, 4.0],
830 nrows: 2,
831 ncols: 2,
832 })
833 .expect("operation should succeed");
834 assert_eq!(matrix_result, "matrix(c(1, 2, 3, 4), nrow=2, ncol=2)");
835 }
836
837 #[test]
838 fn test_package_manager() {
839 let mut manager = RPackageManager::new();
840
841 assert_eq!(manager.get_installed_packages().len(), 0);
843 assert!(!manager.is_installed("ggplot2"));
844
845 manager.installed_packages.push("base".to_string());
847 manager.installed_packages.push("stats".to_string());
848
849 assert!(manager.is_installed("base"));
850 assert!(manager.is_installed("stats"));
851 assert!(!manager.is_installed("ggplot2"));
852 }
853
854 #[test]
855 fn test_workspace_variables() {
856 let mut r = RIntegration::default();
857
858 let value = RValue::Double(42.0);
859 r.save_variable("test_var", value.clone());
860
861 let retrieved = r.get_variable("test_var");
862 assert!(retrieved.is_some());
863
864 match retrieved.expect("operation should succeed") {
865 RValue::Double(val) => assert_eq!(*val, 42.0),
866 _ => panic!("Expected Double value"),
867 }
868
869 r.clear_workspace();
870 assert!(r.get_variable("test_var").is_none());
871 }
872
873 #[test]
874 fn test_output_parsing() {
875 let r = RIntegration::default();
876
877 let result = r.parse_r_output("42.5").expect("operation should succeed");
879 match result {
880 RValue::Double(val) => assert_eq!(val, 42.5),
881 _ => panic!("Expected Double"),
882 }
883
884 let result = r
885 .parse_r_output("1,2,3,4")
886 .expect("operation should succeed");
887 match result {
888 RValue::DoubleVector(vec) => assert_eq!(vec, vec![1.0, 2.0, 3.0, 4.0]),
889 _ => panic!("Expected DoubleVector"),
890 }
891
892 let result = r.parse_r_output("TRUE").expect("operation should succeed");
893 match result {
894 RValue::Logical(val) => assert!(val),
895 _ => panic!("Expected Logical"),
896 }
897
898 let result = r
899 .parse_r_output("test string")
900 .expect("operation should succeed");
901 match result {
902 RValue::Character(val) => assert_eq!(val, "test string"),
903 _ => panic!("Expected Character"),
904 }
905 }
906
907 #[test]
908 fn test_matrix_operations() {
909 let r = RIntegration::default();
910
911 let matrix = r.matrix_to_r_matrix(&[1.0, 2.0, 3.0, 4.0], 2, 2);
912
913 match matrix {
914 RValue::Matrix { data, nrows, ncols } => {
915 assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0]);
916 assert_eq!(nrows, 2);
917 assert_eq!(ncols, 2);
918 }
919 _ => panic!("Expected Matrix"),
920 }
921 }
922
923 }