1use crate::error::{DDAError, Result};
2
3pub fn parse_dda_output(content: &str) -> Result<Vec<Vec<f64>>> {
13 let mut matrix: Vec<Vec<f64>> = Vec::new();
14
15 for line in content.lines() {
17 if line.trim().is_empty() || line.trim().starts_with('#') {
19 continue;
20 }
21
22 let values: Vec<f64> = line
24 .split_whitespace()
25 .filter_map(|s| s.parse::<f64>().ok())
26 .filter(|v| v.is_finite())
27 .collect();
28
29 if !values.is_empty() {
30 matrix.push(values);
31 }
32 }
33
34 if matrix.is_empty() {
35 return Err(DDAError::ParseError("No valid data found in DDA output".to_string()));
36 }
37
38 log::info!("Loaded DDA output shape: {} rows × {} columns", matrix.len(), matrix[0].len());
39
40 if !matrix.is_empty() && matrix[0].len() >= 10 {
42 log::debug!("First row sample (first 10 values): {:?}", &matrix[0][0..10]);
43 }
44
45 if matrix[0].len() > 2 {
49 let mut after_skip: Vec<Vec<f64>> = Vec::new();
51 for row in &matrix {
52 let skipped: Vec<f64> = row.iter().skip(2).copied().collect();
53 after_skip.push(skipped);
54 }
55
56 log::debug!("After skipping first 2 columns: {} rows × {} columns", after_skip.len(), after_skip[0].len());
57
58 if !after_skip.is_empty() && after_skip[0].len() >= 10 {
60 log::debug!("After skip, first row (first 10 values): {:?}", &after_skip[0][0..10]);
61 }
62
63 let mut extracted: Vec<Vec<f64>> = Vec::new();
66
67 for row in &after_skip {
68 let mut row_values = Vec::new();
69 let mut col_idx = 0; while col_idx < row.len() {
71 row_values.push(row[col_idx]);
72 col_idx += 4;
73 }
74 extracted.push(row_values);
75 }
76
77 if !extracted.is_empty() && extracted[0].len() >= 5 {
79 log::debug!("First extracted row sample (first 5 values): {:?}", &extracted[0][0..5]);
80 }
81
82 if extracted.is_empty() || extracted[0].is_empty() {
83 return Err(DDAError::ParseError("No data after column extraction".to_string()));
84 }
85
86 let num_rows = extracted.len();
87 let num_cols = extracted[0].len();
88
89 log::info!("Extracted matrix shape: {} rows × {} columns (time windows × delays/scales)", num_rows, num_cols);
90
91 let mut transposed: Vec<Vec<f64>> = vec![Vec::new(); num_cols];
94
95 for (row_idx, row) in extracted.iter().enumerate() {
96 if row.len() != num_cols {
97 log::warn!("Row {} has {} columns, expected {}. Skipping this row.", row_idx, row.len(), num_cols);
98 continue;
99 }
100 for (col_idx, &value) in row.iter().enumerate() {
101 transposed[col_idx].push(value);
102 }
103 }
104
105 if transposed.is_empty() || transposed[0].is_empty() {
106 return Err(DDAError::ParseError("Transpose resulted in empty data".to_string()));
107 }
108
109 log::info!("Transposed to: {} channels × {} timepoints", transposed.len(), transposed[0].len());
110
111 Ok(transposed)
112 } else {
113 Ok(vec![matrix.into_iter().flatten().collect()])
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_parse_dda_output_basic() {
124 let content = "# Comment line\n\
125 1.0 2.0 3.0 4.0 5.0 6.0\n\
126 7.0 8.0 9.0 10.0 11.0 12.0\n";
127
128 let result = parse_dda_output(content).unwrap();
129 assert!(!result.is_empty());
130 }
131
132 #[test]
133 fn test_parse_empty_content() {
134 let content = "# Only comments\n# More comments\n";
135 let result = parse_dda_output(content);
136 assert!(result.is_err());
137 }
138}