Skip to main content

sklears_utils/
file_io.rs

1//! File I/O utilities for efficient data handling in machine learning workflows
2//!
3//! This module provides utilities for efficient file reading/writing, compression,
4//! format conversion, streaming I/O operations, and data serialization.
5
6use crate::{UtilsError, UtilsResult};
7use scirs2_core::ndarray::{Array1, Array2};
8use std::collections::HashMap;
9use std::fs::{File, OpenOptions};
10use std::io::{BufRead, BufReader, BufWriter, Read, Write};
11use std::path::Path;
12
13// ===== EFFICIENT FILE OPERATIONS =====
14
15/// Efficient file reader with buffering and memory management
16pub struct EfficientFileReader {
17    reader: BufReader<File>,
18    #[allow(dead_code)]
19    buffer_size: usize,
20}
21
22impl EfficientFileReader {
23    /// Create a new efficient file reader
24    pub fn new<P: AsRef<Path>>(path: P, buffer_size: Option<usize>) -> UtilsResult<Self> {
25        let file = File::open(path)
26            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to open file: {e}")))?;
27
28        let buffer_size = buffer_size.unwrap_or(8192);
29        let reader = BufReader::with_capacity(buffer_size, file);
30
31        Ok(Self {
32            reader,
33            buffer_size,
34        })
35    }
36
37    /// Read lines efficiently with iterator
38    pub fn read_lines(&mut self) -> impl Iterator<Item = UtilsResult<String>> + '_ {
39        std::io::BufRead::lines(&mut self.reader).map(|line| {
40            line.map_err(|e| UtilsError::InvalidParameter(format!("Failed to read line: {e}")))
41        })
42    }
43
44    /// Read fixed-size chunks
45    pub fn read_chunk(&mut self, size: usize) -> UtilsResult<Vec<u8>> {
46        let mut buffer = vec![0u8; size];
47        let bytes_read = self
48            .reader
49            .read(&mut buffer)
50            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to read chunk: {e}")))?;
51
52        buffer.truncate(bytes_read);
53        Ok(buffer)
54    }
55
56    /// Read all content efficiently
57    pub fn read_all(&mut self) -> UtilsResult<Vec<u8>> {
58        let mut content = Vec::new();
59        self.reader
60            .read_to_end(&mut content)
61            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to read file: {e}")))?;
62        Ok(content)
63    }
64
65    /// Read numerical data as arrays
66    pub fn read_array1(&mut self, delimiter: &str) -> UtilsResult<Array1<f64>> {
67        let mut line = String::new();
68        self.reader
69            .read_line(&mut line)
70            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to read line: {e}")))?;
71
72        let values: Result<Vec<f64>, _> = line
73            .trim()
74            .split(delimiter)
75            .filter(|s| !s.is_empty())
76            .map(|s| s.parse::<f64>())
77            .collect();
78
79        let values = values
80            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to parse numbers: {e}")))?;
81
82        Ok(Array1::from_vec(values))
83    }
84
85    /// Read 2D numerical data
86    pub fn read_array2(&mut self, delimiter: &str) -> UtilsResult<Array2<f64>> {
87        let mut rows = Vec::new();
88        let mut line = String::new();
89
90        while self.reader.read_line(&mut line).unwrap_or(0) > 0 {
91            if line.trim().is_empty() {
92                line.clear();
93                continue;
94            }
95
96            let values: Result<Vec<f64>, _> = line
97                .trim()
98                .split(delimiter)
99                .filter(|s| !s.is_empty())
100                .map(|s| s.parse::<f64>())
101                .collect();
102
103            let values = values.map_err(|e| {
104                UtilsError::InvalidParameter(format!("Failed to parse numbers: {e}"))
105            })?;
106
107            rows.push(values);
108            line.clear();
109        }
110
111        if rows.is_empty() {
112            return Err(UtilsError::EmptyInput);
113        }
114
115        let ncols = rows[0].len();
116        let nrows = rows.len();
117
118        // Verify all rows have the same length
119        for row in &rows {
120            if row.len() != ncols {
121                return Err(UtilsError::ShapeMismatch {
122                    expected: vec![ncols],
123                    actual: vec![row.len()],
124                });
125            }
126        }
127
128        let flat: Vec<f64> = rows.into_iter().flatten().collect();
129        Array2::from_shape_vec((nrows, ncols), flat)
130            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to create array: {e}")))
131    }
132}
133
134/// Efficient file writer with buffering
135pub struct EfficientFileWriter {
136    writer: BufWriter<File>,
137}
138
139impl EfficientFileWriter {
140    /// Create a new efficient file writer
141    pub fn new<P: AsRef<Path>>(path: P, buffer_size: Option<usize>) -> UtilsResult<Self> {
142        let file = OpenOptions::new()
143            .write(true)
144            .create(true)
145            .truncate(true)
146            .open(path)
147            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to create file: {e}")))?;
148
149        let buffer_size = buffer_size.unwrap_or(8192);
150        let writer = BufWriter::with_capacity(buffer_size, file);
151
152        Ok(Self { writer })
153    }
154
155    /// Append to existing file
156    pub fn append<P: AsRef<Path>>(path: P, buffer_size: Option<usize>) -> UtilsResult<Self> {
157        let file = OpenOptions::new()
158            .create(true)
159            .append(true)
160            .open(path)
161            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to open file: {e}")))?;
162
163        let buffer_size = buffer_size.unwrap_or(8192);
164        let writer = BufWriter::with_capacity(buffer_size, file);
165
166        Ok(Self { writer })
167    }
168
169    /// Write data with automatic flushing
170    pub fn write_data(&mut self, data: &[u8]) -> UtilsResult<()> {
171        self.writer
172            .write_all(data)
173            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write data: {e}")))?;
174        Ok(())
175    }
176
177    /// Write string lines
178    pub fn write_lines<I>(&mut self, lines: I) -> UtilsResult<()>
179    where
180        I: IntoIterator<Item = String>,
181    {
182        for line in lines {
183            writeln!(self.writer, "{line}")
184                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write line: {e}")))?;
185        }
186        Ok(())
187    }
188
189    /// Write array data
190    pub fn write_array1(&mut self, array: &Array1<f64>, delimiter: &str) -> UtilsResult<()> {
191        let line = array
192            .iter()
193            .map(|x| x.to_string())
194            .collect::<Vec<_>>()
195            .join(delimiter);
196
197        writeln!(self.writer, "{line}")
198            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write array: {e}")))?;
199        Ok(())
200    }
201
202    /// Write 2D array data
203    pub fn write_array2(&mut self, array: &Array2<f64>, delimiter: &str) -> UtilsResult<()> {
204        for row in array.outer_iter() {
205            let line = row
206                .iter()
207                .map(|x| x.to_string())
208                .collect::<Vec<_>>()
209                .join(delimiter);
210
211            writeln!(self.writer, "{line}")
212                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write row: {e}")))?;
213        }
214        Ok(())
215    }
216
217    /// Flush the buffer
218    pub fn flush(&mut self) -> UtilsResult<()> {
219        self.writer
220            .flush()
221            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to flush: {e}")))?;
222        Ok(())
223    }
224}
225
226// ===== COMPRESSION UTILITIES =====
227
228/// Simple compression utilities using built-in algorithms
229pub struct CompressionUtils;
230
231impl CompressionUtils {
232    /// Compress data using gzip
233    #[cfg(feature = "compression")]
234    pub fn compress_gzip(data: &[u8]) -> UtilsResult<Vec<u8>> {
235        oxiarc_deflate::gzip_compress(data, 6)
236            .map_err(|e| UtilsError::InvalidParameter(format!("Compression failed: {e}")))
237    }
238
239    /// Decompress gzip data
240    #[cfg(feature = "compression")]
241    pub fn decompress_gzip(data: &[u8]) -> UtilsResult<Vec<u8>> {
242        oxiarc_deflate::gzip_decompress(data)
243            .map_err(|e| UtilsError::InvalidParameter(format!("Decompression failed: {e}")))
244    }
245
246    /// Simple run-length encoding for sparse data
247    pub fn run_length_encode(data: &[u8]) -> Vec<(u8, usize)> {
248        if data.is_empty() {
249            return Vec::new();
250        }
251
252        let mut result = Vec::new();
253        let mut current_value = data[0];
254        let mut count = 1;
255
256        for &byte in &data[1..] {
257            if byte == current_value {
258                count += 1;
259            } else {
260                result.push((current_value, count));
261                current_value = byte;
262                count = 1;
263            }
264        }
265        result.push((current_value, count));
266        result
267    }
268
269    /// Decode run-length encoded data
270    pub fn run_length_decode(encoded: &[(u8, usize)]) -> Vec<u8> {
271        let mut result = Vec::new();
272        for &(value, count) in encoded {
273            result.extend(std::iter::repeat_n(value, count));
274        }
275        result
276    }
277}
278
279// ===== STREAMING I/O =====
280
281/// Streaming data processor for large files
282pub struct StreamProcessor<R: Read> {
283    reader: R,
284    chunk_size: usize,
285}
286
287impl<R: Read> StreamProcessor<R> {
288    /// Create a new stream processor
289    pub fn new(reader: R, chunk_size: usize) -> Self {
290        Self { reader, chunk_size }
291    }
292
293    /// Process data in chunks with a callback function
294    pub fn process_chunks<F>(&mut self, mut processor: F) -> UtilsResult<()>
295    where
296        F: FnMut(&[u8]) -> UtilsResult<()>,
297    {
298        let mut buffer = vec![0u8; self.chunk_size];
299
300        loop {
301            let bytes_read = self
302                .reader
303                .read(&mut buffer)
304                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to read chunk: {e}")))?;
305
306            if bytes_read == 0 {
307                break;
308            }
309
310            processor(&buffer[..bytes_read])?;
311        }
312
313        Ok(())
314    }
315
316    /// Process lines from a text stream
317    pub fn process_lines<F>(&mut self, mut processor: F) -> UtilsResult<()>
318    where
319        F: FnMut(&str) -> UtilsResult<()>,
320        R: BufRead,
321    {
322        let mut line = String::new();
323
324        loop {
325            line.clear();
326            let bytes_read = self
327                .reader
328                .read_line(&mut line)
329                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to read line: {e}")))?;
330
331            if bytes_read == 0 {
332                break;
333            }
334
335            processor(line.trim_end())?;
336        }
337
338        Ok(())
339    }
340}
341
342// ===== FORMAT CONVERSION =====
343
344/// Format conversion utilities
345pub struct FormatConverter;
346
347impl FormatConverter {
348    /// Convert CSV to structured data
349    pub fn csv_to_arrays<P: AsRef<Path>>(
350        path: P,
351        delimiter: char,
352        has_header: bool,
353    ) -> UtilsResult<(Option<Vec<String>>, Array2<f64>)> {
354        let mut reader = EfficientFileReader::new(path, None)?;
355        let mut lines = reader.read_lines();
356
357        let header = if has_header {
358            if let Some(line_result) = lines.next() {
359                let line = line_result?;
360                Some(
361                    line.split(delimiter)
362                        .map(|s| s.trim().to_string())
363                        .collect(),
364                )
365            } else {
366                return Err(UtilsError::EmptyInput);
367            }
368        } else {
369            None
370        };
371
372        let mut rows = Vec::new();
373        for line_result in lines {
374            let line = line_result?;
375            if line.trim().is_empty() {
376                continue;
377            }
378
379            let values: Result<Vec<f64>, _> = line
380                .split(delimiter)
381                .map(|s| s.trim().parse::<f64>())
382                .collect();
383
384            let values = values.map_err(|e| {
385                UtilsError::InvalidParameter(format!("Failed to parse CSV values: {e}"))
386            })?;
387
388            rows.push(values);
389        }
390
391        if rows.is_empty() {
392            return Err(UtilsError::EmptyInput);
393        }
394
395        let ncols = rows[0].len();
396        let nrows = rows.len();
397
398        for row in &rows {
399            if row.len() != ncols {
400                return Err(UtilsError::ShapeMismatch {
401                    expected: vec![ncols],
402                    actual: vec![row.len()],
403                });
404            }
405        }
406
407        let flat: Vec<f64> = rows.into_iter().flatten().collect();
408        let array = Array2::from_shape_vec((nrows, ncols), flat)
409            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to create array: {e}")))?;
410
411        Ok((header, array))
412    }
413
414    /// Convert arrays to CSV format
415    pub fn arrays_to_csv<P: AsRef<Path>>(
416        path: P,
417        data: &Array2<f64>,
418        header: Option<&[String]>,
419        delimiter: char,
420    ) -> UtilsResult<()> {
421        let mut writer = EfficientFileWriter::new(path, None)?;
422
423        if let Some(header) = header {
424            let header_line = header.join(&delimiter.to_string());
425            writeln!(writer.writer, "{header_line}").map_err(|e| {
426                UtilsError::InvalidParameter(format!("Failed to write header: {e}"))
427            })?;
428        }
429
430        for row in data.outer_iter() {
431            let line = row
432                .iter()
433                .map(|x| x.to_string())
434                .collect::<Vec<_>>()
435                .join(&delimiter.to_string());
436
437            writeln!(writer.writer, "{line}")
438                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write row: {e}")))?;
439        }
440
441        writer.flush()?;
442        Ok(())
443    }
444
445    /// Convert JSON-like key-value data
446    pub fn json_to_map(json_str: &str) -> UtilsResult<HashMap<String, serde_json::Value>> {
447        serde_json::from_str(json_str)
448            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to parse JSON: {e}")))
449    }
450
451    /// Convert map to JSON string
452    pub fn map_to_json(map: &HashMap<String, serde_json::Value>) -> UtilsResult<String> {
453        serde_json::to_string_pretty(map)
454            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to serialize JSON: {e}")))
455    }
456
457    /// Convert YAML string to map
458    #[cfg(feature = "yaml")]
459    pub fn yaml_to_map(yaml_str: &str) -> UtilsResult<HashMap<String, serde_json::Value>> {
460        let yaml_value: serde_yaml::Value = serde_yaml::from_str(yaml_str)
461            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to parse YAML: {e}")))?;
462
463        // Convert YAML value to JSON value for consistency
464        let json_str = serde_json::to_string(&yaml_value).map_err(|e| {
465            UtilsError::InvalidParameter(format!("Failed to convert YAML to JSON: {e}"))
466        })?;
467
468        Self::json_to_map(&json_str)
469    }
470
471    /// Convert map to YAML string
472    #[cfg(feature = "yaml")]
473    pub fn map_to_yaml(map: &HashMap<String, serde_json::Value>) -> UtilsResult<String> {
474        serde_yaml::to_string(map)
475            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to serialize YAML: {e}")))
476    }
477
478    /// Convert TOML string to map
479    #[cfg(feature = "toml_support")]
480    pub fn toml_to_map(toml_str: &str) -> UtilsResult<HashMap<String, serde_json::Value>> {
481        let toml_value: toml::Value = toml::from_str(toml_str)
482            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to parse TOML: {e}")))?;
483
484        // Convert TOML value to JSON value for consistency
485        let json_str = serde_json::to_string(&toml_value).map_err(|e| {
486            UtilsError::InvalidParameter(format!("Failed to convert TOML to JSON: {e}"))
487        })?;
488
489        Self::json_to_map(&json_str)
490    }
491
492    /// Convert map to TOML string
493    #[cfg(feature = "toml_support")]
494    pub fn map_to_toml(map: &HashMap<String, serde_json::Value>) -> UtilsResult<String> {
495        // Convert JSON values to TOML-compatible values
496        let toml_value = serde_json::from_str::<toml::Value>(&serde_json::to_string(map)?)
497            .map_err(|e| {
498                UtilsError::InvalidParameter(format!("Failed to convert to TOML value: {e}"))
499            })?;
500
501        toml::to_string_pretty(&toml_value)
502            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to serialize TOML: {e}")))
503    }
504
505    /// Convert XML string to simplified map structure
506    #[cfg(feature = "xml")]
507    pub fn xml_to_simple_map(xml_str: &str) -> UtilsResult<HashMap<String, String>> {
508        use quick_xml::escape::unescape;
509        use quick_xml::events::Event;
510        use quick_xml::Reader;
511
512        let mut reader = Reader::from_str(xml_str);
513        reader.config_mut().trim_text(true);
514
515        let mut result = HashMap::new();
516        let mut current_element = String::new();
517        let mut buf = Vec::new();
518
519        loop {
520            match reader.read_event_into(&mut buf) {
521                Ok(Event::Start(ref e)) => {
522                    current_element = String::from_utf8_lossy(e.name().as_ref()).to_string();
523                }
524                Ok(Event::Text(e)) if !current_element.is_empty() => {
525                    let raw_text = e.decode().map_err(|e| {
526                        UtilsError::InvalidParameter(format!("Failed to decode XML text: {}", e))
527                    })?;
528                    let text = unescape(&raw_text).map_err(|e| {
529                        UtilsError::InvalidParameter(format!("Failed to unescape XML text: {}", e))
530                    })?;
531                    result.insert(current_element.clone(), text.into_owned());
532                }
533                Ok(Event::Text(_)) => {
534                    // current_element is empty; skip this text node
535                }
536                Ok(Event::End(_)) => {
537                    current_element.clear();
538                }
539                Ok(Event::Eof) => break,
540                Err(e) => {
541                    return Err(UtilsError::InvalidParameter(format!(
542                        "Failed to parse XML: {}",
543                        e
544                    )));
545                }
546                _ => {}
547            }
548            buf.clear();
549        }
550
551        Ok(result)
552    }
553
554    /// Convert map to simple XML structure
555    #[cfg(feature = "xml")]
556    pub fn simple_map_to_xml(
557        map: &HashMap<String, String>,
558        root_name: &str,
559    ) -> UtilsResult<String> {
560        use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
561        use quick_xml::Writer;
562        use std::io::Cursor;
563
564        let mut writer = Writer::new(Cursor::new(Vec::new()));
565
566        // Write XML declaration
567        writer
568            .write_event(Event::Decl(quick_xml::events::BytesDecl::new(
569                "1.0",
570                Some("UTF-8"),
571                None,
572            )))
573            .map_err(|e| {
574                UtilsError::InvalidParameter(format!("Failed to write XML declaration: {e}"))
575            })?;
576
577        // Write root element start
578        writer
579            .write_event(Event::Start(BytesStart::new(root_name)))
580            .map_err(|e| {
581                UtilsError::InvalidParameter(format!("Failed to write root element: {e}"))
582            })?;
583
584        // Write map entries
585        for (key, value) in map {
586            writer
587                .write_event(Event::Start(BytesStart::new(key)))
588                .map_err(|e| {
589                    UtilsError::InvalidParameter(format!("Failed to write element start: {e}"))
590                })?;
591
592            writer
593                .write_event(Event::Text(BytesText::new(value)))
594                .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write text: {e}")))?;
595
596            writer
597                .write_event(Event::End(BytesEnd::new(key)))
598                .map_err(|e| {
599                    UtilsError::InvalidParameter(format!("Failed to write element end: {e}"))
600                })?;
601        }
602
603        // Write root element end
604        writer
605            .write_event(Event::End(BytesEnd::new(root_name)))
606            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to write root end: {e}")))?;
607
608        let result = writer.into_inner().into_inner();
609        String::from_utf8(result).map_err(|e| {
610            UtilsError::InvalidParameter(format!("Failed to convert XML to string: {e}"))
611        })
612    }
613
614    /// Enhanced JSON utilities for ML data structures
615    pub fn json_to_arrays(json_str: &str) -> UtilsResult<HashMap<String, Array2<f64>>> {
616        let data: HashMap<String, Vec<Vec<f64>>> = serde_json::from_str(json_str).map_err(|e| {
617            UtilsError::InvalidParameter(format!("Failed to parse JSON arrays: {e}"))
618        })?;
619
620        let mut result = HashMap::new();
621        for (key, matrix) in data {
622            if matrix.is_empty() {
623                continue;
624            }
625
626            let nrows = matrix.len();
627            let ncols = matrix[0].len();
628
629            // Verify all rows have the same length
630            for row in matrix.iter() {
631                if row.len() != ncols {
632                    return Err(UtilsError::ShapeMismatch {
633                        expected: vec![ncols],
634                        actual: vec![row.len()],
635                    });
636                }
637            }
638
639            let flat: Vec<f64> = matrix.into_iter().flatten().collect();
640            let array = Array2::from_shape_vec((nrows, ncols), flat).map_err(|e| {
641                UtilsError::InvalidParameter(format!("Failed to create array: {e}"))
642            })?;
643
644            result.insert(key, array);
645        }
646
647        Ok(result)
648    }
649
650    /// Convert arrays to JSON format for ML data
651    pub fn arrays_to_json(arrays: &HashMap<String, &Array2<f64>>) -> UtilsResult<String> {
652        let mut data = HashMap::new();
653
654        for (key, array) in arrays {
655            let matrix: Vec<Vec<f64>> = array.outer_iter().map(|row| row.to_vec()).collect();
656            data.insert(key.clone(), matrix);
657        }
658
659        serde_json::to_string_pretty(&data).map_err(|e| {
660            UtilsError::InvalidParameter(format!("Failed to serialize arrays to JSON: {e}"))
661        })
662    }
663}
664
665// ===== DATA SERIALIZATION =====
666
667/// Serialization utilities for ML data structures
668pub struct SerializationUtils;
669
670impl SerializationUtils {
671    /// Serialize array to binary format
672    pub fn serialize_array2(array: &Array2<f64>) -> UtilsResult<Vec<u8>> {
673        let shape = array.shape();
674        let mut data = Vec::new();
675
676        // Write shape information
677        data.extend_from_slice(&(shape[0] as u64).to_le_bytes());
678        data.extend_from_slice(&(shape[1] as u64).to_le_bytes());
679
680        // Write array data
681        for &value in array.iter() {
682            data.extend_from_slice(&value.to_le_bytes());
683        }
684
685        Ok(data)
686    }
687
688    /// Deserialize array from binary format
689    pub fn deserialize_array2(data: &[u8]) -> UtilsResult<Array2<f64>> {
690        if data.len() < 16 {
691            return Err(UtilsError::InvalidParameter(
692                "Insufficient data for array header".to_string(),
693            ));
694        }
695
696        // Read shape information
697        let nrows = u64::from_le_bytes([
698            data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
699        ]) as usize;
700
701        let ncols = u64::from_le_bytes([
702            data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
703        ]) as usize;
704
705        let expected_len = 16 + nrows * ncols * 8;
706        if data.len() != expected_len {
707            return Err(UtilsError::InvalidParameter(format!(
708                "Data length mismatch: expected {}, got {}",
709                expected_len,
710                data.len()
711            )));
712        }
713
714        // Read array data
715        let mut values = Vec::with_capacity(nrows * ncols);
716        for i in 0..(nrows * ncols) {
717            let start = 16 + i * 8;
718            let bytes = [
719                data[start],
720                data[start + 1],
721                data[start + 2],
722                data[start + 3],
723                data[start + 4],
724                data[start + 5],
725                data[start + 6],
726                data[start + 7],
727            ];
728            values.push(f64::from_le_bytes(bytes));
729        }
730
731        Array2::from_shape_vec((nrows, ncols), values)
732            .map_err(|e| UtilsError::InvalidParameter(format!("Failed to create array: {e}")))
733    }
734
735    /// Serialize to file
736    pub fn serialize_to_file<P: AsRef<Path>>(path: P, array: &Array2<f64>) -> UtilsResult<()> {
737        let data = Self::serialize_array2(array)?;
738        let mut writer = EfficientFileWriter::new(path, None)?;
739        writer.write_data(&data)?;
740        writer.flush()?;
741        Ok(())
742    }
743
744    /// Deserialize from file
745    pub fn deserialize_from_file<P: AsRef<Path>>(path: P) -> UtilsResult<Array2<f64>> {
746        let mut reader = EfficientFileReader::new(path, None)?;
747        let data = reader.read_all()?;
748        Self::deserialize_array2(&data)
749    }
750}
751
752#[allow(non_snake_case)]
753#[cfg(test)]
754mod tests {
755    use super::*;
756    use std::io::Cursor;
757    use tempfile::NamedTempFile;
758
759    #[test]
760    fn test_efficient_file_reader_writer() {
761        let temp_file = NamedTempFile::new().expect("operation should succeed");
762        let path = temp_file.path();
763
764        // Write test data
765        let mut writer = EfficientFileWriter::new(path, None).expect("operation should succeed");
766        writer
767            .write_lines(vec!["line1".to_string(), "line2".to_string()])
768            .expect("operation should succeed");
769        writer.flush().expect("operation should succeed");
770        drop(writer);
771
772        // Read test data
773        let mut reader = EfficientFileReader::new(path, None).expect("operation should succeed");
774        let lines: Result<Vec<_>, _> = reader.read_lines().collect();
775        let lines = lines.expect("operation should succeed");
776
777        assert_eq!(lines.len(), 2);
778        assert_eq!(lines[0], "line1");
779        assert_eq!(lines[1], "line2");
780    }
781
782    #[test]
783    fn test_array_io() {
784        let temp_file = NamedTempFile::new().expect("operation should succeed");
785        let path = temp_file.path();
786
787        // Create test array
788        let original = Array2::from_shape_vec((2, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
789            .expect("operation should succeed");
790
791        // Write as CSV
792        FormatConverter::arrays_to_csv(path, &original, None, ',')
793            .expect("operation should succeed");
794
795        // Read back as CSV
796        let (header, loaded) =
797            FormatConverter::csv_to_arrays(path, ',', false).expect("operation should succeed");
798        assert!(header.is_none());
799        assert_eq!(original.shape(), loaded.shape());
800        assert!((original - loaded).mapv(f64::abs).sum() < 1e-10);
801    }
802
803    #[test]
804    fn test_serialization() {
805        let original = Array2::from_shape_vec((2, 3), vec![1.1, 2.2, 3.3, 4.4, 5.5, 6.6])
806            .expect("operation should succeed");
807
808        // Serialize and deserialize
809        let serialized =
810            SerializationUtils::serialize_array2(&original).expect("operation should succeed");
811        let deserialized =
812            SerializationUtils::deserialize_array2(&serialized).expect("operation should succeed");
813
814        assert_eq!(original.shape(), deserialized.shape());
815        assert!((original - deserialized).mapv(f64::abs).sum() < 1e-10);
816    }
817
818    #[test]
819    fn test_compression() {
820        let data = b"Hello, World! This is a test string for compression.";
821
822        let encoded = CompressionUtils::run_length_encode(data);
823        let decoded = CompressionUtils::run_length_decode(&encoded);
824
825        assert_eq!(data.to_vec(), decoded);
826    }
827
828    #[test]
829    fn test_stream_processor() {
830        let data = b"chunk1chunk2chunk3";
831        let cursor = Cursor::new(data);
832        let mut processor = StreamProcessor::new(cursor, 6);
833
834        let mut chunks = Vec::new();
835        processor
836            .process_chunks(|chunk| {
837                chunks.push(chunk.to_vec());
838                Ok(())
839            })
840            .expect("operation should succeed");
841
842        assert_eq!(chunks.len(), 3);
843        assert_eq!(&chunks[0], b"chunk1");
844        assert_eq!(&chunks[1], b"chunk2");
845        assert_eq!(&chunks[2], b"chunk3");
846    }
847
848    #[test]
849    fn test_format_conversion() {
850        let temp_file = NamedTempFile::new().expect("operation should succeed");
851        let path = temp_file.path();
852
853        // Create CSV content with only numeric data
854        std::fs::write(path, "age,score\n25,95.5\n30,87.2").expect("operation should succeed");
855
856        let (header, data) =
857            FormatConverter::csv_to_arrays(path, ',', true).expect("operation should succeed");
858
859        assert!(header.is_some());
860        let header = header.expect("operation should succeed");
861        assert_eq!(header, vec!["age", "score"]);
862
863        assert_eq!(data.shape(), &[2, 2]);
864        assert!((data[[0, 0]] - 25.0).abs() < 1e-10);
865        assert!((data[[0, 1]] - 95.5).abs() < 1e-10);
866        assert!((data[[1, 0]] - 30.0).abs() < 1e-10);
867        assert!((data[[1, 1]] - 87.2).abs() < 1e-10);
868    }
869
870    #[test]
871    fn test_enhanced_json_arrays() {
872        // Test JSON to arrays conversion
873        let json_data = r#"
874        {
875            "features": [[1.0, 2.0], [3.0, 4.0]],
876            "targets": [[5.0], [6.0]]
877        }"#;
878
879        let arrays = FormatConverter::json_to_arrays(json_data).expect("operation should succeed");
880
881        assert_eq!(arrays.len(), 2);
882        assert!(arrays.contains_key("features"));
883        assert!(arrays.contains_key("targets"));
884
885        let features = &arrays["features"];
886        assert_eq!(features.shape(), &[2, 2]);
887        assert!((features[[0, 0]] - 1.0).abs() < 1e-10);
888        assert!((features[[1, 1]] - 4.0).abs() < 1e-10);
889
890        // Test arrays to JSON conversion
891        let mut array_refs = HashMap::new();
892        array_refs.insert("features".to_string(), features);
893
894        let json_result =
895            FormatConverter::arrays_to_json(&array_refs).expect("operation should succeed");
896        assert!(json_result.contains("features"));
897        assert!(json_result.contains("1.0"));
898    }
899
900    #[test]
901    #[cfg(feature = "yaml")]
902    fn test_yaml_conversion() {
903        let yaml_data = r#"
904        name: test
905        value: 42
906        nested:
907          key: "hello"
908        "#;
909
910        let map = FormatConverter::yaml_to_map(yaml_data).expect("operation should succeed");
911        assert!(map.contains_key("name"));
912        assert!(map.contains_key("value"));
913
914        let yaml_result = FormatConverter::map_to_yaml(&map).expect("operation should succeed");
915        assert!(yaml_result.contains("name"));
916        assert!(yaml_result.contains("42"));
917    }
918
919    #[test]
920    #[cfg(feature = "toml_support")]
921    fn test_toml_conversion() {
922        let toml_data = r#"
923        name = "test"
924        value = 42
925
926        [nested]
927        key = "hello"
928        "#;
929
930        let map = FormatConverter::toml_to_map(toml_data).expect("operation should succeed");
931        assert!(map.contains_key("name"));
932        assert!(map.contains_key("value"));
933
934        let toml_result = FormatConverter::map_to_toml(&map).expect("operation should succeed");
935        assert!(toml_result.contains("name"));
936        assert!(toml_result.contains("42"));
937    }
938
939    #[test]
940    #[cfg(feature = "xml")]
941    fn test_xml_conversion() {
942        let xml_data = r#"<?xml version="1.0" encoding="UTF-8"?>
943        <root>
944            <name>test</name>
945            <value>42</value>
946        </root>"#;
947
948        let map = FormatConverter::xml_to_simple_map(xml_data).expect("operation should succeed");
949        assert!(map.contains_key("name"));
950        assert!(map.contains_key("value"));
951        assert_eq!(map["name"], "test");
952        assert_eq!(map["value"], "42");
953
954        let xml_result =
955            FormatConverter::simple_map_to_xml(&map, "root").expect("operation should succeed");
956        assert!(xml_result.contains("<name>test</name>"));
957        assert!(xml_result.contains("<value>42</value>"));
958    }
959}