pindakaas/
lib.rs

1//! Pindakaas is an encoding library that helps translate higher abstraction
2//! level constraints into conjunctive normal form (CNF), so that it can be used
3//! by Boolean satisfiability (SAT) solvers. Pindakaas supports constraints such
4//! as propositional logic, Boolean linear constraints (i.e. pseudo-Boolean (PB)
5//! constraints), and integer linear constraint. Importantly, Pindakaas
6//! normalizes and specializes the constraints to be able to use specialized
7//! encoding methods to an efficient solvable encoding. For example, making the
8//! distinction between “at most one”, cardinality, and general pseudo-Boolean
9//! constraints.
10//!
11//! ## Installation
12//!
13//! You can add the pindakaas crate to your project using Cargo:
14//!
15//! ```bash
16//! cargo add pindakaas
17//! ```
18//!
19//! _Note that Pindakaas is also available for Python. For more information,
20//! visit the [Python
21//! documentation](https://pindakaas.readthedocs.io/en/latest/)._
22//!
23//! ## CNF Modelling
24//!
25//! Like other SAT modelling libraries, Pindakaas includes the functionality to
26//! model at CNF level. For example, the following code snippet shows how to
27//! create an empty CNF formula, then create three variables, and add some
28//! circular clauses, and then print the formula in
29//! [DIMACS](https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html)
30//! format.
31//!
32//! ```rust
33//! use pindakaas::{ClauseDatabaseTools, Cnf};
34//!
35//! let mut f = Cnf::default();
36//! let (x, y, z) = f.new_lits();
37//! f.add_clause([!x, y]);
38//! f.add_clause([!y, z]);
39//! f.add_clause([!z, x]);
40//!
41//! assert_eq!(f.to_string(), "p cnf 3 3\n-1 2 0\n-2 3 0\n-3 1 0\n");
42//! ```
43//!
44//! Note that the `!` operator is used to negate a literal.
45//!
46//! It is also possible to load a CNF formula for a DIMACS formatted file using
47//! the [`Cnf::from_file`] method, and to write it to a DIMACS file using the
48//! [`Cnf::to_file`] method.
49//!
50//! ## Using SAT solvers
51//!
52//! In the [`solver`] module, we provide access to several competitive SAT
53//! solvers, such as [CaDiCaL](https://github.com/arminbiere/cadical) and
54//! [Kissat](https://github.com/arminbiere/kissat). We also provide several
55//! common solver traits, such as [`solver::Solver`], such that solvers can be
56//! easily switched.
57//!
58//! To, for example, show that only two solutions exists for the formula in the
59//! previous section using CaDiCaL, we can use the following fragment.
60//!
61//! ```rust
62//! # use pindakaas::{ClauseDatabaseTools, Cnf};
63//! use pindakaas::{
64//!     solver::{cadical::Cadical, SolveResult, Solver},
65//!     Valuation,
66//! };
67//!
68//! # let mut f = Cnf::default();
69//! # let (x, y, z) = f.new_lits();
70//! # f.add_clause([!x, y]);
71//! # f.add_clause([!y, z]);
72//! # f.add_clause([!z, x]);
73//!
74//! let mut slv = Cadical::from(&f);
75//! let mut solns = 0;
76//! while let SolveResult::Satisfied(sol) = slv.solve() {
77//!     solns += 1;
78//!     slv.add_clause([x,y,z].map(|l| if sol.value(l) { !l } else { l }));
79//! }
80//!
81//! assert_eq!(solns, 2);
82//! ```
83//!
84//! If we had wanted to use Kissat instead of CaDiCaL, we would have only had to
85//! add a the `use` statement for [`solver::kissat::Kissat`], and use
86//! `Kissat::from(&f)`.
87//!
88//! In either case, it is also not required to start from a [`Cnf`] instance.
89//! Both [`Cnf`] and [`Solver`](solver::Solver) instances implement the
90//! [`ClauseDatabase`] trait, and can often be used interchangeably.
91//!
92//! _Note that not all solvers are available by default. To minimize upstream
93//! dependencies, each solver has its own feature flag. So enable any of the
94//! following features if you want to enable additional solvers._
95//!
96//! - `cadical` (enabled by default) - enables the use of the [CaDiCaL](https://github.com/arminbiere/cadical)
97//!   solver, available as [`Cadical`](solver::cadical::Cadical) in the
98//!   [`solver::cadical`] module.
99//! - `intel_sat` - enables the use of the [Intel SAT](https://github.com/alexander-nadel/intel_sat_solver)
100//!   solver, available as [`IntelSat`](solver::intel_sat::IntelSat) in the
101//!   [`solver::intel_sat`] module.
102//! - `kissat` - enables the use of the [Kissat](https://github.com/arminbiere/kissat)
103//!   solver, available as [`Kissat`](solver::kissat::Kissat) in the
104//!   [`solver::kissat`] module.
105//! - `libloading` - enables the [`solver::libloading`] module, which allows
106//!   runtime loading of dynamically loaded libraries (DLLs) that implement the
107//!   IPASIR interface.
108//! - `splr` - enables implementation of the pindakaas common solver traits for
109//!   the [SPLR](https://github.com/shnarazk/splr) solver, available in its own
110//!   crate: [`splr::Solver`].
111//!
112//! ## Proposition Logic and [`Encoder`]s
113//!
114//! The first abstraction that Pindakaas provides from modelling using CNF, is
115//! to allow the use of constraint based on propositional logic. This makes it
116//! easy to express most logic based constraints. In Pindakaas, propositional
117//! logic is represented using [`Formula`]. An easy way to create [`Formula`]
118//! instances is to use the `&`, `|`, and `^` operators, which will
119//! create[`Formula::And`], [`Formula::Or`], and[`Formula::Xor`] instances,
120//! respectively. Other, more complex, propositional logic constructs, such as
121//! [`Formula::IfThenElse`] and [`Formula::Equiv`], must be constructed
122//! explicitly.
123//!
124//! A [`Formula`] can be used as a constraint, and as such it must be encoded
125//! into a CNF formula. In Pindakaas types implement the [`Encoder`] to
126//! translate constraint types into CNF formulas. For [`Formula`], it is
127//! [`TseitinEncoder`](propositional_logic::TseitinEncoder) that implements the
128//! [`Encoder`] trait. The following fragment shows how we create two
129//! [`Formula`] instances and encode them to CNF using the
130//! [`TseitinEncoder`](propositional_logic::TseitinEncoder).
131//!
132//! ```rust
133//! use pindakaas::{
134//!     propositional_logic::{Formula, TseitinEncoder},
135//!     ClauseDatabaseTools, Cnf,
136//! };
137//!
138//! let mut f = Cnf::default();
139//! let (x, y, z) = f.new_lits();
140//! let p = (x ^ y) | z;
141//! let q = Formula::IfThenElse {
142//!     cond: Formula::Atom(z).into(),
143//!     then: Formula::Atom(x).into(),
144//!     els: Formula::Atom(y).into(),
145//! };
146//!
147//! f.encode(&p, &TseitinEncoder);
148//! f.encode(&q, &TseitinEncoder);
149//! assert_eq!(f.num_clauses(), 7);
150//! ```
151//!
152//! ## Boolean and Integer Linear Constraints
153//!
154//! The most important feature of Pindakaas is its ability to encode Boolean and
155//! integer linear constraints into CNF formulas. This provides the ability to
156//! model and solve a wide range of problems. To model a linear constraint, we
157//! start by creating linear expressions, represented using [`BoolLinExp`]. We
158//! can use standard operators, such as `+` and `-`, to add [`Lit`]s together,
159//! or `*` to multiply them by a constant. (See the [`BoolLinExp`] documentation
160//! for advanced operations on expressions.) Additionally, integer variables
161//! (decided using Boolean variables) can be added to the linear expression when
162//! they're represented as a [`IntEncoding`].
163//!
164//! [`BoolLinExp`] can be turned into a constraint using the
165//! [`BoolLinear::new`](bool_linear::BoolLinear::new) method. It takes the
166//! linear expression as the left hand side, then a
167//! [`Comparator`](bool_linear::Comparator), and then a constant as the right
168//! hand side.
169//!
170//! Before the constraint is encoded, it is first simplified, normalized, and
171//! specialized by the
172//! [`BoolLinAggregator::aggregate`](bool_linear::BoolLinAggregator::aggregate).
173//! The result of this is a constraint of the form
174//! [`BoolLinVariant`](bool_linear::BoolLinVariant). Depending on the form of
175//! the specialized constraint, the constraint can be encoded using different
176//! encoding methods. For example, if the constraint was found to be a “at most
177//! one” constraint, then it could use the
178//! [`BitwiseEncoder`](cardinality_one::BitwiseEncoder). However, we can always
179//! use general pseudo-Boolean encoders, such as the
180//! [`TotalizerEncoder`](bool_linear::TotalizerEncoder). Making the choice of
181//! encoding can be streamlined by using the
182//! [`StaticLinEncoder`](bool_linear::StaticLinEncoder), which makes a choice
183//! based on the constraint's variant.
184//!
185//! Additionally, the [`LinearEncoder`](bool_linear::LinearEncoder) is meant to
186//! help streamline the process of aggregating and encoding linear expressions.
187//! The following fragment shows the creation of a linear constraint and the
188//! usage of the [`LinearEncoder`](bool_linear::LinearEncoder) to encode it.
189//!
190//! ```rust
191//! use pindakaas::{
192//!     bool_linear::{
193//!         BoolLinear, BoolLinAggregator, Comparator, LinearEncoder, StaticLinEncoder
194//!     },
195//!     Cnf, ClauseDatabaseTools
196//! };
197//!
198//! let mut f = Cnf::default();
199//! let (x, y, z) = f.new_lits();
200//! let con = BoolLinear::new(x * 2 + y * 3 + z * 2, Comparator::LessEq, 2);
201//!
202//! // Use default encoders and aggregator options
203//! let lin_enc: StaticLinEncoder = StaticLinEncoder::default();
204//! let enc = LinearEncoder::new(lin_enc, BoolLinAggregator::default());
205//!
206//! f.encode(&con, &enc);
207//! assert_eq!(f.num_clauses(), 2); // (!x & !z) & !y
208//! ```
209//!
210//! ## Citation
211//!
212//! If you want to cite Pindakaas please use our general software
213//! citation, in addition to any citation to a specific version or paper:
214//!
215//! ```biblatex
216//! @software{Pindakaas,
217//! author = {Bierlee, Hendrik and Dekker, Jip J.},
218//! license = {MPL-2.0},
219//! title = {{Pindakaas}},
220//! url = {https://doi.org/10.5281/zenodo.10851855},
221//! doi = {10.5281/zenodo.10851855},
222//! }
223//! ```
224//!
225//! Note that you might have to use `misc` instead of `software`, if your system
226//! does not support `software` as a type.
227//!
228//! ## Acknowledgements
229//!
230//! This research was partially funded by the Australian Government through the
231//! Australian Research Council Industrial Transformation Training Centre in
232//! Optimisation Technologies, Integrated Methodologies, and Applications
233//! (OPTIMA), Project ID IC200100009.
234
235pub mod bool_linear;
236pub mod cardinality;
237pub mod cardinality_one;
238pub(crate) mod helpers;
239mod integer;
240pub mod propositional_logic;
241pub mod solver;
242mod sorted;
243#[cfg(any(feature = "tracing", test))]
244pub mod trace;
245
246use std::{
247	clone::Clone,
248	cmp::{Eq, Ordering},
249	error::Error,
250	fmt::{self, Display},
251	fs::File,
252	hash::Hash,
253	io::{self, BufRead, BufReader, Write},
254	iter::{repeat_n, FusedIterator},
255	num::NonZeroI32,
256	ops::{Add, BitAnd, BitOr, BitXor, Bound, Mul, Not, RangeBounds, RangeInclusive},
257	path::Path,
258	slice,
259};
260
261use itertools::{traits::HomogeneousTuple, Itertools};
262
263pub use crate::helpers::AsDynClauseDatabase;
264use crate::{
265	bool_linear::BoolLinExp, helpers::subscript_number, propositional_logic::Formula,
266	solver::VarFactory,
267};
268
269/// A helper type used to represent a Boolean value that can be either a literal
270/// for a Boolean decision variable, or a constant Boolean value.
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
272#[expect(
273	variant_size_differences,
274	reason = "bool is 1 byte, but Lit will always require more"
275)]
276pub enum BoolVal {
277	/// A constant Boolean value.
278	Const(bool),
279	/// A literal for a Boolean decision variable.
280	Lit(Lit),
281}
282
283/// Checker is a trait implemented by types that represent constraints. The
284/// [`Checker::check`] methods checks whether an assignment (often referred to
285/// as a model) satisfies the constraint.
286pub trait Checker {
287	/// Check whether the constraint represented by the object is violated.
288	///
289	/// - The method returns [`Result::Ok`] when the assignment satisfies the
290	///   constraint,
291	/// - it returns [`Unsatisfiable`] when the assignment violates the
292	///   constraint
293	fn check<F: Valuation + ?Sized>(&self, value: &F) -> Result<(), Unsatisfiable>;
294}
295
296/// The `ClauseDatabase` trait is the common trait implemented by types that are
297/// used to manage the CNF encoding of constraints and contain their output.
298/// This trait can be used for all encoding methods in this library.
299///
300/// To satisfy the trait, the type must implement a
301/// [`Self::add_clause_from_slice`] method and a [`Self::new_var_range`] method.
302pub trait ClauseDatabase {
303	/// Add a clause to the `ClauseDatabase`. The database is allowed to return
304	/// [`Unsatisfiable`] when the collection of clauses has been *proven* to be
305	/// unsatisfiable. This is used as a signal to the encoder that any
306	/// subsequent encoding effort can be abandoned.
307	fn add_clause_from_slice(&mut self, clause: &[Lit]) -> Result;
308	/// Method to be used to receive a new Boolean variable that can be used in
309	/// the encoding of a problem or constraint.
310	fn new_var_range(&mut self, len: usize) -> VarRange;
311}
312
313/// A trait automatically implemented for types that implement
314/// [`ClauseDatabase`] providing a variety of utility methods that make it
315/// easier to write common clause encoding patterns.
316pub trait ClauseDatabaseTools: ClauseDatabase {
317	/// Add a clause, given as any to the `ClauseDatabase`. The database is
318	/// allowed to return [`Unsatisfiable`] when the collection of clauses has
319	/// been *proven* to be unsatisfiable. This is used as a signal to the
320	/// encoder that any subsequent encoding effort can be abandoned.
321	fn add_clause<Iter>(&mut self, clause: Iter) -> Result
322	where
323		Iter: IntoIterator,
324		Iter::Item: Into<BoolVal>,
325	{
326		let result: Result<Vec<_>, ()> = clause
327			.into_iter()
328			.filter_map(|v| match v.into() {
329				BoolVal::Const(false) => None,         // Irrelevant literal
330				BoolVal::Const(true) => Some(Err(())), // Clause is already satisfied
331				BoolVal::Lit(lit) => Some(Ok(lit)),    // Add literal to clause
332			})
333			.collect();
334		match result {
335			Ok(clause) => {
336				let result = self.add_clause_from_slice(&clause);
337				#[cfg(any(feature = "tracing", test))]
338				{
339					tracing::info!(clause = ?&clause, fail = result.is_err(), "emit clause");
340				}
341				result
342			}
343			// Collecting revealed the clause was already satisfied
344			Err(()) => Ok(()),
345		}
346	}
347
348	/// Encode a constraint using the provided encoder.
349	fn encode<C, E>(&mut self, constraint: &C, encoder: &E) -> Result
350	where
351		C: ?Sized,
352		E: Encoder<Self, C> + ?Sized,
353	{
354		encoder.encode(self, constraint)
355	}
356
357	/// Create a new Boolean variable in the form of a positive literal.
358	fn new_lit(&mut self) -> Lit {
359		self.new_var().into()
360	}
361
362	/// Create multiple new Boolean literals and capture them in a tuple.
363	///
364	/// # Example
365	/// ```
366	/// # use pindakaas::{ClauseDatabaseTools, Cnf};
367	/// # let mut db = Cnf::default();
368	/// let (a, b, c) = db.new_lits();
369	/// ```
370	fn new_lits<T>(&mut self) -> T
371	where
372		T: HomogeneousTuple<Item = Lit>,
373	{
374		let range = self.new_var_range(T::num_items());
375		range.map(Lit::from).collect_tuple().unwrap()
376	}
377
378	#[cfg(any(feature = "tracing", test))]
379	#[inline]
380	/// Create a new Boolean variable in the form of a positive literal. The
381	/// given name is used when the variable is output by the tracer.
382	fn new_named_lit(&mut self, name: &str) -> Lit {
383		self.new_named_var(name).into()
384	}
385
386	#[cfg(any(feature = "tracing", test))]
387	#[inline]
388	/// Create a new Boolean variable that can be used in the encoding of a
389	/// problem. The given name is used when the variable is output by the
390	/// tracer.
391	fn new_named_var(&mut self, name: &str) -> Var {
392		let var = self.new_var();
393		tracing::info!(var = ?i32::from(var), label = name, "new variable");
394		var
395	}
396
397	/// Create a new Boolean variable that can be used in the encoding of a
398	/// problem or constraint.
399	fn new_var(&mut self) -> Var {
400		let mut range = self.new_var_range(1);
401		debug_assert_eq!(range.len(), 1);
402		range.next().unwrap()
403	}
404
405	/// Create multiple new Boolean variables and capture them in a tuple.
406	///
407	/// # Example
408	/// ```
409	/// # use pindakaas::{ClauseDatabaseTools, Cnf};
410	/// # let mut db = Cnf::default();
411	/// let (a, b, c) = db.new_vars();
412	/// ```
413	fn new_vars<T>(&mut self) -> T
414	where
415		T: HomogeneousTuple<Item = Var>,
416	{
417		let range = self.new_var_range(T::num_items());
418		range.collect_tuple().unwrap()
419	}
420
421	/// Create a [`ClauseDatabase`] wrapper that adds the given conditions to
422	/// each clause that it adds to the wrapped database.
423	///
424	/// Note that the wrapped database type itself implements the
425	/// [`ClauseDatabase`] trait.
426	fn with_conditions(&mut self, conditions: Vec<Lit>) -> impl ClauseDatabase + '_
427	where
428		Self: AsDynClauseDatabase,
429	{
430		struct ConditionalDatabase<'a> {
431			db: &'a mut dyn ClauseDatabase,
432			conditions: Vec<Lit>,
433		}
434
435		impl ClauseDatabase for ConditionalDatabase<'_> {
436			fn add_clause_from_slice(&mut self, clause: &[Lit]) -> Result {
437				let chain = self
438					.conditions
439					.iter()
440					.copied()
441					.chain(clause.iter().copied())
442					.collect_vec();
443				self.db.add_clause_from_slice(&chain)
444			}
445
446			fn new_var_range(&mut self, len: usize) -> VarRange {
447				self.db.new_var_range(len)
448			}
449		}
450
451		ConditionalDatabase {
452			db: self.as_mut_dyn(),
453			conditions,
454		}
455	}
456}
457
458/// A representation for Boolean formulas in conjunctive normal form.
459///
460/// It can be used to create formulas manually, to store the results from
461/// encoders, read formulas from a file, and write them to a file
462#[derive(Clone, Debug, Default)]
463pub struct Cnf {
464	/// The variable factory used by [`new_var`]
465	nvar: VarFactory,
466	/// The literals from *all* clauses
467	lits: Vec<Lit>,
468	/// The size *for each* clause
469	size: Vec<usize>,
470}
471
472#[derive(Debug, Clone)]
473/// An iterator over the clauses in a CNF formula.
474struct CnfIterator<'a> {
475	lits: &'a Vec<Lit>,
476	size: slice::Iter<'a, usize>,
477	index: usize,
478}
479
480/// Coeff is a type alias used for the number type used to represent the
481/// coefficients in constraints and expression.
482pub(crate) type Coeff = i64;
483
484enum Dimacs {
485	Cnf(Cnf),
486	Wcnf(Wcnf),
487}
488
489/// Encoder is the central trait implemented for all the encoding algorithms
490pub trait Encoder<Db: ClauseDatabase + ?Sized, Constraint: ?Sized> {
491	/// Encode the constraint into the given clausal database.
492	fn encode(&self, db: &mut Db, con: &Constraint) -> Result;
493}
494
495/// IntEncoding is a enumerated type use to represent an Boolean encoding of a
496/// integer variable within this library
497#[derive(Debug, Clone, PartialEq, Eq, Hash)]
498pub enum IntEncoding<'a> {
499	/// The Direct variant represents a integer variable encoded using domain
500	/// or direct encoding of an integer variable. Each given Boolean literal
501	/// represents whether the integer takes the associated value (i.e., X =
502	/// (first+i) ↔ vals\[i\]).
503	Direct {
504		/// The offset of the value of the encoded integer variable, i.e. the
505		/// value if the first literal is `true`.
506		first: Coeff,
507		/// The list of literals representing the each value of the integer
508		/// variable.
509		vals: &'a [Lit],
510	},
511	/// The Order variant represents a integer variable using an order
512	/// encoding. Each given Boolean literal represents whether the integer
513	/// is bigger than the associated value(i.e., X > (first+i) ↔ vals\[i\]).
514	Order {
515		/// The offset of the value of the encoded integer variable, i.e. the
516		/// value if no literal is `true`.
517		first: Coeff,
518		/// The list of literals representing the each value of the integer
519		/// variable.
520		vals: &'a [Lit],
521	},
522	/// The Log variant represents a integer variable using a two's complement
523	/// encoding. The sum of the Boolean literals multiplied by their
524	/// associated power of two represents value of the integer (i.e., X = ∑
525	/// 2ⁱ·bits\[i\]).
526	Log {
527		/// Whether the first bit is interpreted as a sign bit.
528		signed: bool,
529		/// The list of literals representing the each bit of the integer
530		/// variable.
531		bits: &'a [Lit],
532	},
533}
534
535#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
536/// Literal is type that can be use to represent Boolean decision variables and
537/// their negations
538pub struct Lit(NonZeroI32);
539
540/// Result is a type alias for [`std::result::Result`] that by default returns
541/// an empty value, or the [`Unsatisfiable`] error type.
542type Result<T = (), E = Unsatisfiable> = std::result::Result<T, E>;
543
544#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
545/// Unsatisfiable is an error type returned when the problem being encoded is
546/// found to be inconsistent.
547pub struct Unsatisfiable;
548
549/// A trait implemented by types that can be used to represent a solution/model
550pub trait Valuation {
551	/// Returns the valuation/truth-value for a given literal in the
552	/// current solution/model.
553	///
554	/// Note that the function can return None if the model/solution is
555	/// independent of the given literal.
556	fn value(&self, lit: Lit) -> bool;
557}
558
559#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
560/// A canonical implementation of a Boolean decision variable, independent of
561/// negation.
562pub struct Var(pub(crate) NonZeroI32);
563
564#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
565/// A continuous range of Boolean variables.
566///
567/// This is a representation that is used to represent a range of variables in a
568/// more compact way.
569pub struct VarRange {
570	start: Var,
571	end: Var,
572}
573
574/// A representation for a weighted CNF formula
575///
576/// Same as CNF, but every clause has an optional weight. Otherwise, it is a
577/// hard clause.
578#[derive(Clone, Debug, Default)]
579pub struct Wcnf {
580	/// The CNF formula
581	cnf: Cnf,
582	/// The weight for every clause
583	weights: Vec<Option<Coeff>>,
584	// TODO this can be optimised, for example by having all weighted clauses at the start/end
585}
586
587/// Internal function used to parse a file in the (weighted) DIMACS format.
588///
589/// This function is used by `Cnf::from_str` and `Wcnf::from_str`.
590fn parse_dimacs_file<const WEIGHTED: bool>(path: &Path) -> Result<Dimacs, io::Error> {
591	let file = File::open(path)?;
592	let mut had_header = false;
593
594	let mut wcnf = Wcnf::default();
595
596	let mut cl: Vec<Lit> = Vec::new();
597	let mut top: Option<Coeff> = None;
598
599	for line in BufReader::new(file).lines() {
600		match line {
601			Ok(line) if line.is_empty() || line.starts_with('c') => (),
602			Ok(line) if had_header => {
603				for seg in line.split(' ') {
604					if WEIGHTED {
605						if let Ok(weight) = seg.parse::<Coeff>() {
606							wcnf.weights.push(match weight.cmp(&top.unwrap()) {
607								Ordering::Less => Some(weight),
608								Ordering::Equal => None,
609								Ordering::Greater => panic!(
610								"Found weight weight {weight} greater than top {top:?} from header"
611							),
612							});
613						} else {
614							panic!("Cannot parse line {line}");
615						}
616					}
617
618					if let Ok(lit) = seg.parse::<i32>() {
619						if lit == 0 {
620							wcnf.add_clause(cl.drain(..)).unwrap();
621						} else {
622							cl.push(Lit(NonZeroI32::new(lit).unwrap()));
623						}
624					}
625				}
626			}
627			// parse header, expected format: "p cnf {num_var} {num_clauses}" or "p wcnf {num_var}
628			// {num_clauses} {top}"
629			Ok(line) => {
630				let vec: Vec<&str> = line.split_whitespace().collect();
631				// check "p" and "cnf" keyword
632				if !WEIGHTED && (vec.len() != 4 || vec[0..2] != ["p", "cnf"]) {
633					return Err(io::Error::new(
634						io::ErrorKind::InvalidInput,
635						"expected DIMACS CNF header formatted \"p cnf {variables} {clauses}\"",
636					));
637				} else if WEIGHTED && (vec.len() != 4 || vec[0..2] != ["p", "wcnf"]) {
638					return Err(io::Error::new(
639						io::ErrorKind::InvalidInput,
640						"expected DIMACS WCNF header formatted \"p wcnf {variables} {clauses} {top}\"",
641					));
642				}
643				// parse number of variables
644				wcnf.cnf.nvar = VarFactory {
645					next_var: Some(Var(vec[2].parse::<NonZeroI32>().map_err(|_| {
646						io::Error::new(
647							io::ErrorKind::InvalidInput,
648							"unable to parse number of variables",
649						)
650					})?)),
651				};
652				// parse number of clauses
653				let num_clauses: usize = vec[3].parse().map_err(|_| {
654					io::Error::new(
655						io::ErrorKind::InvalidInput,
656						"unable to parse number of clauses",
657					)
658				})?;
659
660				wcnf.cnf.lits.reserve(num_clauses);
661				wcnf.cnf.size.reserve(num_clauses);
662
663				if WEIGHTED {
664					top = Some(vec[4].parse().map_err(|_| {
665						io::Error::new(io::ErrorKind::InvalidInput, "unable to parse top weight")
666					})?);
667				}
668
669				// parsing header complete
670				had_header = true;
671			}
672			Err(e) => return Err(e),
673		}
674	}
675
676	if WEIGHTED {
677		Ok(Dimacs::Wcnf(wcnf))
678	} else {
679		Ok(Dimacs::Cnf(wcnf.cnf))
680	}
681}
682
683impl BitAnd<BoolVal> for BoolVal {
684	type Output = Formula<BoolVal>;
685
686	fn bitand(self, rhs: BoolVal) -> Self::Output {
687		match (self, rhs) {
688			(BoolVal::Const(a), BoolVal::Const(b)) => Formula::Atom((a & b).into()),
689			(BoolVal::Lit(a), BoolVal::Lit(b)) => (a & b).into(),
690			(BoolVal::Lit(a), BoolVal::Const(b)) | (BoolVal::Const(b), BoolVal::Lit(a)) => {
691				Formula::Atom(a & b)
692			}
693		}
694	}
695}
696
697impl BitAnd<Lit> for BoolVal {
698	type Output = Formula<BoolVal>;
699
700	fn bitand(self, rhs: Lit) -> Self::Output {
701		self & BoolVal::Lit(rhs)
702	}
703}
704
705impl BitAnd<bool> for BoolVal {
706	type Output = BoolVal;
707
708	fn bitand(self, rhs: bool) -> Self::Output {
709		match self {
710			BoolVal::Const(b) => (b & rhs).into(),
711			BoolVal::Lit(l) if rhs => (l).into(),
712			BoolVal::Lit(_) => false.into(),
713		}
714	}
715}
716
717impl BitOr<BoolVal> for BoolVal {
718	type Output = Formula<BoolVal>;
719
720	fn bitor(self, rhs: BoolVal) -> Self::Output {
721		match (self, rhs) {
722			(BoolVal::Const(a), BoolVal::Const(b)) => Formula::Atom((a | b).into()),
723			(BoolVal::Lit(a), BoolVal::Lit(b)) => (a | b).into(),
724			(BoolVal::Lit(a), BoolVal::Const(b)) | (BoolVal::Const(b), BoolVal::Lit(a)) => {
725				Formula::Atom(a | b)
726			}
727		}
728	}
729}
730
731impl BitOr<Lit> for BoolVal {
732	type Output = Formula<BoolVal>;
733
734	fn bitor(self, rhs: Lit) -> Self::Output {
735		self | BoolVal::Lit(rhs)
736	}
737}
738
739impl BitOr<bool> for BoolVal {
740	type Output = BoolVal;
741
742	fn bitor(self, rhs: bool) -> Self::Output {
743		match self {
744			BoolVal::Const(b) => (b | rhs).into(),
745			BoolVal::Lit(_) if rhs => true.into(),
746			BoolVal::Lit(_) => self,
747		}
748	}
749}
750
751impl BitXor<BoolVal> for BoolVal {
752	type Output = Formula<BoolVal>;
753
754	fn bitxor(self, rhs: BoolVal) -> Self::Output {
755		match (self, rhs) {
756			(BoolVal::Const(a), BoolVal::Const(b)) => Formula::Atom((a ^ b).into()),
757			(BoolVal::Lit(a), BoolVal::Lit(b)) => {
758				Formula::Xor(vec![Formula::Atom(a.into()), Formula::Atom(b.into())])
759			}
760			(BoolVal::Lit(a), BoolVal::Const(b)) | (BoolVal::Const(b), BoolVal::Lit(a)) => {
761				Formula::Atom((a ^ b).into())
762			}
763		}
764	}
765}
766
767impl BitXor<Lit> for BoolVal {
768	type Output = Formula<BoolVal>;
769
770	fn bitxor(self, rhs: Lit) -> Self::Output {
771		self ^ BoolVal::Lit(rhs)
772	}
773}
774
775impl BitXor<bool> for BoolVal {
776	type Output = BoolVal;
777
778	fn bitxor(self, rhs: bool) -> Self::Output {
779		if rhs {
780			!self
781		} else {
782			self
783		}
784	}
785}
786
787impl Display for BoolVal {
788	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
789		match self {
790			BoolVal::Const(b) => write!(f, "{b}"),
791			BoolVal::Lit(l) => write!(f, "{l}"),
792		}
793	}
794}
795
796impl From<Lit> for BoolVal {
797	fn from(value: Lit) -> Self {
798		BoolVal::Lit(value)
799	}
800}
801
802impl From<Var> for BoolVal {
803	fn from(value: Var) -> Self {
804		BoolVal::Lit(value.into())
805	}
806}
807
808impl From<bool> for BoolVal {
809	fn from(value: bool) -> Self {
810		BoolVal::Const(value)
811	}
812}
813
814impl Not for BoolVal {
815	type Output = BoolVal;
816
817	fn not(self) -> Self::Output {
818		match self {
819			BoolVal::Lit(l) => (!l).into(),
820			BoolVal::Const(b) => (!b).into(),
821		}
822	}
823}
824
825impl Cnf {
826	/// Read a CNF formula from a file formatted in the DIMACS CNF format
827	pub fn from_file(path: &Path) -> Result<Self, io::Error> {
828		match parse_dimacs_file::<false>(path)? {
829			Dimacs::Cnf(cnf) => Ok(cnf),
830			_ => unreachable!(),
831		}
832	}
833
834	#[cfg(test)]
835	/// Small helper method that gets all the created variables, used for
836	/// testing.
837	pub(crate) fn get_variables(&self) -> VarRange {
838		VarRange::new(
839			Var(NonZeroI32::new(1).unwrap()),
840			self.nvar.next_var.unwrap().prev_var().unwrap(),
841		)
842	}
843
844	/// Returns an iterator over the clauses in the formula.
845	pub fn iter(&self) -> impl ExactSizeIterator<Item = &[Lit]> + '_ {
846		CnfIterator {
847			lits: &self.lits,
848			size: self.size.iter(),
849			index: 0,
850		}
851	}
852
853	/// Returns the number of literals in the formula.
854	pub fn literals(&self) -> usize {
855		self.size.iter().sum()
856	}
857	/// Returns the number of clauses in the formula.
858	pub fn num_clauses(&self) -> usize {
859		self.size.len()
860	}
861
862	/// Store CNF formula at given path in DIMACS format
863	///
864	/// File will optionally be prefaced by a given comment
865	pub fn to_file(&self, path: &Path, comment: Option<&str>) -> Result<(), io::Error> {
866		let mut file = File::create(path)?;
867		if let Some(comment) = comment {
868			for line in comment.lines() {
869				writeln!(file, "c {line}")?;
870			}
871		}
872		write!(file, "{self}")
873	}
874
875	/// Returns the number of variables in the formula.
876	pub fn num_vars(&self) -> usize {
877		self.nvar.num_emitted_vars()
878	}
879
880	/// Returns the range of variables emitted to be used by this formula.
881	pub fn variables(&self) -> VarRange {
882		self.nvar.emitted_vars()
883	}
884}
885
886impl ClauseDatabase for Cnf {
887	fn add_clause_from_slice(&mut self, clause: &[Lit]) -> Result {
888		let size = self.lits.len();
889		self.lits.extend(clause);
890		let len = self.lits.len() - size;
891		self.size.push(len);
892		if len == 0 {
893			Err(Unsatisfiable)
894		} else {
895			Ok(())
896		}
897	}
898
899	fn new_var_range(&mut self, len: usize) -> VarRange {
900		self.nvar.next_var_range(len)
901	}
902}
903
904impl Display for Cnf {
905	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
906		let num_var = &self.num_vars();
907		let num_clauses = self.size.len();
908		writeln!(f, "p cnf {num_var} {num_clauses}")?;
909		let mut start = 0;
910		for size in self.size.iter() {
911			let cl = self.lits.iter().skip(start).take(*size);
912			for &lit in cl {
913				write!(f, "{} ", i32::from(lit))?;
914			}
915			writeln!(f, "0")?;
916			start += size;
917		}
918		Ok(())
919	}
920}
921
922impl ExactSizeIterator for CnfIterator<'_> {}
923
924impl<'a> Iterator for CnfIterator<'a> {
925	type Item = &'a [Lit];
926
927	fn count(self) -> usize {
928		self.size.count()
929	}
930
931	fn next(&mut self) -> Option<Self::Item> {
932		if let Some(size) = self.size.next() {
933			let start = self.index;
934			self.index += size;
935			Some(&self.lits[start..self.index])
936		} else {
937			None
938		}
939	}
940
941	fn size_hint(&self) -> (usize, Option<usize>) {
942		self.size.size_hint()
943	}
944}
945
946impl Add<Lit> for Coeff {
947	type Output = BoolLinExp;
948
949	fn add(self, rhs: Lit) -> Self::Output {
950		rhs + self
951	}
952}
953
954impl Mul<Lit> for Coeff {
955	type Output = BoolLinExp;
956
957	fn mul(self, rhs: Lit) -> Self::Output {
958		rhs * self
959	}
960}
961
962impl<Db: ClauseDatabase + ?Sized> ClauseDatabaseTools for Db {}
963
964impl<F: Fn(Lit) -> bool> Valuation for F {
965	fn value(&self, lit: Lit) -> bool {
966		self(lit)
967	}
968}
969
970impl Lit {
971	/// Coerce a non-zero integer into a literal.
972	///
973	/// ### Warning
974	/// This method is only safe to use if the input integer is known to be a
975	/// integer coerced from a literal part of the same formula. Otherwise, the
976	/// usage of the literal may lead to undefined behavior.
977	pub fn from_raw(value: NonZeroI32) -> Lit {
978		Lit(value)
979	}
980
981	/// Returns whether the literal is a negation of the underlying variable.
982	pub fn is_negated(&self) -> bool {
983		self.0.is_negative()
984	}
985
986	/// Returns the underlying variable of the literal, whether negated or not.
987	pub fn var(&self) -> Var {
988		Var(self.0.abs())
989	}
990}
991
992impl Add for Lit {
993	type Output = BoolLinExp;
994
995	fn add(self, rhs: Self) -> Self::Output {
996		BoolLinExp::from_terms(&[(self, 1), (rhs, 1)])
997	}
998}
999
1000impl Add<Coeff> for Lit {
1001	type Output = BoolLinExp;
1002
1003	fn add(self, rhs: Coeff) -> Self::Output {
1004		BoolLinExp::from_terms(&[(self, 1)]) + rhs
1005	}
1006}
1007
1008impl BitAnd<BoolVal> for Lit {
1009	type Output = Formula<BoolVal>;
1010
1011	fn bitand(self, rhs: BoolVal) -> Self::Output {
1012		rhs & self
1013	}
1014}
1015
1016impl BitAnd<Lit> for Lit {
1017	type Output = Formula<Lit>;
1018
1019	fn bitand(self, rhs: Lit) -> Self::Output {
1020		Formula::And(vec![Formula::Atom(self), Formula::Atom(rhs)])
1021	}
1022}
1023
1024impl BitAnd<bool> for Lit {
1025	type Output = BoolVal;
1026
1027	fn bitand(self, rhs: bool) -> Self::Output {
1028		if rhs {
1029			self.into()
1030		} else {
1031			false.into()
1032		}
1033	}
1034}
1035
1036impl BitOr<BoolVal> for Lit {
1037	type Output = Formula<BoolVal>;
1038
1039	fn bitor(self, rhs: BoolVal) -> Self::Output {
1040		rhs | self
1041	}
1042}
1043
1044impl BitOr<Lit> for Lit {
1045	type Output = Formula<Lit>;
1046
1047	fn bitor(self, rhs: Lit) -> Self::Output {
1048		Formula::Or(vec![Formula::Atom(self), Formula::Atom(rhs)])
1049	}
1050}
1051
1052impl BitOr<bool> for Lit {
1053	type Output = BoolVal;
1054
1055	fn bitor(self, rhs: bool) -> Self::Output {
1056		if rhs {
1057			true.into()
1058		} else {
1059			self.into()
1060		}
1061	}
1062}
1063
1064impl BitXor<BoolVal> for Lit {
1065	type Output = Formula<BoolVal>;
1066
1067	fn bitxor(self, rhs: BoolVal) -> Self::Output {
1068		rhs ^ self
1069	}
1070}
1071
1072impl BitXor<Lit> for Lit {
1073	type Output = Formula<Lit>;
1074
1075	fn bitxor(self, rhs: Lit) -> Self::Output {
1076		Formula::Xor(vec![Formula::Atom(self), Formula::Atom(rhs)])
1077	}
1078}
1079
1080impl BitXor<bool> for Lit {
1081	type Output = Lit;
1082
1083	fn bitxor(self, rhs: bool) -> Self::Output {
1084		if rhs {
1085			!self
1086		} else {
1087			self
1088		}
1089	}
1090}
1091
1092impl Display for Lit {
1093	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1094		write!(
1095			f,
1096			"{}{}",
1097			if self.is_negated() { "¬" } else { "" },
1098			self.var()
1099		)
1100	}
1101}
1102
1103impl From<Var> for Lit {
1104	fn from(value: Var) -> Self {
1105		Lit(value.0)
1106	}
1107}
1108
1109impl Mul<Coeff> for Lit {
1110	type Output = BoolLinExp;
1111
1112	fn mul(self, rhs: Coeff) -> Self::Output {
1113		BoolLinExp::from_terms(&[(self, rhs)])
1114	}
1115}
1116
1117impl Not for Lit {
1118	type Output = Lit;
1119
1120	fn not(self) -> Self::Output {
1121		Lit(-self.0)
1122	}
1123}
1124
1125impl Ord for Lit {
1126	fn cmp(&self, other: &Self) -> Ordering {
1127		match self.var().cmp(&other.var()) {
1128			Ordering::Equal => (self.is_negated()).cmp(&other.is_negated()),
1129			r => r,
1130		}
1131	}
1132}
1133
1134impl PartialOrd for Lit {
1135	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1136		Some(self.cmp(other))
1137	}
1138}
1139
1140impl From<Lit> for NonZeroI32 {
1141	fn from(val: Lit) -> Self {
1142		val.0
1143	}
1144}
1145
1146impl From<Var> for NonZeroI32 {
1147	fn from(val: Var) -> Self {
1148		val.0
1149	}
1150}
1151
1152impl Display for Unsatisfiable {
1153	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1154		write!(f, "Problem inconsistency detected")
1155	}
1156}
1157
1158impl Error for Unsatisfiable {}
1159
1160impl Var {
1161	const MAX_VARS: usize = NonZeroI32::MAX.get() as usize;
1162
1163	fn checked_add(&self, b: NonZeroI32) -> Option<Var> {
1164		self.0
1165			.get()
1166			.checked_add(b.get())
1167			.map(|v| Var(NonZeroI32::new(v).unwrap()))
1168	}
1169
1170	fn next_var(&self) -> Option<Var> {
1171		const ONE: NonZeroI32 = NonZeroI32::new(1).unwrap();
1172		self.checked_add(ONE)
1173	}
1174
1175	fn prev_var(&self) -> Option<Var> {
1176		let prev = self.0.get() - 1;
1177		if prev > 0 {
1178			Some(Var(NonZeroI32::new(prev).unwrap()))
1179		} else {
1180			None
1181		}
1182	}
1183}
1184
1185impl Display for Var {
1186	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1187		write!(f, "x{}", subscript_number(self.0.get() as usize).format(""))
1188	}
1189}
1190
1191impl Not for Var {
1192	type Output = Lit;
1193
1194	fn not(self) -> Self::Output {
1195		!Lit::from(self)
1196	}
1197}
1198
1199impl VarRange {
1200	/// Create an empty variable range
1201	pub fn empty() -> Self {
1202		Self {
1203			start: Var(NonZeroI32::new(2).unwrap()),
1204			end: Var(NonZeroI32::new(1).unwrap()),
1205		}
1206	}
1207
1208	/// Returns the upper bound of the variable range (inclusive).
1209	///
1210	/// Note: the value returned by this method is unspecified after the range
1211	/// has been iterated to exhaustion.
1212	pub fn end(&self) -> Var {
1213		self.end
1214	}
1215
1216	/// Find the index of a variable within the range
1217	pub fn find(&self, var: Var) -> Option<usize> {
1218		if !self.contains(&var) {
1219			None
1220		} else {
1221			let offset = (var.0.get() - self.start.0.get()) as usize;
1222			debug_assert!(offset <= self.len());
1223			Some(offset)
1224		}
1225	}
1226
1227	/// Performs the indexing operation into the variable range
1228	pub fn index(&self, index: usize) -> Var {
1229		if index >= self.len() {
1230			panic!("out of bounds access");
1231		}
1232		if index == 0 {
1233			self.start
1234		} else {
1235			let index = NonZeroI32::new(index as i32).unwrap();
1236			self.start.checked_add(index).unwrap()
1237		}
1238	}
1239
1240	/// Returns `true` if the range contains no items.
1241	///
1242	/// # Examples
1243	///
1244	/// ```
1245	/// # use pindakaas::VarRange;
1246	/// assert!(VarRange::empty().is_empty());
1247	/// ```
1248	pub const fn is_empty(&self) -> bool {
1249		self.len() == 0
1250	}
1251
1252	/// Returns an iterator of the Boolean variables in the range represented as
1253	/// [`Lit`]s.
1254	pub fn iter_lits(&mut self) -> impl Iterator<Item = Lit> + '_ {
1255		self.map(Lit::from)
1256	}
1257
1258	/// Returns the number of variables in the range.
1259	pub const fn len(&self) -> usize {
1260		let len = self.end.0.get() - self.start.0.get() + 1;
1261		if len < 0 {
1262			return 0;
1263		}
1264		len as usize
1265	}
1266
1267	/// Create a range starting from `start` and ending at `end` (inclusive)
1268	pub fn new(start: Var, end: Var) -> Self {
1269		Self { start, end }
1270	}
1271
1272	/// Returns the lower bound of the variable range (inclusive).
1273	///
1274	/// Note: the value returned by this method is unspecified after the range
1275	/// has been iterated to exhaustion.
1276	pub fn start(&self) -> Var {
1277		self.start
1278	}
1279}
1280
1281impl DoubleEndedIterator for VarRange {
1282	fn next_back(&mut self) -> Option<Self::Item> {
1283		if self.start <= self.end {
1284			let item = self.end;
1285			if let Some(prev) = self.end.prev_var() {
1286				self.end = prev;
1287			} else {
1288				*self = VarRange::empty();
1289			}
1290			Some(item)
1291		} else {
1292			None
1293		}
1294	}
1295}
1296
1297impl ExactSizeIterator for VarRange {
1298	fn len(&self) -> usize {
1299		self.len()
1300	}
1301}
1302
1303impl From<RangeInclusive<Var>> for VarRange {
1304	fn from(value: RangeInclusive<Var>) -> Self {
1305		VarRange::new(*value.start(), *value.end())
1306	}
1307}
1308
1309impl FusedIterator for VarRange {}
1310
1311impl Iterator for VarRange {
1312	type Item = Var;
1313
1314	fn count(self) -> usize {
1315		let (lower, upper) = self.size_hint();
1316		debug_assert_eq!(upper, Some(lower));
1317		lower
1318	}
1319
1320	fn next(&mut self) -> Option<Self::Item> {
1321		if self.start <= self.end {
1322			let item = self.start;
1323			self.start = self.start.next_var().unwrap();
1324			Some(item)
1325		} else {
1326			None
1327		}
1328	}
1329
1330	fn size_hint(&self) -> (usize, Option<usize>) {
1331		let len = self.len();
1332		(len, Some(len))
1333	}
1334}
1335
1336impl RangeBounds<Var> for VarRange {
1337	fn end_bound(&self) -> Bound<&Var> {
1338		Bound::Included(&self.end)
1339	}
1340	fn start_bound(&self) -> Bound<&Var> {
1341		Bound::Included(&self.start)
1342	}
1343}
1344
1345impl Wcnf {
1346	/// Add a weighted clause to the formula.
1347	pub fn add_weighted_clause<I>(&mut self, clause: I, weight: Coeff) -> Result
1348	where
1349		I: IntoIterator,
1350		I::Item: Into<BoolVal>,
1351	{
1352		let clauses = self.cnf.num_clauses();
1353		self.cnf.add_clause(clause)?;
1354		if self.cnf.num_clauses() > clauses {
1355			self.weights.push(Some(weight));
1356		}
1357		Ok(())
1358	}
1359
1360	/// Returns the number of clauses in the formula.
1361	pub fn num_clauses(&self) -> usize {
1362		self.cnf.num_clauses()
1363	}
1364
1365	/// Returns the number of variables in the formula.
1366	pub fn num_vars(&self) -> usize {
1367		self.cnf.num_vars()
1368	}
1369
1370	/// Read a WCNF formula from a file formatted in the (W)DIMACS WCNF format
1371	pub fn from_file(path: &Path) -> Result<Self, io::Error> {
1372		match parse_dimacs_file::<true>(path)? {
1373			Dimacs::Wcnf(wcnf) => Ok(wcnf),
1374			_ => unreachable!(),
1375		}
1376	}
1377
1378	/// Returns an iterator over the clauses and their weights.
1379	pub fn iter(&self) -> impl ExactSizeIterator<Item = (&[Lit], &Option<Coeff>)> {
1380		self.cnf.iter().zip(self.weights.iter())
1381	}
1382
1383	/// Returns the number of literals in the formula.
1384	pub fn literals(&self) -> usize {
1385		self.cnf.literals()
1386	}
1387
1388	/// Store WCNF formula at given path in WDIMACS format
1389	///
1390	/// File will optionally be prefaced by a given comment
1391	pub fn to_file(&self, path: &Path, comment: Option<&str>) -> Result<(), io::Error> {
1392		let mut file = File::create(path)?;
1393		if let Some(comment) = comment {
1394			for line in comment.lines() {
1395				writeln!(file, "c {line}")?;
1396			}
1397		}
1398		write!(file, "{self}")
1399	}
1400
1401	/// Returns the range of variables emitted to be used by this formula.
1402	pub fn variables(&self) -> VarRange {
1403		self.cnf.variables()
1404	}
1405}
1406
1407impl ClauseDatabase for Wcnf {
1408	fn add_clause_from_slice(&mut self, clause: &[Lit]) -> Result {
1409		let clauses = self.cnf.num_clauses();
1410		self.cnf.add_clause_from_slice(clause)?;
1411		if self.cnf.num_clauses() > clauses {
1412			self.weights.push(None);
1413		}
1414		Ok(())
1415	}
1416
1417	fn new_var_range(&mut self, len: usize) -> VarRange {
1418		self.cnf.new_var_range(len)
1419	}
1420}
1421
1422impl Display for Wcnf {
1423	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1424		let num_var = &self.cnf.nvar.num_emitted_vars();
1425		let num_clauses = self.cnf.size.len();
1426		let top = self.weights.iter().flatten().fold(1, |a, b| a + *b);
1427		writeln!(f, "p wcnf {num_var} {num_clauses} {top}")?;
1428		let mut start = 0;
1429		for (size, weight) in self.cnf.size.iter().zip(self.weights.iter()) {
1430			let cl = self.cnf.lits.iter().skip(start).take(*size);
1431			let weight = weight.unwrap_or(top);
1432			write!(f, "{weight} ")?;
1433			for lit in cl {
1434				write!(f, "{} ", lit.0)?;
1435			}
1436			writeln!(f, "0")?;
1437			start += size;
1438		}
1439		Ok(())
1440	}
1441}
1442
1443impl From<Cnf> for Wcnf {
1444	fn from(cnf: Cnf) -> Self {
1445		let weights = repeat_n(None, cnf.num_clauses()).collect();
1446		Wcnf { cnf, weights }
1447	}
1448}
1449
1450impl From<Lit> for i32 {
1451	fn from(val: Lit) -> Self {
1452		val.0.get()
1453	}
1454}
1455
1456impl From<Var> for i32 {
1457	fn from(val: Var) -> Self {
1458		val.0.get()
1459	}
1460}
1461
1462#[cfg(test)]
1463mod tests {
1464	use std::num::NonZeroI32;
1465
1466	use crate::{solver::VarFactory, Lit, Var};
1467
1468	#[test]
1469	fn var_range() {
1470		let mut factory = VarFactory::default();
1471
1472		let range = factory.next_var_range(0);
1473		assert_eq!(range.len(), 0);
1474		assert_eq!(factory.next_var, Some(Var(NonZeroI32::new(1).unwrap())));
1475
1476		let range = factory.next_var_range(1);
1477		assert_eq!(range.len(), 1);
1478		assert_eq!(factory.next_var, Some(Var(NonZeroI32::new(2).unwrap())));
1479
1480		let range = factory.next_var_range(2);
1481		assert_eq!(range.len(), 2);
1482		assert_eq!(factory.next_var, Some(Var(NonZeroI32::new(4).unwrap())));
1483
1484		let range = factory.next_var_range(100);
1485		assert_eq!(range.len(), 100);
1486		assert_eq!(factory.next_var, Some(Var(NonZeroI32::new(104).unwrap())));
1487	}
1488
1489	impl From<i32> for Lit {
1490		fn from(value: i32) -> Self {
1491			Lit(NonZeroI32::new(value).expect("cannot create literal with value zero"))
1492		}
1493	}
1494}