sklears_utils/
r_integration.rs

1//! R integration utilities
2//!
3//! This module provides utilities for R integration including data exchange,
4//! R script execution, statistical function bindings, and package management.
5
6use std::collections::HashMap;
7use std::fmt::Write;
8use std::fs;
9use std::process::Command;
10
11/// R integration utilities
12pub 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/// R value types
22#[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/// R data frame representation
46#[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/// R matrix representation
54#[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
63/// R script builder
64pub struct RScriptBuilder {
65    script_lines: Vec<String>,
66    variables: HashMap<String, RValue>,
67    packages: Vec<String>,
68}
69
70/// R package manager
71pub struct RPackageManager {
72    installed_packages: Vec<String>,
73    #[allow(dead_code)]
74    available_packages: HashMap<String, String>, // name -> version
75    cran_mirror: String,
76}
77
78/// R statistical functions
79pub struct RStatisticalFunctions;
80
81impl RIntegration {
82    /// Create new R integration instance
83    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    /// Detect R installation
98    fn detect_r_installation() -> Result<String, RError> {
99        // Try to find R executable
100        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    /// Execute R script
113    pub fn execute_script(&mut self, script: &str) -> Result<String, RError> {
114        // Write script to temporary file
115        let temp_file = "/tmp/sklears_r_script.R";
116        fs::write(temp_file, script).map_err(|e| RError::IoError(e.to_string()))?;
117
118        // Execute R script
119        let output = Command::new("Rscript")
120            .arg(temp_file)
121            .output()
122            .map_err(|e| RError::ExecutionError(e.to_string()))?;
123
124        // Clean up
125        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    /// Load R package
137    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    /// Convert Rust array to R vector
145    pub fn array_to_r_vector(&self, data: &[f64]) -> RValue {
146        RValue::DoubleVector(data.to_vec())
147    }
148
149    /// Convert Rust matrix to R matrix
150    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    /// Convert R value to Rust array
159    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    /// Create R data frame from columns
168    pub fn create_dataframe(&self, columns: HashMap<String, RValue>) -> Result<RDataFrame, RError> {
169        // Validate all columns have same length
170        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    /// Execute R statistical function
199    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        // Convert arguments to R syntax
207        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        // Call function
215        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        // Output result
225        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    /// Convert R value to R code
233    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    /// Parse R output
282    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        // Try to parse as comma-separated values
290        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        // Try to parse as single value
302        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        // Default to character
319        Ok(RValue::Character(trimmed.to_string()))
320    }
321
322    /// Get loaded packages
323    pub fn get_loaded_packages(&self) -> &[String] {
324        &self.loaded_packages
325    }
326
327    /// Check if package is available
328    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    /// Install R package
335    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    /// Save workspace variable
343    pub fn save_variable(&mut self, name: &str, value: RValue) {
344        self.workspace_variables.insert(name.to_string(), value);
345    }
346
347    /// Get workspace variable
348    pub fn get_variable(&self, name: &str) -> Option<&RValue> {
349        self.workspace_variables.get(name)
350    }
351
352    /// Clear workspace
353    pub fn clear_workspace(&mut self) {
354        self.workspace_variables.clear();
355    }
356}
357
358impl RScriptBuilder {
359    /// Create new R script builder
360    pub fn new() -> Self {
361        Self {
362            script_lines: Vec::new(),
363            variables: HashMap::new(),
364            packages: Vec::new(),
365        }
366    }
367
368    /// Add package requirement
369    pub fn require_package(&mut self, package: &str) -> &mut Self {
370        self.packages.push(package.to_string());
371        self
372    }
373
374    /// Add variable assignment
375    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    /// Add R code line
381    pub fn add_line(&mut self, line: &str) -> &mut Self {
382        self.script_lines.push(line.to_string());
383        self
384    }
385
386    /// Add comment
387    pub fn add_comment(&mut self, comment: &str) -> &mut Self {
388        self.script_lines.push(format!("# {comment}"));
389        self
390    }
391
392    /// Build the complete R script
393    pub fn build(&self) -> Result<String, RError> {
394        let mut script = String::new();
395
396        // Add package loading
397        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        // Add variable assignments
407        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        // Add script lines
418        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    /// Convert R value to R code (helper method)
426    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    /// Create new R package manager
447    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    /// Refresh package information
456    pub fn refresh(&mut self) -> Result<(), RError> {
457        // Get installed packages
458        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    /// Check if package is installed
480    pub fn is_installed(&self, package_name: &str) -> bool {
481        self.installed_packages.contains(&package_name.to_string())
482    }
483
484    /// Install package
485    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    /// Remove package
507    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    /// Get installed packages
526    pub fn get_installed_packages(&self) -> &[String] {
527        &self.installed_packages
528    }
529
530    /// Set CRAN mirror
531    pub fn set_cran_mirror(&mut self, mirror_url: &str) {
532        self.cran_mirror = mirror_url.to_string();
533    }
534}
535
536impl RStatisticalFunctions {
537    /// Compute mean using R
538    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    /// Compute standard deviation using R
549    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    /// Perform t-test using R
560    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    /// Perform linear regression using R
570    pub fn lm(r: &mut RIntegration, x: &[f64], y: &[f64]) -> Result<RValue, RError> {
571        r.load_package("stats")?;
572
573        // Create data frame
574        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    /// Compute correlation using R
605    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    /// Perform ANOVA using R
617    pub fn anova(r: &mut RIntegration, groups: &[Vec<f64>]) -> Result<RValue, RError> {
618        r.load_package("stats")?;
619
620        // Prepare data for ANOVA
621        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    /// Generate R plots
662    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/// R integration errors
688#[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        // Test array to R vector
765        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        // Test R vector to array
774        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        // Test various R value types
796        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        // Test initial state
829        assert_eq!(manager.get_installed_packages().len(), 0);
830        assert!(!manager.is_installed("ggplot2"));
831
832        // Test adding packages manually (for testing)
833        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        // Test parsing different output types
865        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    // Note: The following tests would require R to be installed and available
907    // They are commented out but show how integration testing would work
908
909    /*
910    #[test]
911    fn test_r_integration_with_real_r() {
912        let mut r = RIntegration::new().unwrap();
913
914        // Test simple calculation
915        let result = r.execute_script("cat(2 + 2)").unwrap();
916        assert_eq!(result.trim(), "4");
917
918        // Test statistical function
919        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
920        let mean_result = RStatisticalFunctions::mean(&mut r, &data).unwrap();
921        assert_eq!(mean_result, 3.0);
922    }
923
924    #[test]
925    fn test_package_operations() {
926        let mut r = RIntegration::new().unwrap();
927
928        // Test loading base package
929        assert!(r.load_package("stats").is_ok());
930        assert!(r.get_loaded_packages().contains(&"stats".to_string()));
931
932        // Test package availability check
933        let is_available = r.is_package_available("stats").unwrap();
934        assert!(is_available);
935    }
936    */
937}