rustsat/types/
constraints.rs

1//! # Constraint Types
2//!
3//! Different types of constraints. The most important one is [`Clause`] but
4//! Rust SAT supports more complex constraints like [`PbConstraint`] or
5//! [`CardConstraint`].
6
7use core::slice;
8use std::{
9    collections::TryReserveError,
10    fmt,
11    ops::{self, Not, RangeBounds},
12};
13
14use itertools::Itertools;
15use thiserror::Error;
16
17use super::{Assignment, IWLitIter, Lit, LitIter, RsHashSet, TernaryVal, WLitIter};
18
19use crate::RequiresClausal;
20
21/// A reference to any type of constraint throughout RustSAT
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum ConstraintRef<'constr> {
24    /// A clausal constraint
25    Clause(&'constr Clause),
26    /// A cardinality constraint
27    Card(&'constr CardConstraint),
28    /// A pseudo-Boolean constraint
29    Pb(&'constr PbConstraint),
30}
31
32impl fmt::Display for ConstraintRef<'_> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            ConstraintRef::Clause(cl) => write!(f, "{cl}"),
36            ConstraintRef::Card(card) => write!(f, "{card}"),
37            ConstraintRef::Pb(pb) => write!(f, "{pb}"),
38        }
39    }
40}
41
42/// Type representing a clause.
43/// Wrapper around a std collection to allow for changing the data structure.
44/// Optional clauses as sets will be included in the future.
45#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Default)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct Clause {
48    lits: Vec<Lit>,
49}
50
51impl std::hash::Hash for Clause {
52    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
53        self.lits.hash(state);
54    }
55}
56
57impl Clause {
58    /// Creates a new empty clause
59    #[must_use]
60    pub fn new() -> Self {
61        Self::default()
62    }
63
64    /// Creates a new empty clause with at least the specified capacity.
65    ///
66    /// Uses [`Vec::with_capacity`] internally.
67    #[must_use]
68    pub fn with_capacity(capacity: usize) -> Self {
69        Self {
70            lits: Vec::with_capacity(capacity),
71        }
72    }
73
74    /// Reserves capacity for at least `additional` more literals.
75    ///
76    /// Uses [`Vec::reserve`] internally.
77    pub fn reserve(&mut self, additional: usize) {
78        self.lits.reserve(additional);
79    }
80
81    /// Reserves the minimum capacity for at least `additional` more literals.
82    ///
83    /// Uses [`Vec::reserve_exact`] internally.
84    pub fn reserve_exact(&mut self, additional: usize) {
85        self.lits.reserve_exact(additional);
86    }
87
88    /// Tries to reserve capacity for at least `additional` more literals.
89    ///
90    /// Uses [`Vec::try_reserve`] internally.
91    ///
92    /// # Errors
93    ///
94    /// If the capacity overflows, or the allocator reports a failure, then an error is returned.
95    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
96        self.lits.try_reserve(additional)
97    }
98
99    /// Tries to reserve the minimum capacity for at least `additional` more literals.
100    ///
101    /// Uses [`Vec::try_reserve_exact`] internally.
102    ///
103    /// # Errors
104    ///
105    /// If the capacity overflows, or the allocator reports a failure, then an error is returned.
106    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
107        self.lits.try_reserve_exact(additional)
108    }
109
110    /// Like [`Vec::drain`]
111    pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> std::vec::Drain<'_, Lit> {
112        self.lits.drain(range)
113    }
114
115    /// Normalizes the clause. This includes sorting the literals, removing
116    /// duplicates and removing the entire clause if it is a tautology.
117    /// Comparing two normalized clauses checks their logical equivalence.
118    #[must_use]
119    pub fn normalize(mut self) -> Option<Self> {
120        if self.len() <= 1 {
121            return Some(self);
122        }
123        // Sort and filter duplicates
124        self.lits.sort_unstable();
125        self.lits.dedup();
126        // Check for tautology
127        let mut neg_last = None;
128        if let Err(()) = self.iter().try_for_each(|l| {
129            if let Some(neg_last) = neg_last {
130                if l == &neg_last {
131                    // Positive lits always come first
132                    return Err(());
133                }
134            }
135            neg_last = Some(!*l);
136            Ok(())
137        }) {
138            return None;
139        }
140        Some(self)
141    }
142
143    /// Sanitizes the clause. This includes removing duplicates and removing the
144    /// entire clause if it is a tautology. This preserves the order of the
145    /// literals in the clause.
146    #[must_use]
147    pub fn sanitize(mut self) -> Option<Self> {
148        if self.len() <= 1 {
149            return Some(self);
150        }
151        let mut lset = RsHashSet::default();
152        let mut idx = 0;
153        while idx < self.len() {
154            let l = self[idx];
155            if lset.contains(&!l) {
156                // Tautology
157                return None;
158            }
159            if lset.contains(&l) {
160                self.lits.remove(idx);
161            } else {
162                lset.insert(l);
163                idx += 1;
164            }
165        }
166        Some(self)
167    }
168
169    /// Adds a literal to the clause
170    pub fn add(&mut self, lit: Lit) {
171        self.lits.push(lit);
172    }
173
174    /// Removes the first occurrence of a literal from the clause
175    /// Returns true if an occurrence was found
176    pub fn remove(&mut self, lit: Lit) -> bool {
177        for (i, l) in self.lits.iter().enumerate() {
178            if *l == lit {
179                self.lits.swap_remove(i);
180                return true;
181            }
182        }
183        false
184    }
185
186    /// Removes all occurrences of a literal from the clause
187    pub fn remove_thorough(&mut self, lit: Lit) -> bool {
188        let mut idxs = Vec::new();
189        for (i, l) in self.lits.iter().enumerate() {
190            if *l == lit {
191                idxs.push(i);
192            }
193        }
194        for i in idxs.iter().rev() {
195            self.lits.remove(*i);
196        }
197        !idxs.is_empty()
198    }
199}
200
201impl ops::Deref for Clause {
202    type Target = Cl;
203
204    fn deref(&self) -> &Self::Target {
205        Cl::new(self)
206    }
207}
208
209impl std::ops::DerefMut for Clause {
210    fn deref_mut(&mut self) -> &mut Self::Target {
211        Cl::new_mut(self)
212    }
213}
214
215impl AsRef<[Lit]> for Clause {
216    fn as_ref(&self) -> &[Lit] {
217        &self.lits
218    }
219}
220
221impl AsMut<[Lit]> for Clause {
222    fn as_mut(&mut self) -> &mut [Lit] {
223        &mut self.lits
224    }
225}
226
227impl AsRef<Cl> for Clause {
228    fn as_ref(&self) -> &Cl {
229        Cl::new(self)
230    }
231}
232
233impl AsMut<Cl> for Clause {
234    fn as_mut(&mut self) -> &mut Cl {
235        Cl::new_mut(self)
236    }
237}
238
239impl<const N: usize> From<[Lit; N]> for Clause {
240    fn from(value: [Lit; N]) -> Self {
241        Self {
242            lits: Vec::from(value),
243        }
244    }
245}
246
247impl From<&[Lit]> for Clause {
248    fn from(value: &[Lit]) -> Self {
249        Self {
250            lits: Vec::from(value),
251        }
252    }
253}
254
255impl Extend<Lit> for Clause {
256    fn extend<T: IntoIterator<Item = Lit>>(&mut self, iter: T) {
257        self.lits.extend(iter);
258    }
259}
260
261impl IntoIterator for Clause {
262    type Item = Lit;
263
264    type IntoIter = std::vec::IntoIter<Lit>;
265
266    #[inline]
267    fn into_iter(self) -> Self::IntoIter {
268        self.lits.into_iter()
269    }
270}
271
272impl FromIterator<Lit> for Clause {
273    fn from_iter<T: IntoIterator<Item = Lit>>(iter: T) -> Self {
274        Self {
275            lits: Vec::from_iter(iter),
276        }
277    }
278}
279
280impl<'a> IntoIterator for &'a Clause {
281    type Item = &'a Lit;
282
283    type IntoIter = std::slice::Iter<'a, Lit>;
284
285    fn into_iter(self) -> Self::IntoIter {
286        self.lits.iter()
287    }
288}
289
290impl<'a> IntoIterator for &'a mut Clause {
291    type Item = &'a mut Lit;
292
293    type IntoIter = std::slice::IterMut<'a, Lit>;
294
295    fn into_iter(self) -> Self::IntoIter {
296        self.lits.iter_mut()
297    }
298}
299
300impl fmt::Display for Clause {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        write!(f, "({})", self.iter().format("|"))
303    }
304}
305
306impl fmt::Debug for Clause {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        write!(f, "({})", self.iter().format("|"))
309    }
310}
311
312#[cfg(feature = "proof-logging")]
313impl pigeons::ConstraintLike<crate::types::Var> for Clause {
314    fn rhs(&self) -> isize {
315        1
316    }
317
318    fn sum_iter(&self) -> impl Iterator<Item = (isize, pigeons::Axiom<crate::types::Var>)> {
319        self.lits.iter().map(|l| (1, pigeons::Axiom::from(*l)))
320    }
321}
322
323#[cfg(feature = "proof-logging")]
324impl pigeons::ConstraintLike<crate::types::Var> for Cl {
325    fn rhs(&self) -> isize {
326        1
327    }
328
329    fn sum_iter(&self) -> impl Iterator<Item = (isize, pigeons::Axiom<crate::types::Var>)> {
330        self.lits.iter().map(|l| (1, pigeons::Axiom::from(*l)))
331    }
332}
333
334/// Creates a clause from a list of literals
335#[macro_export]
336macro_rules! clause {
337    () => {
338        $crate::types::Clause::new()
339    };
340    ( $($l:expr),* ) => {
341        {
342            let mut tmp_clause = $crate::types::Clause::new();
343            $(
344                tmp_clause.add($l);
345            )*
346            tmp_clause
347        }
348    };
349}
350
351/// Dynamically sized clause type to be used with references
352///
353/// [`Clause`] is the owned version: if [`Clause`] is [`String`], this is [`str`].
354#[derive(PartialEq, Eq, Hash)]
355#[repr(transparent)]
356pub struct Cl {
357    lits: [Lit],
358}
359
360impl Cl {
361    /// Directly wraps a literal slice as a [`Cl`]
362    ///
363    /// This is a cost-free conversion
364    ///
365    /// # Examples
366    ///
367    /// ```
368    /// use rustsat::{types::Cl, lit};
369    ///
370    /// let lits = [lit![0], lit![1]];
371    /// Cl::new(&lits);
372    /// ```
373    pub fn new<S: AsRef<[Lit]> + ?Sized>(s: &S) -> &Cl {
374        unsafe { &*(std::ptr::from_ref::<[Lit]>(s.as_ref()) as *const Cl) }
375    }
376
377    /// Directly wraps a mutable literal slice as a [`Cl`]
378    pub fn new_mut<S: AsMut<[Lit]> + ?Sized>(s: &mut S) -> &mut Cl {
379        unsafe { &mut *(std::ptr::from_mut::<[Lit]>(s.as_mut()) as *mut Cl) }
380    }
381
382    /// Gets the length of the clause
383    #[inline]
384    #[must_use]
385    pub fn len(&self) -> usize {
386        self.lits.len()
387    }
388
389    /// Checks if the clause is empty
390    #[inline]
391    #[must_use]
392    pub fn is_empty(&self) -> bool {
393        self.lits.is_empty()
394    }
395
396    /// Gets an iterator over the clause
397    #[inline]
398    pub fn iter(&self) -> impl Iterator<Item = &Lit> {
399        self.lits.iter()
400    }
401
402    /// Gets a mutable iterator over the clause
403    #[inline]
404    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Lit> {
405        self.lits.iter_mut()
406    }
407
408    /// Evaluates a clause under a given assignment
409    #[must_use]
410    pub fn evaluate(&self, assignment: &Assignment) -> TernaryVal {
411        self.iter()
412            .fold(TernaryVal::False, |val, l| match assignment.lit_value(*l) {
413                TernaryVal::True => TernaryVal::True,
414                TernaryVal::DontCare => {
415                    if val == TernaryVal::False {
416                        TernaryVal::DontCare
417                    } else {
418                        val
419                    }
420                }
421                TernaryVal::False => val,
422            })
423    }
424
425    /// Checks whether the clause is tautological
426    #[must_use]
427    pub fn is_tautology(&self) -> bool {
428        if self.is_empty() {
429            return false;
430        }
431        for (idx, &l1) in self[0..self.len() - 1].iter().enumerate() {
432            for &l2 in &self[idx + 1..] {
433                if l1 == !l2 {
434                    return true;
435                }
436            }
437        }
438        false
439    }
440
441    /// Performs [`slice::sort_unstable`] on the clause
442    pub fn sort(&mut self) {
443        self.lits.sort_unstable();
444    }
445
446    /// Checks if the clause is a unit clause
447    #[inline]
448    #[must_use]
449    pub fn is_unit(&self) -> bool {
450        self.lits.len() == 1
451    }
452
453    /// Checks if the clause is binary
454    #[inline]
455    #[must_use]
456    pub fn is_binary(&self) -> bool {
457        self.lits.len() == 2
458    }
459}
460
461impl AsRef<[Lit]> for Cl {
462    fn as_ref(&self) -> &[Lit] {
463        &self.lits
464    }
465}
466
467impl AsMut<[Lit]> for Cl {
468    fn as_mut(&mut self) -> &mut [Lit] {
469        &mut self.lits
470    }
471}
472
473impl<I> ops::Index<I> for Cl
474where
475    I: slice::SliceIndex<[Lit]>,
476{
477    type Output = <I as slice::SliceIndex<[Lit]>>::Output;
478
479    #[inline]
480    fn index(&self, index: I) -> &Self::Output {
481        &self.lits[index]
482    }
483}
484
485impl<I> ops::IndexMut<I> for Cl
486where
487    I: slice::SliceIndex<[Lit]>,
488{
489    #[inline]
490    fn index_mut(&mut self, index: I) -> &mut Self::Output {
491        &mut self.lits[index]
492    }
493}
494
495impl<'a> IntoIterator for &'a Cl {
496    type Item = &'a Lit;
497
498    type IntoIter = std::slice::Iter<'a, Lit>;
499
500    #[inline]
501    fn into_iter(self) -> Self::IntoIter {
502        self.lits.iter()
503    }
504}
505
506impl<'a> IntoIterator for &'a mut Cl {
507    type Item = &'a mut Lit;
508
509    type IntoIter = std::slice::IterMut<'a, Lit>;
510
511    #[inline]
512    fn into_iter(self) -> Self::IntoIter {
513        self.lits.iter_mut()
514    }
515}
516
517impl fmt::Display for Cl {
518    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519        write!(f, "({})", self.iter().format("|"))
520    }
521}
522
523impl fmt::Debug for Cl {
524    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525        write!(f, "({})", self.iter().format("|"))
526    }
527}
528
529impl AsRef<Cl> for [Lit] {
530    fn as_ref(&self) -> &Cl {
531        Cl::new(self)
532    }
533}
534
535impl AsMut<Cl> for [Lit] {
536    fn as_mut(&mut self) -> &mut Cl {
537        Cl::new_mut(self)
538    }
539}
540
541/// Type representing a cardinality constraint.
542#[derive(Hash, Eq, PartialEq, Clone, Debug)]
543#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
544pub enum CardConstraint {
545    /// An upper bound cardinality constraint
546    Ub(CardUbConstr),
547    /// A lower bound cardinality constraint
548    Lb(CardLbConstr),
549    /// An equality cardinality constraint
550    Eq(CardEqConstr),
551}
552
553impl fmt::Display for CardConstraint {
554    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555        match self {
556            CardConstraint::Ub(c) => write!(f, "{c}"),
557            CardConstraint::Lb(c) => write!(f, "{c}"),
558            CardConstraint::Eq(c) => write!(f, "{c}"),
559        }
560    }
561}
562
563macro_rules! write_lit_sum {
564    ($f:expr, $vec:expr) => {{
565        for i in 0..$vec.len() {
566            if i < $vec.len() - 1 {
567                write!($f, "{} + ", $vec[i])?;
568            } else {
569                write!($f, "{}", $vec[i])?;
570            }
571        }
572    }};
573}
574
575impl CardConstraint {
576    /// Constructs a new unit constraint enforcing a literal
577    #[must_use]
578    pub fn new_unit(lit: Lit) -> Self {
579        CardConstraint::Lb(CardLbConstr {
580            lits: vec![lit],
581            b: 1,
582        })
583    }
584
585    /// Constructs a new upper bound cardinality constraint (`sum of lits <= b`)
586    pub fn new_ub<LI: LitIter>(lits: LI, b: usize) -> Self {
587        CardConstraint::Ub(CardUbConstr {
588            lits: lits.into_iter().collect(),
589            b,
590        })
591    }
592
593    /// Constructs a new lower bound cardinality constraint (`sum of lits >= b`)
594    pub fn new_lb<LI: LitIter>(lits: LI, b: usize) -> Self {
595        CardConstraint::Lb(CardLbConstr {
596            lits: lits.into_iter().collect(),
597            b,
598        })
599    }
600
601    /// Constructs a new equality cardinality constraint (`sum of lits = b`)
602    pub fn new_eq<LI: LitIter>(lits: LI, b: usize) -> Self {
603        CardConstraint::Eq(CardEqConstr {
604            lits: lits.into_iter().collect(),
605            b,
606        })
607    }
608
609    /// Adds literals to the cardinality constraint
610    pub fn add<LI: LitIter>(&mut self, lits: LI) {
611        match self {
612            CardConstraint::Ub(constr) => constr.lits.extend(lits),
613            CardConstraint::Lb(constr) => constr.lits.extend(lits),
614            CardConstraint::Eq(constr) => constr.lits.extend(lits),
615        }
616    }
617
618    /// Gets the bound of the constraint
619    #[must_use]
620    pub fn bound(&self) -> usize {
621        match self {
622            CardConstraint::Ub(CardUbConstr { b, .. })
623            | CardConstraint::Lb(CardLbConstr { b, .. })
624            | CardConstraint::Eq(CardEqConstr { b, .. }) => *b,
625        }
626    }
627
628    /// Gets the number of literals in the constraint
629    #[must_use]
630    pub fn len(&self) -> usize {
631        match self {
632            CardConstraint::Ub(constr) => constr.lits.len(),
633            CardConstraint::Lb(constr) => constr.lits.len(),
634            CardConstraint::Eq(constr) => constr.lits.len(),
635        }
636    }
637
638    /// Checks whether the constraint is empty
639    #[must_use]
640    pub fn is_empty(&self) -> bool {
641        self.len() == 0
642    }
643
644    /// Gets the number of literals in the constraint
645    #[must_use]
646    pub fn n_lits(&self) -> usize {
647        self.len()
648    }
649
650    /// Changes the bound on the constraint
651    #[deprecated(
652        since = "0.7.0",
653        note = "`change_bound` has been renamed to `set_bound`"
654    )]
655    pub fn change_bound(&mut self, b: usize) {
656        self.set_bound(b);
657    }
658
659    /// Sets the bound of the constraint
660    pub fn set_bound(&mut self, bound: usize) {
661        match self {
662            CardConstraint::Ub(CardUbConstr { b, .. })
663            | CardConstraint::Lb(CardLbConstr { b, .. })
664            | CardConstraint::Eq(CardEqConstr { b, .. }) => *b = bound,
665        }
666    }
667
668    /// Checks if the constraint is always satisfied
669    #[must_use]
670    pub fn is_tautology(&self) -> bool {
671        match self {
672            CardConstraint::Ub(constr) => constr.is_tautology(),
673            CardConstraint::Lb(constr) => constr.is_tautology(),
674            CardConstraint::Eq(_) => false,
675        }
676    }
677
678    /// Checks if the constraint is unsatisfiable
679    #[must_use]
680    pub fn is_unsat(&self) -> bool {
681        match self {
682            CardConstraint::Ub(_) => false,
683            CardConstraint::Lb(constr) => constr.is_unsat(),
684            CardConstraint::Eq(constr) => constr.is_unsat(),
685        }
686    }
687
688    /// Checks if the constraint assigns all literals to true
689    #[must_use]
690    pub fn is_positive_assignment(&self) -> bool {
691        match self {
692            CardConstraint::Ub(_) => false,
693            CardConstraint::Lb(constr) => constr.is_positive_assignment(),
694            CardConstraint::Eq(constr) => constr.is_positive_assignment(),
695        }
696    }
697
698    /// Checks if the constraint assigns all literals to false
699    #[must_use]
700    pub fn is_negative_assignment(&self) -> bool {
701        match self {
702            CardConstraint::Ub(constr) => constr.is_negative_assignment(),
703            CardConstraint::Lb(_) => false,
704            CardConstraint::Eq(constr) => constr.is_negative_assignment(),
705        }
706    }
707
708    /// Checks if the constraint is a clause
709    #[must_use]
710    pub fn is_clause(&self) -> bool {
711        match self {
712            CardConstraint::Ub(constr) => constr.is_clause(),
713            CardConstraint::Lb(constr) => constr.is_clause(),
714            CardConstraint::Eq(_) => false,
715        }
716    }
717
718    /// Normalizes the constraint. This only consists of sorting the literals.
719    /// Comparing two normalized constraints checks their logical equivalence.
720    #[must_use]
721    pub fn normalize(mut self) -> Self {
722        let norm = |lits: &mut Vec<Lit>| {
723            if lits.len() <= 1 {
724                return;
725            }
726            lits.sort_unstable();
727        };
728        match &mut self {
729            CardConstraint::Ub(constr) => norm(&mut constr.lits),
730            CardConstraint::Lb(constr) => norm(&mut constr.lits),
731            CardConstraint::Eq(constr) => norm(&mut constr.lits),
732        }
733        self
734    }
735
736    /// Gets the literals that are in the constraint
737    #[must_use]
738    pub fn into_lits(self) -> Vec<Lit> {
739        match self {
740            CardConstraint::Ub(constr) => constr.lits,
741            CardConstraint::Lb(constr) => constr.lits,
742            CardConstraint::Eq(constr) => constr.lits,
743        }
744    }
745
746    /// Converts the constraint into a clause, if possible
747    ///
748    /// # Errors
749    ///
750    /// If the constraint is not a clause, returns [`RequiresClausal`].
751    pub fn into_clause(self) -> Result<Clause, RequiresClausal> {
752        if !self.is_clause() {
753            return Err(RequiresClausal);
754        }
755        match self {
756            CardConstraint::Ub(constr) => Ok(constr.lits.into_iter().map(Lit::not).collect()),
757            CardConstraint::Lb(constr) => Ok(Clause::from_iter(constr.lits)),
758            CardConstraint::Eq(_) => unreachable!(),
759        }
760    }
761
762    /// Gets an iterator over the literals in the constraint
763    #[inline]
764    pub fn iter(&self) -> std::slice::Iter<'_, Lit> {
765        match self {
766            CardConstraint::Ub(constr) => constr.lits.iter(),
767            CardConstraint::Lb(constr) => constr.lits.iter(),
768            CardConstraint::Eq(constr) => constr.lits.iter(),
769        }
770    }
771
772    /// Gets a mutable iterator over the literals in the constraint
773    #[inline]
774    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Lit> {
775        match self {
776            CardConstraint::Ub(constr) => constr.lits.iter_mut(),
777            CardConstraint::Lb(constr) => constr.lits.iter_mut(),
778            CardConstraint::Eq(constr) => constr.lits.iter_mut(),
779        }
780    }
781
782    /// Checks whether the cardinality constraint is satisfied by the given assignment
783    #[must_use]
784    pub fn evaluate(&self, assign: &Assignment) -> TernaryVal {
785        #[allow(clippy::range_plus_one)]
786        let range = self
787            .iter()
788            .fold(0..0, |rng, &lit| match assign.lit_value(lit) {
789                TernaryVal::True => rng.start + 1..rng.end + 1,
790                TernaryVal::False => rng,
791                TernaryVal::DontCare => rng.start..rng.end + 1,
792            });
793        if range.contains(&self.bound()) {
794            return TernaryVal::DontCare;
795        }
796        match self {
797            CardConstraint::Ub(CardUbConstr { b, .. }) => (range.start <= *b).into(),
798            CardConstraint::Lb(CardLbConstr { b, .. }) => (range.start >= *b).into(),
799            CardConstraint::Eq(CardEqConstr { b, .. }) => (range.start == *b).into(),
800        }
801    }
802}
803
804impl<'slf> IntoIterator for &'slf CardConstraint {
805    type Item = &'slf Lit;
806
807    type IntoIter = std::slice::Iter<'slf, Lit>;
808
809    fn into_iter(self) -> Self::IntoIter {
810        self.iter()
811    }
812}
813
814impl<'slf> IntoIterator for &'slf mut CardConstraint {
815    type Item = &'slf mut Lit;
816
817    type IntoIter = std::slice::IterMut<'slf, Lit>;
818
819    fn into_iter(self) -> Self::IntoIter {
820        self.iter_mut()
821    }
822}
823
824impl From<Clause> for CardConstraint {
825    fn from(value: Clause) -> Self {
826        CardConstraint::new_lb(value, 1)
827    }
828}
829
830#[cfg(feature = "proof-logging")]
831impl pigeons::ConstraintLike<crate::types::Var> for CardConstraint {
832    fn rhs(&self) -> isize {
833        match self {
834            CardConstraint::Ub(c) => {
835                isize::try_from(c.lits.len())
836                    .expect("cannot handle more than `isize::MAX` literals")
837                    - isize::try_from(c.b).expect("cannot handle bounds larger than `isize::MAX`")
838            }
839            CardConstraint::Lb(c) => {
840                isize::try_from(c.b).expect("cannot handle bounds larger than `isize::MAX`")
841            }
842            CardConstraint::Eq(_) => {
843                panic!("VeriPB does not support equality constraints in the proof")
844            }
845        }
846    }
847
848    fn sum_iter(&self) -> impl Iterator<Item = (isize, pigeons::Axiom<crate::types::Var>)> {
849        match self {
850            CardConstraint::Ub(CardUbConstr { lits, .. }) => PigeonLitIter {
851                lits: lits.iter(),
852                negate: true,
853            },
854            CardConstraint::Lb(CardLbConstr { lits, .. })
855            | CardConstraint::Eq(CardEqConstr { lits, .. }) => PigeonLitIter {
856                lits: lits.iter(),
857                negate: false,
858            },
859        }
860    }
861}
862
863#[cfg(feature = "proof-logging")]
864struct PigeonLitIter<'a> {
865    lits: std::slice::Iter<'a, Lit>,
866    negate: bool,
867}
868
869#[cfg(feature = "proof-logging")]
870impl Iterator for PigeonLitIter<'_> {
871    type Item = (isize, pigeons::Axiom<crate::types::Var>);
872
873    fn next(&mut self) -> Option<Self::Item> {
874        self.lits.next().map(|l| {
875            (
876                1,
877                if self.negate {
878                    pigeons::Axiom::from(!*l)
879                } else {
880                    pigeons::Axiom::from(*l)
881                },
882            )
883        })
884    }
885}
886
887/// An upper bound cardinality constraint (`sum of lits <= b`)
888#[derive(Hash, Eq, PartialEq, Clone, Debug)]
889#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
890pub struct CardUbConstr {
891    lits: Vec<Lit>,
892    b: usize,
893}
894
895impl fmt::Display for CardUbConstr {
896    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
897        write_lit_sum!(f, self.lits);
898        write!(f, " <= {}", self.b)
899    }
900}
901
902impl CardUbConstr {
903    /// Decomposes the constraint to a set of input literals and an upper bound
904    #[must_use]
905    pub fn decompose(self) -> (Vec<Lit>, usize) {
906        (self.lits, self.b)
907    }
908
909    /// Get references to the constraints internals
910    pub(crate) fn decompose_ref(&self) -> (&Vec<Lit>, &usize) {
911        (&self.lits, &self.b)
912    }
913
914    /// Converts the upper bound constraint into an equivalent lower bound constraint
915    #[must_use]
916    pub fn invert(mut self) -> CardLbConstr {
917        self.lits.iter_mut().for_each(|l| {
918            *l = !*l;
919        });
920        let length = self.lits.len();
921        CardLbConstr {
922            lits: self.lits,
923            b: length - self.b,
924        }
925    }
926
927    /// Checks if the constraint is always satisfied
928    #[must_use]
929    pub fn is_tautology(&self) -> bool {
930        self.b >= self.lits.len()
931    }
932
933    /// Checks if the constraint assigns all literals to false
934    #[must_use]
935    pub fn is_negative_assignment(&self) -> bool {
936        self.b == 0
937    }
938
939    /// Checks if the constraint is a clause
940    #[must_use]
941    pub fn is_clause(&self) -> bool {
942        self.b + 1 == self.lits.len()
943    }
944}
945
946/// A lower bound cardinality constraint (`sum of lits >= b`)
947#[derive(Hash, Eq, PartialEq, Clone, Debug)]
948#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
949pub struct CardLbConstr {
950    lits: Vec<Lit>,
951    b: usize,
952}
953
954impl fmt::Display for CardLbConstr {
955    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956        write_lit_sum!(f, self.lits);
957        write!(f, " >= {}", self.b)
958    }
959}
960
961impl CardLbConstr {
962    /// Decomposes the constraint to a set of input literals and a lower bound
963    #[must_use]
964    pub fn decompose(self) -> (Vec<Lit>, usize) {
965        (self.lits, self.b)
966    }
967
968    /// Get references to the constraints internals
969    pub(crate) fn decompose_ref(&self) -> (&Vec<Lit>, &usize) {
970        (&self.lits, &self.b)
971    }
972
973    /// Converts the lower bound constraint into an equivalent upper bound constraint
974    #[must_use]
975    pub fn invert(mut self) -> CardUbConstr {
976        self.lits.iter_mut().for_each(|l| {
977            *l = !*l;
978        });
979        let length = self.lits.len();
980        CardUbConstr {
981            lits: self.lits,
982            b: length - self.b,
983        }
984    }
985
986    /// Checks if the constraint is always satisfied
987    #[must_use]
988    pub fn is_tautology(&self) -> bool {
989        self.b == 0
990    }
991
992    /// Checks if the constraint is unsatisfiable
993    #[must_use]
994    pub fn is_unsat(&self) -> bool {
995        self.b > self.lits.len()
996    }
997
998    /// Checks if the constraint assigns all literals to true
999    #[must_use]
1000    pub fn is_positive_assignment(&self) -> bool {
1001        self.b == self.lits.len()
1002    }
1003
1004    /// Checks if the constraint is a clause
1005    #[must_use]
1006    pub fn is_clause(&self) -> bool {
1007        self.b == 1
1008    }
1009}
1010
1011/// An equality cardinality constraint (`sum of lits = b`)
1012#[derive(Hash, Eq, PartialEq, Clone, Debug)]
1013#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1014pub struct CardEqConstr {
1015    lits: Vec<Lit>,
1016    b: usize,
1017}
1018
1019impl fmt::Display for CardEqConstr {
1020    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1021        write_lit_sum!(f, self.lits);
1022        write!(f, " = {}", self.b)
1023    }
1024}
1025
1026impl CardEqConstr {
1027    /// Decomposes the constraint to a set of input literals and an equality bound
1028    #[must_use]
1029    pub fn decompose(self) -> (Vec<Lit>, usize) {
1030        (self.lits, self.b)
1031    }
1032
1033    /// Get references to the constraints internals
1034    pub(crate) fn decompose_ref(&self) -> (&Vec<Lit>, &usize) {
1035        (&self.lits, &self.b)
1036    }
1037
1038    /// Splits the equality constraint into a lower bound and an upper bound constraint
1039    #[must_use]
1040    pub fn split(self) -> (CardLbConstr, CardUbConstr) {
1041        (
1042            CardLbConstr {
1043                lits: self.lits.clone(),
1044                b: self.b,
1045            },
1046            CardUbConstr {
1047                lits: self.lits,
1048                b: self.b,
1049            },
1050        )
1051    }
1052
1053    /// Checks if the constraint is unsatisfiable
1054    #[must_use]
1055    pub fn is_unsat(&self) -> bool {
1056        self.b > self.lits.len()
1057    }
1058
1059    /// Checks if the constraint assigns all literals to true
1060    #[must_use]
1061    pub fn is_positive_assignment(&self) -> bool {
1062        self.b == self.lits.len()
1063    }
1064
1065    /// Checks if the constraint assigns all literals to false
1066    #[must_use]
1067    pub fn is_negative_assignment(&self) -> bool {
1068        self.b == 0
1069    }
1070}
1071
1072/// Errors when converting pseudo-boolean to cardinality constraints
1073#[derive(Error, Debug)]
1074pub enum PbToCardError {
1075    /// the pseudo-boolean constraint is not a cardinality constraint
1076    #[error("the PB constraint is not a cardinality constraint")]
1077    NotACard,
1078    /// the pseudo-boolean constraint is unsatisfiable
1079    #[error("the PB constraint is unsatisfiable")]
1080    Unsat,
1081    /// the pseudo-boolean constraint is a tautology
1082    #[error("the PB constraint is a tautology")]
1083    Tautology,
1084}
1085
1086/// Type representing a pseudo-boolean constraint. When literals are added to a
1087/// constraint, the constraint is transformed so that all coefficients are
1088/// positive.
1089#[derive(Eq, PartialEq, Clone, Debug)]
1090#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1091pub enum PbConstraint {
1092    /// An upper bound pseudo-boolean constraint
1093    Ub(PbUbConstr),
1094    /// A lower bound pseudo-boolean constraint
1095    Lb(PbLbConstr),
1096    /// An equality pseudo-boolean constraint
1097    Eq(PbEqConstr),
1098}
1099
1100impl fmt::Display for PbConstraint {
1101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1102        match self {
1103            PbConstraint::Ub(c) => write!(f, "{c}"),
1104            PbConstraint::Lb(c) => write!(f, "{c}"),
1105            PbConstraint::Eq(c) => write!(f, "{c}"),
1106        }
1107    }
1108}
1109
1110macro_rules! write_wlit_sum {
1111    ($f:expr, $vec:expr) => {{
1112        for i in 0..$vec.len() {
1113            if i < $vec.len() - 1 {
1114                write!($f, "{} {} + ", $vec[i].1, $vec[i].0)?;
1115            } else {
1116                write!($f, "{} {}", $vec[i].1, $vec[i].0)?;
1117            }
1118        }
1119    }};
1120}
1121
1122impl PbConstraint {
1123    /// Constructs a new unit constraint enforcing a literal
1124    #[must_use]
1125    pub fn new_unit(lit: Lit) -> Self {
1126        PbConstraint::Lb(PbLbConstr {
1127            lits: vec![(lit, 1)],
1128            weight_sum: 1,
1129            b: 1,
1130        })
1131    }
1132
1133    /// Converts input literals to non-negative weights, also returns the weight sum and the sum to add to the bound
1134    fn convert_input_lits<LI: IWLitIter>(lits: LI) -> (impl WLitIter, usize, isize) {
1135        let mut b_add = 0;
1136        let mut weight_sum = 0;
1137        let lits: Vec<(Lit, usize)> = lits
1138            .into_iter()
1139            .map(|(l, w)| {
1140                if w >= 0 {
1141                    weight_sum += w.unsigned_abs();
1142                    (l, w.unsigned_abs())
1143                } else {
1144                    b_add -= w;
1145                    weight_sum += w.unsigned_abs();
1146                    (!l, w.unsigned_abs())
1147                }
1148            })
1149            .collect();
1150        (lits, weight_sum, b_add)
1151    }
1152
1153    /// Constructs a new upper bound pseudo-boolean constraint (`weighted sum of lits <= b`)
1154    pub fn new_ub<LI: IWLitIter>(lits: LI, b: isize) -> Self {
1155        let (lits, weight_sum, b_add) = PbConstraint::convert_input_lits(lits);
1156        PbConstraint::Ub(PbUbConstr {
1157            lits: lits.into_iter().collect(),
1158            weight_sum,
1159            b: b + b_add,
1160        })
1161    }
1162
1163    /// Constructs a new upper bound pseudo-boolean constraint (`weighted sum of lits <= b`)
1164    pub fn new_ub_unsigned<LI: WLitIter>(lits: LI, b: isize) -> Self {
1165        let lits: Vec<_> = lits.into_iter().collect();
1166        let weight_sum = lits.iter().fold(0, |sum, (_, w)| sum + w);
1167        PbConstraint::Ub(PbUbConstr {
1168            lits,
1169            weight_sum,
1170            b,
1171        })
1172    }
1173
1174    /// Constructs a new lower bound pseudo-boolean constraint (`weighted sum of lits >= b`)
1175    pub fn new_lb<LI: IWLitIter>(lits: LI, b: isize) -> Self {
1176        let (lits, weight_sum, b_add) = PbConstraint::convert_input_lits(lits);
1177        PbConstraint::Lb(PbLbConstr {
1178            lits: lits.into_iter().collect(),
1179            weight_sum,
1180            b: b + b_add,
1181        })
1182    }
1183
1184    /// Constructs a new lower bound pseudo-boolean constraint (`weighted sum of lits >= b`)
1185    pub fn new_lb_unsigned<LI: WLitIter>(lits: LI, b: isize) -> Self {
1186        let lits: Vec<_> = lits.into_iter().collect();
1187        let weight_sum = lits.iter().fold(0, |sum, (_, w)| sum + w);
1188        PbConstraint::Lb(PbLbConstr {
1189            lits,
1190            weight_sum,
1191            b,
1192        })
1193    }
1194
1195    /// Constructs a new equality pseudo-boolean constraint (`weighted sum of lits = b`)
1196    pub fn new_eq<LI: IWLitIter>(lits: LI, b: isize) -> Self {
1197        let (lits, weight_sum, b_add) = PbConstraint::convert_input_lits(lits);
1198        PbConstraint::Eq(PbEqConstr {
1199            lits: lits.into_iter().collect(),
1200            weight_sum,
1201            b: b + b_add,
1202        })
1203    }
1204
1205    /// Constructs a new equality pseudo-boolean constraint (`weighted sum of lits = b`)
1206    pub fn new_eq_unsigned<LI: WLitIter>(lits: LI, b: isize) -> Self {
1207        let lits: Vec<_> = lits.into_iter().collect();
1208        let weight_sum = lits.iter().fold(0, |sum, (_, w)| sum + w);
1209        PbConstraint::Eq(PbEqConstr {
1210            lits,
1211            weight_sum,
1212            b,
1213        })
1214    }
1215
1216    /// Gets the sum of weights of the constraint
1217    #[must_use]
1218    pub fn weight_sum(&self) -> usize {
1219        match self {
1220            PbConstraint::Ub(c) => c.weight_sum,
1221            PbConstraint::Lb(c) => c.weight_sum,
1222            PbConstraint::Eq(c) => c.weight_sum,
1223        }
1224    }
1225
1226    /// Gets mutable references to the underlying data
1227    fn get_data(&mut self) -> (&mut Vec<(Lit, usize)>, &mut usize, &mut isize) {
1228        match self {
1229            PbConstraint::Ub(constr) => (&mut constr.lits, &mut constr.weight_sum, &mut constr.b),
1230            PbConstraint::Lb(constr) => (&mut constr.lits, &mut constr.weight_sum, &mut constr.b),
1231            PbConstraint::Eq(constr) => (&mut constr.lits, &mut constr.weight_sum, &mut constr.b),
1232        }
1233    }
1234
1235    /// Sets the bound of the constraint
1236    pub fn set_bound(&mut self, bound: isize) {
1237        match self {
1238            PbConstraint::Ub(PbUbConstr { b, .. })
1239            | PbConstraint::Lb(PbLbConstr { b, .. })
1240            | PbConstraint::Eq(PbEqConstr { b, .. }) => *b = bound,
1241        }
1242    }
1243
1244    /// Adds literals to the cardinality constraint
1245    pub fn add<LI: IWLitIter>(&mut self, lits: LI) {
1246        let (lits, add_weight_sum, b_add) = PbConstraint::convert_input_lits(lits);
1247        let (data_lits, weight_sum, b) = self.get_data();
1248        data_lits.extend(lits);
1249        *weight_sum += add_weight_sum;
1250        *b += b_add;
1251    }
1252
1253    /// Gets the bound of the constraint
1254    #[must_use]
1255    pub fn bound(&self) -> isize {
1256        match self {
1257            PbConstraint::Ub(PbUbConstr { b, .. })
1258            | PbConstraint::Lb(PbLbConstr { b, .. })
1259            | PbConstraint::Eq(PbEqConstr { b, .. }) => *b,
1260        }
1261    }
1262
1263    /// Gets the number of literals in the constraint
1264    #[must_use]
1265    pub fn len(&self) -> usize {
1266        match self {
1267            PbConstraint::Ub(constr) => constr.lits.len(),
1268            PbConstraint::Lb(constr) => constr.lits.len(),
1269            PbConstraint::Eq(constr) => constr.lits.len(),
1270        }
1271    }
1272
1273    /// Checks whether the constraint is empty
1274    #[must_use]
1275    pub fn is_empty(&self) -> bool {
1276        self.len() == 0
1277    }
1278
1279    /// Gets the number of literals in the constraint
1280    #[must_use]
1281    pub fn n_lits(&self) -> usize {
1282        self.len()
1283    }
1284
1285    /// Checks if the constraint is always satisfied
1286    #[must_use]
1287    pub fn is_tautology(&self) -> bool {
1288        match self {
1289            PbConstraint::Ub(constr) => constr.is_tautology(),
1290            PbConstraint::Lb(constr) => constr.is_tautology(),
1291            PbConstraint::Eq(_) => false,
1292        }
1293    }
1294
1295    /// Checks if the constraint is unsatisfiable
1296    #[must_use]
1297    pub fn is_unsat(&self) -> bool {
1298        match self {
1299            PbConstraint::Ub(constr) => constr.is_unsat(),
1300            PbConstraint::Lb(constr) => constr.is_unsat(),
1301            PbConstraint::Eq(constr) => constr.is_unsat(),
1302        }
1303    }
1304
1305    /// Checks if the constraint assigns all literals to true
1306    #[must_use]
1307    pub fn is_positive_assignment(&self) -> bool {
1308        match self {
1309            PbConstraint::Ub(_) => false,
1310            PbConstraint::Lb(constr) => constr.is_positive_assignment(),
1311            PbConstraint::Eq(constr) => constr.is_positive_assignment(),
1312        }
1313    }
1314
1315    /// Checks if the constraint assigns all literals to false
1316    #[must_use]
1317    pub fn is_negative_assignment(&self) -> bool {
1318        match self {
1319            PbConstraint::Ub(constr) => constr.is_negative_assignment(),
1320            PbConstraint::Lb(_) => false,
1321            PbConstraint::Eq(constr) => constr.is_negative_assignment(),
1322        }
1323    }
1324
1325    /// Checks if the constraint is a cardinality constraint
1326    #[must_use]
1327    pub fn is_card(&self) -> bool {
1328        match self {
1329            PbConstraint::Ub(constr) => constr.find_unit_weight().is_some(),
1330            PbConstraint::Lb(constr) => constr.find_unit_weight().is_some(),
1331            PbConstraint::Eq(constr) => constr.find_unit_weight().is_some(),
1332        }
1333    }
1334
1335    /// Checks if the constraint is a clause
1336    #[must_use]
1337    pub fn is_clause(&self) -> bool {
1338        match self {
1339            PbConstraint::Ub(constr) => constr.is_clause(),
1340            PbConstraint::Lb(constr) => constr.is_clause(),
1341            PbConstraint::Eq(_) => false,
1342        }
1343    }
1344
1345    /// Normalizes the constraint. This sorts the literal and merges duplicates.
1346    /// Comparing two normalized constraints checks their logical equivalence.
1347    #[must_use]
1348    pub fn normalize(self) -> Self {
1349        let norm = |mut lits: Vec<(Lit, usize)>| {
1350            if lits.len() <= 1 {
1351                return lits;
1352            }
1353            lits.sort_unstable();
1354            let mut merged = Vec::new();
1355            for (l, w) in lits {
1356                match merged.last_mut() {
1357                    Some((l2, w2)) => {
1358                        if l == *l2 {
1359                            *w2 += w;
1360                        } else {
1361                            merged.push((l, w));
1362                        }
1363                    }
1364                    None => merged.push((l, w)),
1365                }
1366            }
1367            merged
1368        };
1369        match self {
1370            PbConstraint::Ub(constr) => PbConstraint::Ub(PbUbConstr {
1371                lits: norm(constr.lits),
1372                ..constr
1373            }),
1374            PbConstraint::Lb(constr) => PbConstraint::Lb(PbLbConstr {
1375                lits: norm(constr.lits),
1376                ..constr
1377            }),
1378            PbConstraint::Eq(constr) => PbConstraint::Eq(PbEqConstr {
1379                lits: norm(constr.lits),
1380                ..constr
1381            }),
1382        }
1383    }
1384
1385    /// Converts the pseudo-boolean constraint into a cardinality constraint, only if the
1386    /// constraint is already a cardinality
1387    ///
1388    /// # Errors
1389    ///
1390    /// If the constraint is not a cardinality constraint, including when it is a tautology or
1391    /// unsat, returns [`PbToCardError`]
1392    pub fn into_card_constr(self) -> Result<CardConstraint, PbToCardError> {
1393        if self.is_tautology() {
1394            return Err(PbToCardError::Tautology);
1395        }
1396        if self.is_unsat() {
1397            return Err(PbToCardError::Unsat);
1398        }
1399        Ok(match self {
1400            PbConstraint::Ub(constr) => {
1401                let unit_weight = constr.find_unit_weight();
1402                // since this is not unsat, b is non-negative
1403                let b = constr.b.unsigned_abs();
1404                match unit_weight {
1405                    None => return Err(PbToCardError::NotACard),
1406                    Some(unit_weight) => {
1407                        let lits = constr.lits.into_iter().map(|(l, _)| l);
1408                        CardConstraint::new_ub(lits, b / unit_weight)
1409                    }
1410                }
1411            }
1412            PbConstraint::Lb(constr) => {
1413                let unit_weight = constr.find_unit_weight();
1414                // since this is not a tautology, b is non-negative
1415                let b = constr.b.unsigned_abs();
1416                match unit_weight {
1417                    None => return Err(PbToCardError::NotACard),
1418                    Some(unit_weight) => {
1419                        let lits = constr.lits.into_iter().map(|(l, _)| l);
1420                        CardConstraint::new_lb(
1421                            lits,
1422                            b / unit_weight + usize::from(b % unit_weight != 0),
1423                        )
1424                    }
1425                }
1426            }
1427            PbConstraint::Eq(constr) => {
1428                let unit_weight = constr.find_unit_weight();
1429                // since this is not unsat, b is non-negative
1430                let b = constr.b.unsigned_abs();
1431                match unit_weight {
1432                    None => return Err(PbToCardError::NotACard),
1433                    Some(unit_weight) => {
1434                        if b % unit_weight != 0 {
1435                            return Err(PbToCardError::Unsat);
1436                        }
1437                        let lits = constr.lits.into_iter().map(|(l, _)| l);
1438                        CardConstraint::new_eq(lits, b / unit_weight)
1439                    }
1440                }
1441            }
1442        })
1443    }
1444
1445    /// Extends the constraint into a cardinality constraint by repeating literals as often as
1446    /// their coefficient requires
1447    ///
1448    /// # Panics
1449    ///
1450    /// If the constraint is UNSAT or a tautology
1451    #[must_use]
1452    pub fn extend_to_card_constr(self) -> CardConstraint {
1453        match self {
1454            PbConstraint::Ub(constr) => CardConstraint::Ub(constr.extend_to_card_constr()),
1455            PbConstraint::Lb(constr) => CardConstraint::Lb(constr.extend_to_card_constr()),
1456            PbConstraint::Eq(constr) => CardConstraint::Eq(constr.extend_to_card_constr()),
1457        }
1458    }
1459
1460    /// Converts the constraint into a clause, if possible
1461    ///
1462    /// # Errors
1463    ///
1464    /// If the constraint is not a clause, returns [`RequiresClausal`]
1465    pub fn into_clause(self) -> Result<Clause, RequiresClausal> {
1466        if !self.is_clause() {
1467            return Err(RequiresClausal);
1468        }
1469        match self {
1470            PbConstraint::Ub(constr) => Ok(constr.lits.into_iter().map(|(lit, _)| !lit).collect()),
1471            PbConstraint::Lb(constr) => Ok(constr.lits.into_iter().map(|(lit, _)| lit).collect()),
1472            PbConstraint::Eq(_) => unreachable!(),
1473        }
1474    }
1475
1476    /// Gets the (positively) weighted literals that are in the constraint
1477    #[must_use]
1478    pub fn into_lits(self) -> impl WLitIter {
1479        match self {
1480            PbConstraint::Ub(constr) => constr.lits,
1481            PbConstraint::Lb(constr) => constr.lits,
1482            PbConstraint::Eq(constr) => constr.lits,
1483        }
1484    }
1485
1486    /// Gets an iterator over the literals in the constraint
1487    #[inline]
1488    pub fn iter(&self) -> std::slice::Iter<'_, (Lit, usize)> {
1489        match self {
1490            PbConstraint::Ub(constr) => constr.lits.iter(),
1491            PbConstraint::Lb(constr) => constr.lits.iter(),
1492            PbConstraint::Eq(constr) => constr.lits.iter(),
1493        }
1494    }
1495
1496    /// Gets a mutable iterator over the literals in the constraint
1497    #[inline]
1498    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, (Lit, usize)> {
1499        match self {
1500            PbConstraint::Ub(constr) => constr.lits.iter_mut(),
1501            PbConstraint::Lb(constr) => constr.lits.iter_mut(),
1502            PbConstraint::Eq(constr) => constr.lits.iter_mut(),
1503        }
1504    }
1505
1506    /// Checks whether the PB constraint is satisfied by the given assignment
1507    #[must_use]
1508    pub fn evaluate(&self, assign: &Assignment) -> TernaryVal {
1509        #[allow(clippy::range_plus_one)]
1510        let range = self
1511            .iter()
1512            .fold(0..0, |rng, &(lit, coeff)| match assign.lit_value(lit) {
1513                TernaryVal::True => rng.start + coeff..rng.end + coeff,
1514                TernaryVal::False => rng,
1515                TernaryVal::DontCare => rng.start..rng.end + coeff,
1516            });
1517        match (isize::try_from(range.start), isize::try_from(range.end)) {
1518            (Ok(start), Ok(end)) => {
1519                if (start..end).contains(&self.bound()) {
1520                    return TernaryVal::DontCare;
1521                }
1522                match self {
1523                    PbConstraint::Ub(PbUbConstr { b, .. }) => (start <= *b).into(),
1524                    PbConstraint::Lb(PbLbConstr { b, .. }) => (start >= *b).into(),
1525                    PbConstraint::Eq(PbEqConstr { b, .. }) => (start == *b).into(),
1526                }
1527            }
1528            (Ok(start), Err(_)) => match self {
1529                PbConstraint::Ub(PbUbConstr { b, .. }) => {
1530                    if (start..).contains(b) {
1531                        return TernaryVal::DontCare;
1532                    }
1533                    TernaryVal::True
1534                }
1535                PbConstraint::Lb(PbLbConstr { b, .. }) | PbConstraint::Eq(PbEqConstr { b, .. }) => {
1536                    if (start..).contains(b) {
1537                        return TernaryVal::DontCare;
1538                    }
1539                    TernaryVal::False
1540                }
1541            },
1542            (Err(_), Err(_)) => matches!(self, PbConstraint::Lb(_)).into(),
1543            (Err(_), Ok(_)) => unreachable!(), // since end >= start
1544        }
1545    }
1546}
1547
1548#[cfg(feature = "proof-logging")]
1549impl pigeons::ConstraintLike<crate::types::Var> for PbConstraint {
1550    fn rhs(&self) -> isize {
1551        match self {
1552            PbConstraint::Ub(c) => {
1553                isize::try_from(c.weight_sum).expect("can handle at most `isize::MAX` weight sum")
1554                    - c.b
1555            }
1556            PbConstraint::Lb(c) => c.b,
1557            PbConstraint::Eq(_) => {
1558                panic!("VeriPB does not support equality constraints in the proof")
1559            }
1560        }
1561    }
1562
1563    fn sum_iter(&self) -> impl Iterator<Item = (isize, pigeons::Axiom<crate::types::Var>)> {
1564        match self {
1565            PbConstraint::Ub(PbUbConstr { lits, .. }) => PigeonWLitIter {
1566                lits: lits.iter(),
1567                negate: true,
1568            },
1569            PbConstraint::Lb(PbLbConstr { lits, .. })
1570            | PbConstraint::Eq(PbEqConstr { lits, .. }) => PigeonWLitIter {
1571                lits: lits.iter(),
1572                negate: false,
1573            },
1574        }
1575    }
1576}
1577
1578#[cfg(feature = "proof-logging")]
1579struct PigeonWLitIter<'a> {
1580    lits: std::slice::Iter<'a, (Lit, usize)>,
1581    negate: bool,
1582}
1583
1584#[cfg(feature = "proof-logging")]
1585impl Iterator for PigeonWLitIter<'_> {
1586    type Item = (isize, pigeons::Axiom<crate::types::Var>);
1587
1588    fn next(&mut self) -> Option<Self::Item> {
1589        self.lits.next().map(|(l, w)| {
1590            (
1591                isize::try_from(*w).expect("can only handle coefficients up to `isize::MAX`"),
1592                if self.negate {
1593                    pigeons::Axiom::from(!*l)
1594                } else {
1595                    pigeons::Axiom::from(*l)
1596                },
1597            )
1598        })
1599    }
1600}
1601
1602impl<'slf> IntoIterator for &'slf PbConstraint {
1603    type Item = &'slf (Lit, usize);
1604
1605    type IntoIter = std::slice::Iter<'slf, (Lit, usize)>;
1606
1607    fn into_iter(self) -> Self::IntoIter {
1608        self.iter()
1609    }
1610}
1611
1612impl<'slf> IntoIterator for &'slf mut PbConstraint {
1613    type Item = &'slf mut (Lit, usize);
1614
1615    type IntoIter = std::slice::IterMut<'slf, (Lit, usize)>;
1616
1617    fn into_iter(self) -> Self::IntoIter {
1618        self.iter_mut()
1619    }
1620}
1621
1622impl From<Clause> for PbConstraint {
1623    fn from(value: Clause) -> Self {
1624        PbConstraint::new_lb(value.into_iter().map(|l| (l, 1)), 1)
1625    }
1626}
1627
1628impl From<CardConstraint> for PbConstraint {
1629    fn from(value: CardConstraint) -> Self {
1630        match value {
1631            CardConstraint::Ub(constr) => PbConstraint::new_ub(
1632                constr.lits.into_iter().map(|l| (l, 1)),
1633                isize::try_from(constr.b).expect("cannot handle bounds higher than `isize::MAX`"),
1634            ),
1635            CardConstraint::Lb(constr) => PbConstraint::new_lb(
1636                constr.lits.into_iter().map(|l| (l, 1)),
1637                isize::try_from(constr.b).expect("cannot handle bounds higher than `isize::MAX`"),
1638            ),
1639            CardConstraint::Eq(constr) => PbConstraint::new_eq(
1640                constr.lits.into_iter().map(|l| (l, 1)),
1641                isize::try_from(constr.b).expect("cannot handle bounds higher than `isize::MAX`"),
1642            ),
1643        }
1644    }
1645}
1646
1647/// An upper bound pseudo-boolean constraint (`weighted sum of lits <= b`)
1648#[derive(Eq, PartialEq, Clone, Debug)]
1649#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1650pub struct PbUbConstr {
1651    lits: Vec<(Lit, usize)>,
1652    weight_sum: usize,
1653    b: isize,
1654}
1655
1656impl fmt::Display for PbUbConstr {
1657    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1658        write_wlit_sum!(f, self.lits);
1659        write!(f, " <= {}", self.b)
1660    }
1661}
1662
1663impl PbUbConstr {
1664    /// Decomposes the constraint to a set of input literals and an upper bound
1665    #[must_use]
1666    pub fn decompose(self) -> (Vec<(Lit, usize)>, isize) {
1667        (self.lits, self.b)
1668    }
1669
1670    /// Gets references to the constraints internals
1671    pub(crate) fn decompose_ref(&self) -> (&Vec<(Lit, usize)>, &isize) {
1672        (&self.lits, &self.b)
1673    }
1674
1675    /// Converts the upper bound constraint into an equivalent lower bound constraint
1676    ///
1677    /// # Panics
1678    ///
1679    /// If the sum of weights is larger than [`isize::MAX`]
1680    #[must_use]
1681    pub fn invert(mut self) -> PbLbConstr {
1682        self.lits.iter_mut().for_each(|(l, _)| {
1683            *l = !*l;
1684        });
1685        PbLbConstr {
1686            lits: self.lits,
1687            weight_sum: self.weight_sum,
1688            b: -self.b
1689                + isize::try_from(self.weight_sum)
1690                    .expect("cannot handle weight sum larger than `isize::MAX`"),
1691        }
1692    }
1693
1694    /// Extends the constraint into a cardinality constraint by repeating literals as often as
1695    /// their coefficient requires
1696    ///
1697    /// # Panics
1698    ///
1699    /// If the constraint is UNSAT
1700    #[must_use]
1701    pub fn extend_to_card_constr(self) -> CardUbConstr {
1702        CardUbConstr {
1703            lits: self
1704                .lits
1705                .into_iter()
1706                .flat_map(|(l, w)| std::iter::repeat(l).take(w))
1707                .collect(),
1708            b: self.b.unsigned_abs(),
1709        }
1710    }
1711
1712    /// Checks if the constraint is always satisfied
1713    #[must_use]
1714    pub fn is_tautology(&self) -> bool {
1715        if self.b < 0 {
1716            return false;
1717        }
1718        self.b.unsigned_abs() >= self.weight_sum
1719    }
1720
1721    /// Checks if the constraint is unsatisfiable
1722    #[must_use]
1723    pub fn is_unsat(&self) -> bool {
1724        self.b < 0
1725    }
1726
1727    /// Checks if the constraint assigns all literals to false
1728    #[must_use]
1729    pub fn is_negative_assignment(&self) -> bool {
1730        if self.is_unsat() {
1731            return false;
1732        }
1733        let min_coeff: usize = self
1734            .lits
1735            .iter()
1736            .fold(usize::MAX, |min, (_, coeff)| std::cmp::min(min, *coeff));
1737        // absolute is safe since is not unsat
1738        min_coeff > self.b.unsigned_abs()
1739    }
1740
1741    /// Gets the unit weight of the constraint, if it exists
1742    #[must_use]
1743    pub fn find_unit_weight(&self) -> Option<usize> {
1744        let mut unit_weight = None;
1745        for (_, w) in &self.lits {
1746            if let Some(uw) = unit_weight {
1747                if uw != *w {
1748                    return None;
1749                }
1750            } else {
1751                unit_weight = Some(*w);
1752            }
1753        }
1754        unit_weight
1755    }
1756
1757    /// Checks if the constraint is a clause
1758    #[must_use]
1759    pub fn is_clause(&self) -> bool {
1760        if self.is_tautology() {
1761            return false;
1762        }
1763        if self.is_unsat() {
1764            return false;
1765        }
1766        let min_coeff: usize = self
1767            .lits
1768            .iter()
1769            .fold(usize::MAX, |min, (_, coeff)| std::cmp::min(min, *coeff));
1770        // self.b >= 0 since not unsat
1771        self.weight_sum <= self.b.unsigned_abs() + min_coeff
1772            && self.weight_sum > self.b.unsigned_abs()
1773    }
1774}
1775
1776/// A lower bound pseudo-boolean constraint (`weighted sum of lits >= b`)
1777#[derive(Eq, PartialEq, Clone, Debug)]
1778#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1779pub struct PbLbConstr {
1780    lits: Vec<(Lit, usize)>,
1781    weight_sum: usize,
1782    b: isize,
1783}
1784
1785impl fmt::Display for PbLbConstr {
1786    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1787        write_wlit_sum!(f, self.lits);
1788        write!(f, " >= {}", self.b)
1789    }
1790}
1791
1792impl PbLbConstr {
1793    /// Decomposes the constraint to a set of input literals and a lower bound
1794    #[must_use]
1795    pub fn decompose(self) -> (Vec<(Lit, usize)>, isize) {
1796        (self.lits, self.b)
1797    }
1798
1799    /// Gets references to the constraints internals
1800    pub(crate) fn decompose_ref(&self) -> (&Vec<(Lit, usize)>, &isize) {
1801        (&self.lits, &self.b)
1802    }
1803
1804    /// Converts the lower bound constraint into an equivalent upper bound constraint
1805    ///
1806    /// # Panics
1807    ///
1808    /// If the sum of weights is larger than [`isize::MAX`]
1809    #[must_use]
1810    pub fn invert(mut self) -> PbUbConstr {
1811        self.lits.iter_mut().for_each(|(l, _)| {
1812            *l = !*l;
1813        });
1814        PbUbConstr {
1815            lits: self.lits,
1816            weight_sum: self.weight_sum,
1817            b: -self.b
1818                + isize::try_from(self.weight_sum)
1819                    .expect("cannot handle weight sum larger than `isize::MAX`"),
1820        }
1821    }
1822
1823    /// Extends the constraint into a cardinality constraint by repeating literals as often as
1824    /// their coefficient requires
1825    ///
1826    /// # Panics
1827    ///
1828    /// If the constraint is a tautology
1829    #[must_use]
1830    pub fn extend_to_card_constr(self) -> CardLbConstr {
1831        CardLbConstr {
1832            lits: self
1833                .lits
1834                .into_iter()
1835                .flat_map(|(l, w)| std::iter::repeat(l).take(w))
1836                .collect(),
1837            b: self.b.unsigned_abs(),
1838        }
1839    }
1840
1841    /// Checks if the constraint is always satisfied
1842    #[must_use]
1843    pub fn is_tautology(&self) -> bool {
1844        self.b <= 0
1845    }
1846
1847    /// Checks if the constraint is unsatisfiable
1848    #[must_use]
1849    pub fn is_unsat(&self) -> bool {
1850        if self.b < 0 {
1851            return false;
1852        }
1853        self.b.unsigned_abs() > self.weight_sum
1854    }
1855
1856    /// Checks if the constraint assigns all literals to true
1857    #[must_use]
1858    pub fn is_positive_assignment(&self) -> bool {
1859        if self.b < 0 || self.is_unsat() {
1860            return false;
1861        }
1862        let min_coeff: usize = self
1863            .lits
1864            .iter()
1865            .fold(usize::MAX, |min, (_, coeff)| std::cmp::min(min, *coeff));
1866        // note: self.b <= self.weight_sum is checked in is_unsat
1867        self.b.unsigned_abs() + min_coeff > self.weight_sum
1868    }
1869
1870    /// Gets the unit weight of the constraint, if it exists
1871    #[must_use]
1872    pub fn find_unit_weight(&self) -> Option<usize> {
1873        let mut unit_weight = None;
1874        for (_, w) in &self.lits {
1875            if let Some(uw) = unit_weight {
1876                if uw != *w {
1877                    return None;
1878                }
1879            } else {
1880                unit_weight = Some(*w);
1881            }
1882        }
1883        unit_weight
1884    }
1885
1886    /// Checks if the constraint is a clause
1887    #[must_use]
1888    pub fn is_clause(&self) -> bool {
1889        if self.is_tautology() {
1890            return false;
1891        }
1892        if self.is_unsat() {
1893            return false;
1894        }
1895        let min_coeff: usize = self
1896            .lits
1897            .iter()
1898            .fold(usize::MAX, |min, (_, coeff)| std::cmp::min(min, *coeff));
1899        // self.b > 0 because is not tautology
1900        self.b.unsigned_abs() <= min_coeff
1901    }
1902}
1903
1904/// An equality pseudo-boolean constraint (`weighted sum of lits = b`)
1905#[derive(Eq, PartialEq, Clone, Debug)]
1906#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1907pub struct PbEqConstr {
1908    lits: Vec<(Lit, usize)>,
1909    weight_sum: usize,
1910    b: isize,
1911}
1912
1913impl fmt::Display for PbEqConstr {
1914    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1915        write_wlit_sum!(f, self.lits);
1916        write!(f, " = {}", self.b)
1917    }
1918}
1919
1920impl PbEqConstr {
1921    /// Decomposes the constraint to a set of input literals and an equality bound
1922    #[must_use]
1923    pub fn decompose(self) -> (Vec<(Lit, usize)>, isize) {
1924        (self.lits, self.b)
1925    }
1926
1927    /// Gets references to the constraints internals
1928    pub(crate) fn decompose_ref(&self) -> (&Vec<(Lit, usize)>, &isize) {
1929        (&self.lits, &self.b)
1930    }
1931
1932    /// Splits the equality constraint into a lower bound and an upper bound constraint
1933    #[must_use]
1934    pub fn split(self) -> (PbLbConstr, PbUbConstr) {
1935        (
1936            PbLbConstr {
1937                lits: self.lits.clone(),
1938                weight_sum: self.weight_sum,
1939                b: self.b,
1940            },
1941            PbUbConstr {
1942                lits: self.lits,
1943                weight_sum: self.weight_sum,
1944                b: self.b,
1945            },
1946        )
1947    }
1948
1949    /// Extends the constraint into a cardinality constraint by repeating literals as often as
1950    /// their coefficient requires
1951    ///
1952    /// # Panics
1953    ///
1954    /// If the constraint is UNSAT
1955    #[must_use]
1956    pub fn extend_to_card_constr(self) -> CardEqConstr {
1957        CardEqConstr {
1958            lits: self
1959                .lits
1960                .into_iter()
1961                .flat_map(|(l, w)| std::iter::repeat(l).take(w))
1962                .collect(),
1963            b: self.b.unsigned_abs(),
1964        }
1965    }
1966
1967    /// Checks if the constraint is unsatisfiable
1968    ///
1969    /// This only checks whether the bound is smaller than the sum of weights, not if a subset sum
1970    /// of the inputs exists that can satisfy the equality.
1971    #[must_use]
1972    pub fn is_unsat(&self) -> bool {
1973        if self.b < 0 {
1974            return true;
1975        }
1976        self.b.unsigned_abs() > self.weight_sum
1977    }
1978
1979    /// Checks if the constraint assigns all literals to true
1980    #[must_use]
1981    pub fn is_positive_assignment(&self) -> bool {
1982        if self.b < 0 {
1983            return false;
1984        }
1985        self.b.unsigned_abs() == self.weight_sum
1986    }
1987
1988    /// Checks if the constraint assigns all literals to false
1989    #[must_use]
1990    pub fn is_negative_assignment(&self) -> bool {
1991        if self.b < 0 {
1992            return false;
1993        }
1994        self.b.unsigned_abs() == 0
1995    }
1996
1997    /// Gets the unit weight of the constraint, if it exists
1998    #[must_use]
1999    pub fn find_unit_weight(&self) -> Option<usize> {
2000        let mut unit_weight = None;
2001        for (_, w) in &self.lits {
2002            if let Some(uw) = unit_weight {
2003                if uw != *w {
2004                    return None;
2005                }
2006            } else {
2007                unit_weight = Some(*w);
2008            }
2009        }
2010        unit_weight
2011    }
2012}
2013
2014#[cfg(test)]
2015mod tests {
2016    use super::{CardConstraint, Cl, PbConstraint};
2017    use crate::{
2018        lit,
2019        types::{Assignment, TernaryVal},
2020        var,
2021    };
2022
2023    #[test]
2024    fn clause_remove() {
2025        let mut cl = clause![lit![0], lit![1], lit![2], lit![1]];
2026        assert!(!cl.remove(lit![3]));
2027        assert!(cl.remove(lit![1]));
2028        assert_eq!(cl.len(), 3);
2029    }
2030
2031    #[test]
2032    fn clause_remove_thorough() {
2033        let mut cl = clause![lit![0], lit![1], lit![2], lit![1]];
2034        assert!(!cl.remove_thorough(lit![3]));
2035        assert!(cl.remove_thorough(lit![1]));
2036        assert_eq!(cl.len(), 2);
2037    }
2038
2039    #[test]
2040    fn clause_normalize() {
2041        let taut = clause![lit![0], lit![1], lit![2], lit![3], !lit![2]];
2042        assert_eq!(taut.normalize(), None);
2043        let cl = clause![
2044            lit![5],
2045            !lit![2],
2046            !lit![3],
2047            lit![17],
2048            lit![0],
2049            lit![1],
2050            !lit![2]
2051        ];
2052        assert_eq!(
2053            cl.normalize(),
2054            Some(clause![
2055                lit![0],
2056                lit![1],
2057                !lit![2],
2058                !lit![3],
2059                lit![5],
2060                lit![17]
2061            ])
2062        );
2063    }
2064
2065    macro_rules! assign {
2066        ($val:expr) => {{
2067            let mut assign = Assignment::default();
2068            assign.assign_var(var![0], (($val & 1) == 1).into());
2069            assign.assign_var(var![1], ((($val >> 1) & 1) == 1).into());
2070            assign.assign_var(var![2], (($val >> 2) & 1 == 1).into());
2071            assign
2072        }};
2073    }
2074
2075    #[test]
2076    fn clause_evaluate() {
2077        let cl = clause![lit![0], lit![1], lit![2]];
2078        assert_eq!(cl.evaluate(&assign!(0b000)), TernaryVal::False);
2079        assert_eq!(cl.evaluate(&assign!(0b001)), TernaryVal::True);
2080        assert_eq!(cl.evaluate(&assign!(0b010)), TernaryVal::True);
2081        assert_eq!(cl.evaluate(&assign!(0b011)), TernaryVal::True);
2082        assert_eq!(cl.evaluate(&assign!(0b100)), TernaryVal::True);
2083        assert_eq!(cl.evaluate(&assign!(0b101)), TernaryVal::True);
2084        assert_eq!(cl.evaluate(&assign!(0b110)), TernaryVal::True);
2085        assert_eq!(cl.evaluate(&assign!(0b111)), TernaryVal::True);
2086        assert_eq!(
2087            cl.evaluate(&assign!(0b000).truncate(var![1])),
2088            TernaryVal::DontCare
2089        );
2090    }
2091
2092    #[test]
2093    fn clause_is_tautology() {
2094        assert!(!Cl::new(&[lit![0], lit![1], lit![2]]).is_tautology());
2095        assert!(Cl::new(&[lit![0], !lit![0], lit![2]]).is_tautology());
2096        assert!(Cl::new(&[!lit![2], !lit![0], lit![2]]).is_tautology());
2097    }
2098
2099    #[test]
2100    fn card_is_tautology() {
2101        let lits = vec![lit![0], lit![1], lit![2]];
2102        assert!(CardConstraint::new_ub(lits.clone(), 3).is_tautology());
2103        assert!(!CardConstraint::new_ub(lits.clone(), 2).is_tautology());
2104        assert!(CardConstraint::new_lb(lits.clone(), 0).is_tautology());
2105        assert!(!CardConstraint::new_lb(lits.clone(), 1).is_tautology());
2106        assert!(!CardConstraint::new_eq(lits.clone(), 2).is_tautology());
2107    }
2108
2109    #[test]
2110    fn card_is_unsat() {
2111        let lits = vec![lit![0], lit![1], lit![2]];
2112        assert!(!CardConstraint::new_ub(lits.clone(), 1).is_unsat());
2113        assert!(!CardConstraint::new_lb(lits.clone(), 3).is_unsat());
2114        assert!(CardConstraint::new_lb(lits.clone(), 4).is_unsat());
2115        assert!(!CardConstraint::new_eq(lits.clone(), 2).is_unsat());
2116        assert!(CardConstraint::new_eq(lits.clone(), 4).is_unsat());
2117    }
2118
2119    #[test]
2120    fn card_is_positive_assignment() {
2121        let lits = vec![lit![0], lit![1], lit![2]];
2122        assert!(!CardConstraint::new_ub(lits.clone(), 1).is_positive_assignment());
2123        assert!(CardConstraint::new_lb(lits.clone(), 3).is_positive_assignment());
2124        assert!(!CardConstraint::new_lb(lits.clone(), 2).is_positive_assignment());
2125        assert!(CardConstraint::new_eq(lits.clone(), 3).is_positive_assignment());
2126        assert!(!CardConstraint::new_eq(lits.clone(), 2).is_positive_assignment());
2127    }
2128
2129    #[test]
2130    fn card_is_negative_assignment() {
2131        let lits = vec![lit![0], lit![1], lit![2]];
2132        assert!(CardConstraint::new_ub(lits.clone(), 0).is_negative_assignment());
2133        assert!(!CardConstraint::new_ub(lits.clone(), 1).is_negative_assignment());
2134        assert!(!CardConstraint::new_lb(lits.clone(), 2).is_negative_assignment());
2135        assert!(CardConstraint::new_eq(lits.clone(), 0).is_negative_assignment());
2136        assert!(!CardConstraint::new_eq(lits.clone(), 1).is_negative_assignment());
2137    }
2138
2139    #[test]
2140    fn card_is_clause() {
2141        let lits = vec![lit![0], lit![1], lit![2]];
2142        assert!(!CardConstraint::new_ub(lits.clone(), 1).is_clause());
2143        assert!(!CardConstraint::new_lb(lits.clone(), 3).is_clause());
2144        assert!(CardConstraint::new_lb(lits.clone(), 1).is_clause());
2145        assert!(!CardConstraint::new_eq(lits.clone(), 2).is_clause());
2146    }
2147
2148    #[test]
2149    fn pb_is_tautology() {
2150        let lits = vec![(lit![0], 1), (lit![1], 2), (lit![2], 3)];
2151        assert!(PbConstraint::new_ub(lits.clone(), 6).is_tautology());
2152        assert!(!PbConstraint::new_ub(lits.clone(), 5).is_tautology());
2153        assert!(PbConstraint::new_lb(lits.clone(), 0).is_tautology());
2154        assert!(!PbConstraint::new_lb(lits.clone(), 1).is_tautology());
2155        assert!(!PbConstraint::new_eq(lits.clone(), 2).is_tautology());
2156    }
2157
2158    #[test]
2159    fn pb_is_unsat() {
2160        let lits = vec![(lit![0], 1), (lit![1], 2), (lit![2], 3)];
2161        assert!(!PbConstraint::new_ub(lits.clone(), 1).is_unsat());
2162        assert!(!PbConstraint::new_lb(lits.clone(), 6).is_unsat());
2163        assert!(PbConstraint::new_lb(lits.clone(), 7).is_unsat());
2164        assert!(!PbConstraint::new_eq(lits.clone(), 2).is_unsat());
2165        assert!(PbConstraint::new_eq(lits.clone(), 7).is_unsat());
2166    }
2167
2168    #[test]
2169    fn pb_is_positive_assignment() {
2170        let lits = vec![(lit![0], 1), (lit![1], 2), (lit![2], 3)];
2171        assert!(!PbConstraint::new_ub(lits.clone(), 1).is_positive_assignment());
2172        assert!(PbConstraint::new_lb(lits.clone(), 6).is_positive_assignment());
2173        assert!(!PbConstraint::new_lb(lits.clone(), 2).is_positive_assignment());
2174        assert!(PbConstraint::new_eq(lits.clone(), 6).is_positive_assignment());
2175        assert!(!PbConstraint::new_eq(lits.clone(), 2).is_positive_assignment());
2176    }
2177
2178    #[test]
2179    fn pb_is_negative_assignment() {
2180        let lits = vec![(lit![0], 2), (lit![1], 2), (lit![2], 3)];
2181        assert!(PbConstraint::new_ub(lits.clone(), 1).is_negative_assignment());
2182        assert!(!PbConstraint::new_ub(lits.clone(), 2).is_negative_assignment());
2183        assert!(!PbConstraint::new_lb(lits.clone(), 2).is_negative_assignment());
2184        assert!(PbConstraint::new_eq(lits.clone(), 0).is_negative_assignment());
2185        assert!(!PbConstraint::new_eq(lits.clone(), 1).is_negative_assignment());
2186    }
2187
2188    #[test]
2189    fn pb_is_card() {
2190        let lits = vec![(lit![0], 2), (lit![1], 2), (lit![2], 2)];
2191        assert!(PbConstraint::new_ub(lits.clone(), 1).is_card());
2192        assert!(PbConstraint::new_lb(lits.clone(), 3).is_card());
2193        assert!(PbConstraint::new_eq(lits.clone(), 2).is_card());
2194        let lits = vec![(lit![0], 2), (lit![1], 1), (lit![2], 2)];
2195        assert!(!PbConstraint::new_ub(lits.clone(), 1).is_card());
2196        assert!(!PbConstraint::new_lb(lits.clone(), 3).is_card());
2197        assert!(!PbConstraint::new_eq(lits.clone(), 2).is_card());
2198    }
2199
2200    #[test]
2201    fn pb_is_clause() {
2202        let lits = vec![(lit![0], 2), (lit![1], 3), (lit![2], 2)];
2203        assert!(PbConstraint::new_ub(lits.clone(), 6).is_clause());
2204        assert!(PbConstraint::new_lb(lits.clone(), 2).is_clause());
2205        assert!(!PbConstraint::new_ub(lits.clone(), 7).is_clause());
2206        assert!(!PbConstraint::new_ub(lits.clone(), 4).is_clause());
2207        assert!(!PbConstraint::new_lb(lits.clone(), 3).is_clause());
2208        assert!(!PbConstraint::new_eq(lits.clone(), 2).is_card());
2209    }
2210}