1use crate::literal::{Polarity, VariableIdx};
3use crate::manager::SddManager;
4use crate::sdd::SddRef;
5
6use anyhow::{bail, Result};
7
8#[derive(Debug, PartialEq, Eq)]
10pub struct Preamble {
11 pub clauses: usize,
12 pub variables: usize,
13}
14
15#[derive(Debug, PartialEq, Eq)]
17pub(crate) struct Clause {
18 pub(crate) var_label_polarities: Vec<Polarity>,
19 pub(crate) var_label_indices: Vec<u32>,
20}
21
22impl Clause {
23 #[must_use]
24 pub(crate) fn to_sdd(&self, manager: &SddManager) -> SddRef {
25 let mut sdd = manager.contradiction();
26
27 for (idx, polarity) in self
28 .var_label_indices
29 .iter()
30 .zip(self.var_label_polarities.iter())
31 {
32 let lit = manager.literal_from_idx(VariableIdx(idx - 1), *polarity);
34 sdd = manager.disjoin(&sdd, &lit);
35 }
36
37 sdd
38 }
39}
40
41#[derive(PartialEq, Eq)]
43enum DimacsParserState {
44 Initialized,
45 PreambleParsed,
46 ParsingClauses,
47 Finished,
48}
49
50#[allow(clippy::module_name_repetitions)]
52pub struct DimacsParser<'a> {
53 reader: &'a mut dyn std::io::BufRead,
54 state: DimacsParserState,
55}
56
57impl<'a> DimacsParser<'a> {
58 #[must_use]
59 pub fn new(reader: &'a mut dyn std::io::BufRead) -> Self {
60 DimacsParser {
61 state: DimacsParserState::Initialized,
62 reader,
63 }
64 }
65
66 pub fn parse_preamble(&mut self) -> Result<Preamble> {
76 if self.state != DimacsParserState::Initialized {
77 bail!("preamble already parsed");
78 }
79
80 while match peek_line(self.reader).get(0..1) {
82 None => bail!("preamble is missing a problem line"),
83 Some(first_char) => first_char == "c",
84 } {
85 let mut buf = String::new();
86 match self.reader.read_line(&mut buf) {
87 Ok(..) => {}
88 Err(err) => bail!("could not parse preamble comment: {err}"),
89 }
90 }
91
92 let mut problem = String::new();
93 match self.reader.read_line(&mut problem) {
94 Ok(..) => self.parse_problem_line(problem.trim()),
95 Err(err) => bail!("could not parse problem line: {err}"),
96 }
97 }
98
99 pub(crate) fn parse_next_clause(&mut self) -> Result<Option<Clause>> {
101 assert!(self.state != DimacsParserState::Initialized);
102
103 if self.state == DimacsParserState::Finished {
104 return Ok(None);
105 }
106
107 let mut clause = String::new();
108 match self.reader.read_line(&mut clause) {
109 Ok(read) => {
110 if read == 0 {
111 self.state = DimacsParserState::Finished;
112 Ok(None)
113 } else {
114 self.state = DimacsParserState::ParsingClauses;
115 let clause = clause.trim().to_owned();
116 if clause == "%" {
117 let mut zero = String::new();
118 let _ = self.reader.read_line(&mut zero);
119 if zero.trim() == "0" {
120 return Ok(None);
121 }
122 bail!("expected '0' after '%' but found '{zero}' instead");
123 }
124
125 DimacsParser::parse_clause_line(&clause)
126 }
127 }
128 Err(err) => bail!("could not parse clause: {err}"),
129 }
130 }
131
132 fn parse_problem_line(&mut self, line: &str) -> Result<Preamble> {
133 let items: Vec<_> = line
134 .split(' ')
135 .filter(|element| !element.trim().is_empty())
136 .collect();
137 if items.len() != 4 {
138 bail!("problem line must contain exactly 4 fields: 'p cnf VARIABLES CLAUSES'");
139 }
140
141 if *items.first().unwrap() != "p" {
142 bail!("first field of problem line must be 'p'");
143 }
144
145 if *items.get(1).unwrap() != "cnf" {
146 bail!("second field of problem line must be 'cnf'");
147 }
148
149 let variables = match items.get(2).unwrap().parse::<usize>() {
150 Ok(variables) => variables,
151 Err(err) => bail!("could not parse number of variables: {err}"),
152 };
153
154 let clauses = match items.get(3).unwrap().parse::<usize>() {
155 Ok(variables) => variables,
156 Err(err) => bail!("could not parse number of clauses: {err}"),
157 };
158
159 self.state = DimacsParserState::PreambleParsed;
160 Ok(Preamble { clauses, variables })
161 }
162
163 fn parse_clause_line(line: &str) -> Result<Option<Clause>> {
164 let tokens: Vec<_> = line
165 .split(' ')
166 .filter(|token| *token != "0" && token.trim() != "")
167 .collect();
168
169 let literals: Vec<_> = tokens
170 .iter()
171 .map(|variable_idx| match variable_idx.trim().parse::<i32>() {
172 Err(err) => bail!("literal '{variable_idx}' is invalid: {err}"),
173 Ok(idx) => Ok((Polarity::from(!variable_idx.starts_with('-')), idx)),
174 })
175 .collect();
176
177 if literals.is_empty() {
179 return Ok(None);
180 }
181
182 let mut clause = Clause {
183 var_label_indices: Vec::new(),
184 var_label_polarities: Vec::new(),
185 };
186
187 for literal in &literals {
188 match literal {
189 Ok((polarity, idx)) => {
190 clause.var_label_indices.push(idx.unsigned_abs());
191 clause.var_label_polarities.push(*polarity);
192 }
193 Err(err) => bail!("could not parse clause: {err}"),
194 }
195 }
196
197 Ok(Some(clause))
198 }
199}
200
201#[must_use]
202fn peek_line(reader: &mut dyn std::io::BufRead) -> &str {
203 std::str::from_utf8(reader.fill_buf().unwrap()).unwrap()
204}
205
206#[cfg(test)]
207mod test {
208 use pretty_assertions::assert_eq;
209
210 use std::io::BufReader;
211
212 use super::{DimacsParser, Preamble};
213 use crate::{literal::Polarity, manager::dimacs::Clause};
214
215 fn collect_clauses(dimacs: &mut DimacsParser) -> Vec<Clause> {
216 let mut clauses = Vec::new();
217
218 loop {
219 match dimacs.parse_next_clause() {
220 Ok(Some(clause)) => clauses.push(clause),
221 Ok(None) => break,
222 Err(err) => panic!("{err}"),
223 }
224 }
225
226 clauses
227 }
228
229 #[test]
230 fn dimacs_ok() {
231 let contents = "c Example CNF format file
232c
233p cnf 4 3
2341 3 -4 0
2354 0
2362 -3 0";
237 let mut reader = BufReader::new(contents.as_bytes());
238 let mut dimacs = DimacsParser::new(&mut reader);
239
240 assert_eq!(
241 dimacs.parse_preamble().unwrap(),
242 Preamble {
243 variables: 4,
244 clauses: 3
245 }
246 );
247
248 let clauses = collect_clauses(&mut dimacs);
249
250 assert_eq!(
251 clauses,
252 vec![
253 Clause {
254 var_label_indices: vec![1, 3, 4],
255 var_label_polarities: vec![
256 Polarity::Positive,
257 Polarity::Positive,
258 Polarity::Negative
259 ],
260 },
261 Clause {
262 var_label_indices: vec![4],
263 var_label_polarities: vec![Polarity::Positive,],
264 },
265 Clause {
266 var_label_indices: vec![2, 3],
267 var_label_polarities: vec![Polarity::Positive, Polarity::Negative],
268 },
269 ]
270 );
271 }
272
273 #[test]
274 fn preamble_with_whitespace() {
275 let contents = "c Example CNF format file
276c
277p cnf 4 3
2781 3 -4 0
2794 0 2
280-3 0";
281 let mut reader = BufReader::new(contents.as_bytes());
282 let mut dimacs = DimacsParser::new(&mut reader);
283
284 assert_eq!(
285 dimacs.parse_preamble().unwrap(),
286 Preamble {
287 variables: 4,
288 clauses: 3
289 }
290 );
291 }
292
293 #[test]
294 fn clauses_with_whitespace() {
295 let contents = "c Example CNF format file
296c
297p cnf 4 2
2981 3 -4 0
299 4 0
300";
301 let mut reader = BufReader::new(contents.as_bytes());
302 let mut dimacs = DimacsParser::new(&mut reader);
303
304 assert_eq!(
305 dimacs.parse_preamble().unwrap(),
306 Preamble {
307 variables: 4,
308 clauses: 2
309 }
310 );
311 }
312
313 #[test]
314 fn trailing_eof_syntax() {
315 let contents = "c Example CNF format file
317c
318p cnf 4 2
3191 3 -4 0
3204 0
321%
3220
323";
324 let mut reader = BufReader::new(contents.as_bytes());
325 let mut dimacs = DimacsParser::new(&mut reader);
326
327 assert_eq!(
328 dimacs.parse_preamble().unwrap(),
329 Preamble {
330 variables: 4,
331 clauses: 2
332 }
333 );
334
335 let clauses = collect_clauses(&mut dimacs);
336
337 assert_eq!(
338 clauses,
339 vec![
340 Clause {
341 var_label_indices: vec![1, 3, 4],
342 var_label_polarities: vec![
343 Polarity::Positive,
344 Polarity::Positive,
345 Polarity::Negative
346 ],
347 },
348 Clause {
349 var_label_indices: vec![4],
350 var_label_polarities: vec![Polarity::Positive],
351 },
352 ]
353 );
354 }
355
356 #[test]
357 fn trailing_zeros() {
358 let contents = "c Example CNF format file
359c
360p cnf 4 2
3611 3 -4 0
3624 0
363";
364 let mut reader = BufReader::new(contents.as_bytes());
365 let mut dimacs = DimacsParser::new(&mut reader);
366
367 assert_eq!(
368 dimacs.parse_preamble().unwrap(),
369 Preamble {
370 variables: 4,
371 clauses: 2
372 }
373 );
374
375 let clauses = collect_clauses(&mut dimacs);
376
377 assert_eq!(
378 clauses,
379 vec![
380 Clause {
381 var_label_indices: vec![1, 3, 4],
382 var_label_polarities: vec![
383 Polarity::Positive,
384 Polarity::Positive,
385 Polarity::Negative
386 ],
387 },
388 Clause {
389 var_label_indices: vec![4],
390 var_label_polarities: vec![Polarity::Positive],
391 },
392 ]
393 );
394 }
395}