use regex::Regex;
use fxhash::FxHashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use crate::errors::Error;
#[derive(Debug, Copy, Clone, Hash, Eq, Ord, PartialOrd, PartialEq)]
pub struct BinaryClause {
pub a: isize,
pub b: isize,
}
impl BinaryClause {
pub fn new(x: isize, y: isize) -> BinaryClause {
BinaryClause {
a: x.min(y),
b: x.max(y),
}
}
pub fn is_tautology(self) -> bool {
self.a == -self.b
}
pub fn is_unit(self) -> bool {
self.a == self.b
}
}
#[derive(Debug, Clone, Default)]
pub struct Weighed2Sat {
pub nb_vars: usize,
pub weights: FxHashMap<BinaryClause, isize>,
}
pub fn read_instance<P: AsRef<Path>>(fname: P) -> Result<Weighed2Sat, Error> {
let f = File::open(fname)?;
let f = BufReader::new(f);
let comment = Regex::new(r"^c\s.*$").unwrap();
let pb_decl = Regex::new(r"^p\s+wcnf\s+(?P<vars>\d+)\s+(?P<clauses>\d+)").unwrap();
let bin_decl = Regex::new(r"^(?P<w>-?\d+)\s+(?P<x>-?\d+)\s+(?P<y>-?\d+)\s+0").unwrap();
let unit_decl = Regex::new(r"^(?P<w>-?\d+)\s+(?P<x>-?\d+)-?\s+0").unwrap();
let mut instance: Weighed2Sat = Default::default();
for line in f.lines() {
let line = line?;
let line = line.trim();
if line.is_empty() {
continue;
}
if comment.is_match(line) {
continue;
}
if let Some(caps) = pb_decl.captures(line) {
let n = caps["vars"].to_string().parse::<usize>()?;
instance.nb_vars = n;
continue;
}
if let Some(caps) = bin_decl.captures(line) {
let w = caps["w"].to_string().parse::<isize>()?;
let x = caps["x"].to_string().parse::<isize>()?;
let y = caps["y"].to_string().parse::<isize>()?;
instance.weights.insert(BinaryClause::new(x, y), w);
continue;
}
if let Some(caps) = unit_decl.captures(line) {
let w = caps["w"].to_string().parse::<isize>()?;
let x = caps["x"].to_string().parse::<isize>()?;
instance.weights.insert(BinaryClause::new(x, x), w);
continue;
}
}
Ok(instance)
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[test]
fn test_load_from_file_using_trait() {
let fname = locate("debug2.wcnf");
let inst: Weighed2Sat = read_instance(fname).expect("Could not parse instance");
assert_eq!(inst.nb_vars, 3);
assert_eq!(inst.weights.len(), 4);
}
#[test]
fn test_is_unit() {
let cla = BinaryClause::new(-1, -1);
assert!(cla.is_unit())
}
fn locate(id: &str) -> PathBuf {
PathBuf::new()
.join(env!("CARGO_MANIFEST_DIR"))
.join("../resources/max2sat/")
.join(id)
}
}