use std::fs;
use std::path::Path;
use crate::SparseCol;
#[derive(Debug, Clone)]
pub struct SparseMatrixFile {
pub rows: usize,
pub cols: Vec<SparseCol>,
}
fn parse_usize(s: &str, what: &str) -> Result<usize, String> {
s.trim()
.parse::<usize>()
.map_err(|_| format!("failed to parse {} as usize: {}", what, s))
}
pub fn parse_sparse_columns_str(input: &str) -> Result<SparseMatrixFile, String> {
let mut lines = input.lines();
let header = lines
.next()
.ok_or_else(|| "missing header line; expected rows=<N>".to_string())?
.trim();
let rows = if let Some(rest) = header.strip_prefix("rows=") {
parse_usize(rest, "rows")?
} else {
return Err(format!(
"invalid header '{}'; expected first line to look like rows=<N>",
header
));
};
let mut cols: Vec<SparseCol> = Vec::new();
for (idx, raw_line) in lines.enumerate() {
let line = raw_line.trim();
if line.starts_with('#') {
continue;
}
if line.is_empty() {
cols.push(Vec::new());
continue;
}
let mut col: Vec<u32> = Vec::new();
for tok in line.split_whitespace() {
let r = tok
.parse::<usize>()
.map_err(|_| format!("line {}: failed to parse row index '{}'", idx + 2, tok))?;
if r >= rows {
return Err(format!(
"line {}: row index {} out of bounds for rows={}",
idx + 2,
r,
rows
));
}
col.push(r as u32);
}
col.sort_unstable();
col.dedup();
cols.push(col);
}
Ok(SparseMatrixFile { rows, cols })
}
pub fn load_sparse_columns_file<P: AsRef<Path>>(path: P) -> Result<SparseMatrixFile, String> {
let path_ref = path.as_ref();
let text = fs::read_to_string(path_ref)
.map_err(|e| format!("failed to read {}: {}", path_ref.display(), e))?;
parse_sparse_columns_str(&text)
}