use alloc::format;
use alloc::string::String;
use crate::sat::literal::Lit;
use crate::sat::solver::Solver;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct DimacsError(pub String);
impl core::fmt::Display for DimacsError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "DIMACS parse error: {}", self.0)
}
}
pub fn parse(input: &str) -> Result<Solver, DimacsError> {
let mut solver = Solver::new();
let mut clause: alloc::vec::Vec<Lit> = alloc::vec::Vec::new();
for line in input.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('c') || line.starts_with('p') {
continue; }
for tok in line.split_whitespace() {
let n: i64 = tok
.parse()
.map_err(|_| DimacsError(format!("invalid token {tok:?}")))?;
if n == 0 {
solver.add_clause(&clause);
clause.clear();
} else {
let var = (n.unsigned_abs() - 1) as u32;
clause.push(Lit::new(var, n < 0));
}
}
}
if !clause.is_empty() {
solver.add_clause(&clause);
}
Ok(solver)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sat::solver::SatResult;
#[test]
fn parses_and_solves_sat() {
let cnf = "\
c a small satisfiable instance
p cnf 3 2
1 -2 0
2 3 0
";
let mut s = parse(cnf).unwrap();
assert_eq!(s.num_vars(), 3);
assert_eq!(s.solve(), SatResult::Sat);
}
#[test]
fn parses_and_solves_unsat() {
let cnf = "p cnf 1 2\n1 0\n-1 0\n";
let mut s = parse(cnf).unwrap();
assert_eq!(s.solve(), SatResult::Unsat);
}
#[test]
fn clauses_may_span_lines() {
let cnf = "1 2\n3 0 -1\n-2 0";
let mut s = parse(cnf).unwrap();
assert_eq!(s.solve(), SatResult::Sat);
}
#[test]
fn rejects_garbage() {
assert!(parse("1 foo 0").is_err());
}
}