1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
//! DIMACS CNF Parser
//!
//! parser for DIMACS CNF files, returns a [`Formula`] struct
use crate::notation::Literal;
use crate::notation::Clause;
use crate::notation::Formula;
/// Parses a CNF file and returns a [`Formula`] struct
///
/// # Arguments
/// * `_buffer` - A string slice that holds the contents of the CNF file
///
/// # Examples
/// If the CNF file is in `/bin/problem.cnf`:
/// ```rust
/// use sat_rs::cnfparser::parse_cnf;
///
/// let buffer = include_str!("bin/problem.cnf");
/// let formula = parse_cnf(&buffer);
/// ```
pub fn parse_cnf(_buffer: &str) -> Result<Formula, String> {
let mut num_clauses: i32 = 0;
let mut num_vars: i32 = 0;
let mut clauses: Vec<Clause> = Vec::new();
// Iterate over lines in the buffer
for line in _buffer.lines() {
// Skip comments
if line.starts_with("c") {
continue;
} else if line.starts_with("p") {
// If the line starts with "p", then it contains the number of clauses and variables
let mut tokens = line.split_whitespace();
// Skip the first two tokens, i.e. "p" and "cnf" if the file is valid
for _ in 0..2 {
if tokens.next().is_none() {
// Handle the case where there are not enough tokens
return Err("Not enough tokens in the line".to_string());
}
}
// Get the number of variables
let num_vars_str = tokens.next().ok_or("Missing num_vars token")?;
num_vars = num_vars_str.parse::<i32>().map_err(|e| format!("Failed to parse num_vars: {}", e))?;
// Get the number of clauses
let num_clauses_str = tokens.next().ok_or("Missing num_clauses token")?;
num_clauses = num_clauses_str.parse::<i32>().map_err(|e| format!("Failed to parse num_clauses: {}", e))?;
} else {
// If the line does not start with "c" or "p", then it contains a clause
let mut clause: Clause = Clause::new();
// Iterate over tokens in the line
for token in line.split_whitespace() {
let _lit = token.parse::<i32>().unwrap();
if _lit == 0 {
// If the token is 0, then it is the end of the clause
break;
}
let literal = Literal{
value: _lit.abs(),
negated: _lit < 0
};
clause.literals.push(literal);
}
clauses.push(clause);
}
}
// Iterate over clauses and create a vector of unique literals
let mut literals: Vec<i32> = Vec::new();
for clause in &clauses {
for literal in &clause.literals {
if !literals.contains(&literal.value) {
literals.push(literal.value);
}
}
}
assert_eq!(num_clauses, clauses.len() as i32);
assert_eq!(num_vars, literals.len() as i32);
let formula: Formula = Formula {
clauses: clauses,
literals: literals,
num_clauses: num_clauses,
num_vars: num_vars,
};
Ok(formula)
}