1use core::ffi::c_int;
6use std::{
7 fmt,
8 ops::{self, Index, IndexMut},
9 path::Path,
10};
11
12use anyhow::Context;
13use thiserror::Error;
14
15pub mod constraints;
16pub use constraints::{Cl, Clause};
17
18use crate::instances::fio::{self, SolverOutput};
19
20#[cfg(feature = "fxhash")]
22pub type RsHashMap<K, V> = rustc_hash::FxHashMap<K, V>;
23#[cfg(not(feature = "fxhash"))]
25pub type RsHashMap<K, V> = std::collections::HashMap<K, V>;
26
27#[cfg(feature = "fxhash")]
29pub type RsHashSet<V> = rustc_hash::FxHashSet<V>;
30#[cfg(not(feature = "fxhash"))]
32pub type RsHashSet<V> = std::collections::HashSet<V>;
33
34#[cfg(feature = "fxhash")]
36pub type RsHasher = rustc_hash::FxHasher;
37#[cfg(not(feature = "fxhash"))]
39pub type RsHasher = std::collections::hash_map::DefaultHasher;
40
41#[derive(Hash, Eq, PartialEq, PartialOrd, Clone, Copy, Ord)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[cfg_attr(feature = "serde", serde(try_from = "u32"))]
45#[repr(transparent)]
46struct LimitedU32<const U: u32>(u32);
47
48impl<const U: u32> TryFrom<u32> for LimitedU32<U> {
49 type Error = LimitedU32Error<U>;
50
51 fn try_from(value: u32) -> Result<Self, Self::Error> {
52 if value > U {
53 Err(LimitedU32Error(()))
54 } else {
55 Ok(Self(value))
56 }
57 }
58}
59
60#[derive(thiserror::Error, Debug)]
62struct LimitedU32Error<const U: u32>(());
63
64impl<const U: u32> std::fmt::Display for LimitedU32Error<U> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "value must at most be {U}")
67 }
68}
69
70const MAX_VAR_IDX: u32 = (u32::MAX - 1) / 2;
71
72#[derive(Hash, Eq, PartialEq, PartialOrd, Clone, Copy, Ord)]
77#[allow(clippy::unsafe_derive_deserialize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[repr(transparent)]
80#[allow(clippy::unsafe_derive_deserialize)]
82pub struct Var {
83 idx: LimitedU32<MAX_VAR_IDX>,
84}
85
86impl Var {
87 pub const MAX_IDX: u32 = MAX_VAR_IDX;
89
90 #[must_use]
97 pub const fn new(idx: u32) -> Var {
98 assert!(idx <= Var::MAX_IDX, "variable index too high");
99 Var {
100 idx: LimitedU32(idx),
101 }
102 }
103
104 pub fn new_with_error(idx: u32) -> Result<Var, TypeError> {
111 Ok(Var {
112 idx: idx
113 .try_into()
114 .map_err(|_| TypeError::IdxTooHigh(idx, Var::MAX_IDX))?,
115 })
116 }
117
118 #[inline]
127 #[must_use]
128 pub const unsafe fn new_unchecked(idx: u32) -> Var {
129 debug_assert!(idx <= Var::MAX_IDX);
130 Var {
131 idx: LimitedU32(idx),
132 }
133 }
134
135 #[inline]
146 #[must_use]
147 pub const fn lit(self, negated: bool) -> Lit {
148 unsafe { Lit::new_unchecked(self.idx.0, negated) }
149 }
150
151 #[inline]
162 #[must_use]
163 pub const fn pos_lit(self) -> Lit {
164 unsafe { Lit::positive_unchecked(self.idx.0) }
165 }
166
167 #[inline]
178 #[must_use]
179 pub const fn neg_lit(self) -> Lit {
180 unsafe { Lit::negative_unchecked(self.idx.0) }
181 }
182
183 #[inline]
196 #[must_use]
197 pub fn idx(self) -> usize {
198 self.idx.0 as usize
199 }
200
201 #[inline]
211 #[must_use]
212 pub fn idx32(self) -> u32 {
213 self.idx.0
214 }
215
216 #[must_use]
227 pub fn to_ipasir(self) -> c_int {
228 (self.idx32() + 1)
229 .try_into()
230 .expect("variable index too high to fit in c_int")
231 }
232
233 pub fn to_ipasir_with_error(self) -> Result<c_int, TypeError> {
244 (self.idx32() + 1)
245 .try_into()
246 .map_err(|_| TypeError::IdxTooHigh(self.idx32() + 1, c_int::MAX as u32))
247 }
248}
249
250impl ops::Add<u32> for Var {
252 type Output = Var;
253
254 fn add(self, rhs: u32) -> Self::Output {
255 let idx = self.idx.0 + rhs;
256 debug_assert!(idx <= Var::MAX_IDX, "variable index overflow");
257 Var {
258 idx: LimitedU32(idx),
259 }
260 }
261}
262
263impl ops::AddAssign<u32> for Var {
264 fn add_assign(&mut self, rhs: u32) {
265 debug_assert!(self.idx.0 + rhs <= Var::MAX_IDX, "variable index overflow");
266 self.idx.0 += rhs;
267 }
268}
269
270impl ops::Sub<u32> for Var {
272 type Output = Var;
273
274 fn sub(self, rhs: u32) -> Self::Output {
275 Var {
276 idx: LimitedU32(self.idx.0 - rhs),
277 }
278 }
279}
280
281impl ops::SubAssign<u32> for Var {
282 fn sub_assign(&mut self, rhs: u32) {
283 self.idx.0 -= rhs;
284 }
285}
286
287impl fmt::Display for Var {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 if cfg!(feature = "ipasir-display") {
291 write!(f, "x{}", self.to_ipasir())
292 } else {
293 write!(f, "x{}", self.idx())
294 }
295 }
296}
297
298impl fmt::Debug for Var {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 write!(f, "x{}", self.idx.0)
302 }
303}
304
305#[cfg(kani)]
306impl kani::Arbitrary for Var {
307 fn any() -> Self {
308 let idx = u32::any();
309 kani::assume(idx <= Var::MAX_IDX);
310 Var::new(idx)
311 }
312}
313
314#[macro_export]
323macro_rules! var {
324 ($v:expr) => {
325 $crate::types::Var::new($v)
326 };
327}
328
329#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
338#[allow(clippy::unsafe_derive_deserialize)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340#[repr(transparent)]
341#[allow(clippy::unsafe_derive_deserialize)]
343pub struct Lit {
344 lidx: u32,
345}
346
347impl Lit {
348 #[inline]
350 const fn represent(idx: u32, negated: bool) -> u32 {
351 (idx << 1) + if negated { 1 } else { 0 }
352 }
353
354 #[must_use]
360 pub const fn new(idx: u32, negated: bool) -> Lit {
361 assert!(idx < Var::MAX_IDX, "variable index too high");
362 Lit {
363 lidx: Lit::represent(idx, negated),
364 }
365 }
366
367 pub fn new_with_error(idx: u32, negated: bool) -> Result<Lit, TypeError> {
373 if idx > Var::MAX_IDX {
374 return Err(TypeError::IdxTooHigh(idx, Var::MAX_IDX));
375 }
376 Ok(Lit {
377 lidx: Lit::represent(idx, negated),
378 })
379 }
380
381 #[must_use]
389 pub const unsafe fn new_unchecked(idx: u32, negated: bool) -> Lit {
390 debug_assert!(idx <= Var::MAX_IDX);
391 Lit {
392 lidx: Lit::represent(idx, negated),
393 }
394 }
395
396 #[inline]
399 #[must_use]
400 pub const fn positive(idx: u32) -> Lit {
401 Lit::new(idx, false)
402 }
403
404 #[inline]
407 #[must_use]
408 pub const fn negative(idx: u32) -> Lit {
409 Lit::new(idx, true)
410 }
411
412 #[inline]
418 pub fn positive_with_error(idx: u32) -> Result<Lit, TypeError> {
419 Lit::new_with_error(idx, false)
420 }
421
422 #[inline]
428 pub fn negative_with_error(idx: u32) -> Result<Lit, TypeError> {
429 Lit::new_with_error(idx, true)
430 }
431
432 #[inline]
440 #[must_use]
441 pub const unsafe fn positive_unchecked(idx: u32) -> Lit {
442 Lit::new_unchecked(idx, false)
443 }
444
445 #[inline]
453 #[must_use]
454 pub const unsafe fn negative_unchecked(idx: u32) -> Lit {
455 Lit::new_unchecked(idx, true)
456 }
457
458 pub fn from_ipasir(val: c_int) -> Result<Lit, TypeError> {
466 if val == 0 {
467 return Err(TypeError::IpasirZero);
468 }
469 let negated = val < 0;
470 let idx = val.unsigned_abs();
471 Lit::new_with_error(idx - 1, negated)
472 }
473
474 #[inline]
476 #[must_use]
477 pub fn vidx(self) -> usize {
478 (self.lidx >> 1) as usize
479 }
480
481 #[inline]
483 #[must_use]
484 pub fn vidx32(self) -> u32 {
485 self.lidx >> 1
486 }
487
488 #[inline]
490 #[must_use]
491 pub fn lidx(self) -> usize {
492 self.lidx as usize
493 }
494
495 #[inline]
506 #[must_use]
507 pub fn var(self) -> Var {
508 unsafe { Var::new_unchecked(self.vidx32()) }
509 }
510
511 #[inline]
513 #[must_use]
514 pub fn is_pos(self) -> bool {
515 (self.lidx & 1u32) == 0
516 }
517
518 #[inline]
520 #[must_use]
521 pub fn is_neg(self) -> bool {
522 (self.lidx & 1u32) == 1
523 }
524
525 #[must_use]
537 pub fn to_ipasir(self) -> c_int {
538 let negated = self.is_neg();
539 let idx: c_int = (self.vidx32() + 1)
540 .try_into()
541 .expect("variable index too high to fit in c_int");
542 if negated {
543 -idx
544 } else {
545 idx
546 }
547 }
548
549 pub fn to_ipasir_with_error(self) -> Result<c_int, TypeError> {
560 let negated = self.is_neg();
561 let idx: c_int = match (self.vidx() + 1).try_into() {
562 Ok(idx) => idx,
563 Err(_) => return Err(TypeError::IdxTooHigh(self.vidx32() + 1, c_int::MAX as u32)),
564 };
565 Ok(if negated { -idx } else { idx })
566 }
567}
568
569impl ops::Not for Lit {
571 type Output = Lit;
572
573 #[inline]
574 fn not(self) -> Lit {
575 Lit {
576 lidx: self.lidx ^ 1u32,
577 }
578 }
579}
580
581impl ops::Neg for Lit {
583 type Output = Lit;
584
585 #[inline]
586 fn neg(self) -> Lit {
587 Lit {
588 lidx: self.lidx ^ 1u32,
589 }
590 }
591}
592
593impl ops::Add<u32> for Lit {
595 type Output = Lit;
596
597 fn add(self, rhs: u32) -> Self::Output {
598 Lit {
599 lidx: self.lidx + 2 * rhs,
600 }
601 }
602}
603
604impl ops::Sub<u32> for Lit {
606 type Output = Lit;
607
608 fn sub(self, rhs: u32) -> Self::Output {
609 Lit {
610 lidx: self.lidx - 2 * rhs,
611 }
612 }
613}
614
615impl fmt::Display for Lit {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 write!(f, "{}{}", if self.is_neg() { "~" } else { "" }, self.var())
619 }
620}
621
622impl fmt::Debug for Lit {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 if self.is_neg() {
626 write!(f, "~x{}", self.vidx())
627 } else {
628 write!(f, "x{}", self.vidx())
629 }
630 }
631}
632
633#[cfg(kani)]
634impl kani::Arbitrary for Lit {
635 fn any() -> Self {
636 let var = Var::any();
637 var.lit(bool::any())
638 }
639}
640
641#[macro_export]
651macro_rules! lit {
652 ($l:expr) => {
653 $crate::types::Lit::positive($l)
654 };
655}
656
657#[macro_export]
669macro_rules! ipasir_lit {
670 ($l:expr) => {
671 $crate::types::Lit::from_ipasir($l).unwrap()
672 };
673}
674
675#[derive(Clone, Copy, PartialEq, Eq, Default)]
677#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
678#[repr(u8)]
679pub enum TernaryVal {
680 True,
682 False,
684 #[default]
686 DontCare,
687}
688
689impl TernaryVal {
690 #[must_use]
692 pub fn to_bool_with_def(self, def: bool) -> bool {
693 match self {
694 TernaryVal::True => true,
695 TernaryVal::False => false,
696 TernaryVal::DontCare => def,
697 }
698 }
699}
700
701impl fmt::Display for TernaryVal {
703 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704 match self {
705 TernaryVal::True => write!(f, "1"),
706 TernaryVal::False => write!(f, "0"),
707 TernaryVal::DontCare => write!(f, "_"),
708 }
709 }
710}
711
712impl fmt::Debug for TernaryVal {
714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715 match self {
716 TernaryVal::True => write!(f, "1"),
717 TernaryVal::False => write!(f, "0"),
718 TernaryVal::DontCare => write!(f, "_"),
719 }
720 }
721}
722
723impl From<bool> for TernaryVal {
724 fn from(value: bool) -> Self {
725 if value {
726 return TernaryVal::True;
727 }
728 TernaryVal::False
729 }
730}
731
732impl ops::Not for TernaryVal {
733 type Output = TernaryVal;
734
735 fn not(self) -> Self::Output {
736 match self {
737 TernaryVal::True => TernaryVal::False,
738 TernaryVal::False => TernaryVal::True,
739 TernaryVal::DontCare => TernaryVal::DontCare,
740 }
741 }
742}
743
744impl ops::Neg for TernaryVal {
745 type Output = TernaryVal;
746
747 fn neg(self) -> Self::Output {
748 match self {
749 TernaryVal::True => TernaryVal::False,
750 TernaryVal::False => TernaryVal::True,
751 TernaryVal::DontCare => TernaryVal::DontCare,
752 }
753 }
754}
755
756#[cfg(kani)]
757impl kani::Arbitrary for TernaryVal {
758 fn any() -> Self {
759 let val: u8 = kani::any();
760 kani::assume(val < 3);
761 match val {
762 0 => TernaryVal::False,
763 1 => TernaryVal::True,
764 2 => TernaryVal::DontCare,
765 _ => panic!(),
766 }
767 }
768}
769
770#[derive(Error, Debug)]
772pub enum InvalidVLine {
773 #[error("The value line does not start with 'v ' but with {0}")]
775 InvalidTag(char),
776 #[error("The output of the SAT solver assigned different values to variable {0}")]
778 ConflictingAssignment(Var),
779 #[error("Empty value line")]
781 EmptyLine,
782}
783
784enum VLineFormat {
786 SatComp,
789 MaxSatEval,
792 PbComp,
795}
796
797#[derive(Clone, PartialEq, Eq, Default)]
799#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
800#[repr(transparent)]
801pub struct Assignment {
802 assignment: Vec<TernaryVal>,
803}
804
805impl Assignment {
806 #[must_use]
808 pub fn len(&self) -> usize {
809 self.assignment.len()
810 }
811
812 #[must_use]
814 pub fn is_empty(&self) -> bool {
815 self.assignment.is_empty()
816 }
817
818 #[must_use]
821 pub fn var_value(&self, var: Var) -> TernaryVal {
822 if var.idx() >= self.assignment.len() {
823 TernaryVal::DontCare
824 } else {
825 self.assignment[var.idx()]
826 }
827 }
828
829 #[must_use]
831 pub fn lit_value(&self, lit: Lit) -> TernaryVal {
832 if lit.is_neg() {
833 match self.var_value(lit.var()) {
834 TernaryVal::DontCare => TernaryVal::DontCare,
835 TernaryVal::True => TernaryVal::False,
836 TernaryVal::False => TernaryVal::True,
837 }
838 } else {
839 self.var_value(lit.var())
840 }
841 }
842
843 pub fn replace_dont_care(&mut self, def: bool) {
845 self.assignment.iter_mut().for_each(|tv| {
846 if tv == &TernaryVal::DontCare {
847 if def {
848 *tv = TernaryVal::True;
849 } else {
850 *tv = TernaryVal::False;
851 }
852 }
853 });
854 }
855
856 pub fn assign_var(&mut self, variable: Var, value: TernaryVal) {
858 if self.assignment.len() < variable.idx() + 1 {
859 self.assignment
860 .resize(variable.idx() + 1, TernaryVal::DontCare);
861 }
862 self.assignment[variable.idx()] = value;
863 }
864
865 pub fn assign_lit(&mut self, lit: Lit) {
867 let val = if lit.is_pos() {
868 TernaryVal::True
869 } else {
870 TernaryVal::False
871 };
872 self.assign_var(lit.var(), val);
873 }
874
875 #[must_use]
877 pub fn truncate(mut self, max_var: Var) -> Self {
878 self.assignment.truncate(max_var.idx() + 1);
879 self
880 }
881
882 #[must_use]
888 pub fn max_var(&self) -> Option<Var> {
889 if self.assignment.is_empty() {
890 None
891 } else {
892 Some(var![
893 u32::try_from(self.assignment.len())
894 .expect("assignment contains more than `u32::MAX` variables")
895 - 1
896 ])
897 }
898 }
899
900 pub fn from_solver_output_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
910 let mut reader = std::io::BufReader::new(
911 fio::open_compressed_uncompressed_read(path).context("failed to open reader")?,
912 );
913 let output = fio::parse_sat_solver_output(&mut reader)?;
914 match output {
915 SolverOutput::Sat(solution) => Ok(solution),
916 _ => anyhow::bail!("solver output does not indicate satisfiability"),
917 }
918 }
919
920 pub fn from_vline(line: &str) -> anyhow::Result<Self> {
926 let mut assignment = Assignment::default();
927 assignment.extend_from_vline(line)?;
928 Ok(assignment)
929 }
930
931 pub fn extend_from_vline(&mut self, lines: &str) -> anyhow::Result<()> {
942 anyhow::ensure!(!lines.is_empty(), InvalidVLine::EmptyLine);
943 let format = if lines.contains('x') {
945 VLineFormat::PbComp
946 } else if !lines[1..].trim().contains(' ')
947 && lines[1..].trim() != "0"
948 && lines[1..]
949 .trim()
950 .chars()
951 .try_for_each(|c| {
952 if c == '1' || c == '0' {
953 Ok(())
954 } else {
955 Err(())
956 }
957 })
958 .is_ok()
959 {
960 VLineFormat::MaxSatEval
961 } else {
962 VLineFormat::SatComp
963 };
964
965 for line in lines.lines() {
966 if let Some(tag) = line.chars().next() {
967 anyhow::ensure!(tag == 'v', InvalidVLine::InvalidTag(tag));
968 } else {
969 anyhow::bail!(InvalidVLine::EmptyLine);
970 }
971
972 match format {
973 VLineFormat::SatComp => self.extend_sat_comp_vline(line),
974 VLineFormat::MaxSatEval => self.extend_maxsat_eval_vline(line),
975 VLineFormat::PbComp => self.extend_pb_comp_vline(line),
976 }?;
977 }
978 Ok(())
979 }
980
981 fn extend_sat_comp_vline(&mut self, line: &str) -> anyhow::Result<()> {
983 let line = &line[1..];
984 for number in line.split_whitespace() {
985 let number = number.parse::<i32>()?;
986
987 if number == 0 {
989 continue;
990 }
991
992 let literal = Lit::from_ipasir(number)?;
993 let val = self.lit_value(literal);
994 if val == TernaryVal::True && literal.is_neg()
995 || val == TernaryVal::False && literal.is_pos()
996 {
997 anyhow::bail!(InvalidVLine::ConflictingAssignment(literal.var()));
999 }
1000 self.assign_lit(literal);
1001 }
1002 Ok(())
1003 }
1004
1005 fn extend_maxsat_eval_vline(&mut self, line: &str) -> anyhow::Result<()> {
1007 let line = line[1..].trim();
1008 self.assignment.reserve(line.len());
1009 for char in line.chars() {
1010 match char {
1011 '0' => self.assignment.push(TernaryVal::False),
1012 '1' => self.assignment.push(TernaryVal::True),
1013 _ => anyhow::bail!("unexpected character in MaxSAT Evaluation v-line"),
1014 }
1015 }
1016 Ok(())
1017 }
1018
1019 fn extend_pb_comp_vline(&mut self, line: &str) -> anyhow::Result<()> {
1021 let line = &line[1..];
1022 for number in line.split_whitespace() {
1023 let (_, lit) = fio::opb::literal(number, fio::opb::Options::default())
1024 .map_err(nom::Err::<nom::error::Error<&str>>::to_owned)
1025 .with_context(|| format!("failed to parse pb literal '{number}'"))?;
1026 let val = self.lit_value(lit);
1027 if val == TernaryVal::True && lit.is_neg() || val == TernaryVal::False && lit.is_pos() {
1028 anyhow::bail!(InvalidVLine::ConflictingAssignment(lit.var()));
1030 }
1031 self.assign_lit(lit);
1032 }
1033 Ok(())
1034 }
1035
1036 #[allow(clippy::cast_possible_truncation)]
1038 pub fn iter(&self) -> impl Iterator<Item = Lit> + '_ {
1039 self.assignment
1040 .iter()
1041 .enumerate()
1042 .filter_map(|(idx, tv)| match tv {
1043 TernaryVal::True => Some(Lit::new(idx as u32, false)),
1044 TernaryVal::False => Some(Lit::new(idx as u32, true)),
1045 TernaryVal::DontCare => None,
1046 })
1047 }
1048}
1049
1050#[cfg(kani)]
1051impl Assignment {
1052 pub fn arbitrary(n_vars: u32) -> Self {
1054 Self::from_iter((0..n_vars).map(|_| <bool as kani::Arbitrary>::any()))
1055 }
1056}
1057
1058impl fmt::Debug for Assignment {
1059 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1060 self.assignment.iter().try_for_each(|tv| write!(f, "{tv}"))
1061 }
1062}
1063
1064impl fmt::Display for Assignment {
1065 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1066 self.assignment.iter().try_for_each(|tv| write!(f, "{tv}"))
1067 }
1068}
1069
1070impl IntoIterator for Assignment {
1072 type Item = Lit;
1073
1074 type IntoIter = std::iter::FilterMap<
1075 std::iter::Enumerate<std::vec::IntoIter<TernaryVal>>,
1076 fn((usize, TernaryVal)) -> Option<Lit>,
1077 >;
1078
1079 fn into_iter(self) -> Self::IntoIter {
1080 self.assignment
1081 .into_iter()
1082 .enumerate()
1083 .filter_map(|(idx, tv)| match tv {
1084 TernaryVal::True => Some(Var::new(idx.try_into().unwrap()).pos_lit()),
1085 TernaryVal::False => Some(Var::new(idx.try_into().unwrap()).neg_lit()),
1086 TernaryVal::DontCare => None,
1087 })
1088 }
1089}
1090
1091impl<'a> IntoIterator for &'a Assignment {
1093 type Item = Lit;
1094
1095 type IntoIter = std::iter::FilterMap<
1096 std::iter::Enumerate<std::slice::Iter<'a, TernaryVal>>,
1097 fn((usize, &TernaryVal)) -> Option<Lit>,
1098 >;
1099
1100 fn into_iter(self) -> Self::IntoIter {
1101 self.assignment
1102 .iter()
1103 .enumerate()
1104 .filter_map(|(idx, tv)| match tv {
1105 TernaryVal::True => Some(Var::new(idx.try_into().unwrap()).pos_lit()),
1106 TernaryVal::False => Some(Var::new(idx.try_into().unwrap()).neg_lit()),
1107 TernaryVal::DontCare => None,
1108 })
1109 }
1110}
1111
1112impl FromIterator<Lit> for Assignment {
1113 fn from_iter<T: IntoIterator<Item = Lit>>(iter: T) -> Self {
1114 let mut assignment = Assignment::default();
1115 iter.into_iter().for_each(|l| assignment.assign_lit(l));
1116 assignment
1117 }
1118}
1119
1120impl FromIterator<TernaryVal> for Assignment {
1121 fn from_iter<T: IntoIterator<Item = TernaryVal>>(iter: T) -> Self {
1122 Self::from(iter.into_iter().collect::<Vec<_>>())
1123 }
1124}
1125
1126impl FromIterator<bool> for Assignment {
1127 fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
1128 iter.into_iter().map(TernaryVal::from).collect()
1129 }
1130}
1131
1132impl From<Vec<TernaryVal>> for Assignment {
1133 fn from(assignment: Vec<TernaryVal>) -> Self {
1134 Self { assignment }
1135 }
1136}
1137
1138impl Index<Var> for Assignment {
1139 type Output = TernaryVal;
1140
1141 fn index(&self, index: Var) -> &Self::Output {
1142 &self.assignment[index.idx()]
1143 }
1144}
1145
1146impl IndexMut<Var> for Assignment {
1147 fn index_mut(&mut self, index: Var) -> &mut Self::Output {
1148 &mut self.assignment[index.idx()]
1149 }
1150}
1151
1152#[derive(Error, Debug)]
1154pub enum TypeError {
1155 #[error("index {0} is too high (maximum {1})")]
1158 IdxTooHigh(u32, u32),
1159 #[error("zero is an invalid IPASIR/DIMACS literal")]
1161 IpasirZero,
1162}
1163
1164pub trait LitIter: IntoIterator<Item = Lit> {}
1166impl<I: IntoIterator<Item = Lit>> LitIter for I {}
1167pub trait ClsIter: IntoIterator<Item = Clause> {}
1169impl<I: IntoIterator<Item = Clause>> ClsIter for I {}
1170
1171pub trait WLitIter: IntoIterator<Item = (Lit, usize)> {}
1173impl<I: IntoIterator<Item = (Lit, usize)>> WLitIter for I {}
1174pub trait WClsIter: IntoIterator<Item = (Clause, usize)> {}
1176impl<I: IntoIterator<Item = (Clause, usize)>> WClsIter for I {}
1177pub trait IWLitIter: IntoIterator<Item = (Lit, isize)> {}
1179impl<I: IntoIterator<Item = (Lit, isize)>> IWLitIter for I {}
1180
1181#[cfg(feature = "proof-logging")]
1182mod pigeons {
1183 use std::fmt;
1184
1185 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1188 #[repr(transparent)]
1189 pub struct PidgeonVarFormatter(super::Var);
1190
1191 impl From<super::Var> for PidgeonVarFormatter {
1192 fn from(value: super::Var) -> Self {
1193 PidgeonVarFormatter(value)
1194 }
1195 }
1196
1197 impl fmt::Display for PidgeonVarFormatter {
1198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1199 write!(f, "x{}", self.0.idx() + 1)
1200 }
1201 }
1202
1203 impl pigeons::VarLike for super::Var {
1204 type Formatter = PidgeonVarFormatter;
1205 }
1206
1207 impl From<super::Lit> for pigeons::Axiom<super::Var> {
1208 fn from(value: super::Lit) -> Self {
1209 use pigeons::VarLike;
1210
1211 if value.is_pos() {
1212 value.var().pos_axiom()
1213 } else {
1214 value.var().neg_axiom()
1215 }
1216 }
1217 }
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use std::{mem::size_of, num::ParseIntError};
1223
1224 use super::{Assignment, InvalidVLine, Lit, TernaryVal, Var};
1225
1226 #[test]
1227 fn var_index() {
1228 let idx = 5;
1229 let var = Var::new(idx);
1230 assert_eq!(var.idx(), idx as usize);
1231 }
1232
1233 #[test]
1234 fn var_index32() {
1235 let idx = 5;
1236 let var = Var::new(idx);
1237 assert_eq!(var.idx32(), idx);
1238 }
1239
1240 #[test]
1241 fn var_pos_lit() {
1242 let idx = 5;
1243 let var = Var::new(idx);
1244 let lit = Lit::positive(idx);
1245 assert_eq!(var.pos_lit(), lit);
1246 }
1247
1248 #[test]
1249 fn var_neg_lit() {
1250 let idx = 5;
1251 let var = Var::new(idx);
1252 let lit = Lit::negative(idx);
1253 assert_eq!(var.neg_lit(), lit);
1254 }
1255
1256 #[test]
1257 fn var_from_lit() {
1258 let idx = 0;
1259 let lit = Lit::positive(idx);
1260 let var = Var::new(idx);
1261 assert_eq!(lit.var(), var);
1262 }
1263
1264 #[cfg(feature = "proof-logging")]
1265 #[test]
1266 fn proof_log_var() {
1267 use pigeons::VarLike;
1268 let var = Var::new(3);
1269 assert_eq!(&format!("{}", <Var as VarLike>::Formatter::from(var)), "x4");
1270 }
1271
1272 #[test]
1273 #[should_panic(expected = "variable index overflow")]
1274 fn var_add_1_overflow() {
1275 let var = Var::new(Var::MAX_IDX);
1276 let _ = var + 1;
1277 }
1278
1279 #[test]
1280 #[should_panic(expected = "variable index overflow")]
1281 fn var_add_42_overflow() {
1282 let var = Var::new(Var::MAX_IDX - 41);
1283 let _ = var + 42;
1284 }
1285
1286 #[test]
1287 #[should_panic(expected = "variable index overflow")]
1288 fn var_addassign_1_overflow() {
1289 let mut var = Var::new(Var::MAX_IDX);
1290 var += 1;
1291 }
1292
1293 #[test]
1294 #[should_panic(expected = "variable index overflow")]
1295 fn var_addassign_overflow() {
1296 let mut var = Var::new(Var::MAX_IDX - 41);
1297 var += 42;
1298 }
1299
1300 #[test]
1301 fn lit_representation() {
1302 let lidx = Lit::represent(5, true);
1303 assert_eq!(lidx, 0b1011);
1304 }
1305
1306 #[test]
1307 fn lit_is_pos() {
1308 let lit = Lit::positive(0);
1309 assert!(lit.is_pos());
1310 assert!(!lit.is_neg());
1311 }
1312
1313 #[test]
1314 fn lit_is_neg() {
1315 let lit = Lit::negative(0);
1316 assert!(!lit.is_pos());
1317 assert!(lit.is_neg());
1318 }
1319
1320 #[test]
1321 fn lit_negation() {
1322 let lit1 = Lit::positive(0);
1323 let lit2 = !lit1;
1324 assert!(!lit2.is_pos());
1325 assert!(lit2.is_neg());
1326 assert_eq!(lit1.var(), lit2.var());
1327 }
1328
1329 #[test]
1330 fn ipasir_lit_not_zero() {
1331 let lit = Lit::positive(0);
1332 assert_ne!(lit.to_ipasir(), 0);
1333 let lit = !lit;
1334 assert_ne!(lit.to_ipasir(), 0);
1335 }
1336
1337 #[test]
1338 fn ipasir_lit_idx_plus_one() {
1339 let idx = 5;
1340 let lit = Lit::positive(idx);
1341 assert_eq!(lit.to_ipasir(), i32::try_from(idx).unwrap() + 1);
1342 let lit = !lit;
1343 assert_eq!(lit.to_ipasir(), -(i32::try_from(idx).unwrap() + 1));
1344 }
1345
1346 #[test]
1347 fn ternary_var_true() {
1348 let tv = TernaryVal::True;
1349 assert!(tv.to_bool_with_def(true));
1350 assert!(tv.to_bool_with_def(false));
1351 }
1352
1353 #[test]
1354 fn ternary_var_false() {
1355 let tv = TernaryVal::False;
1356 assert!(!tv.to_bool_with_def(true));
1357 assert!(!tv.to_bool_with_def(false));
1358 }
1359
1360 #[test]
1361 fn ternary_var_dnc() {
1362 let tv = TernaryVal::DontCare;
1363 assert!(tv.to_bool_with_def(true));
1364 assert!(!tv.to_bool_with_def(false));
1365 }
1366
1367 #[test]
1368 fn sol_var_val() {
1369 let sol = Assignment::from(vec![
1370 TernaryVal::True,
1371 TernaryVal::False,
1372 TernaryVal::DontCare,
1373 ]);
1374 let val = sol.var_value(Var::new(0));
1375 assert_eq!(val, TernaryVal::True);
1376 let val = sol.var_value(Var::new(1));
1377 assert_eq!(val, TernaryVal::False);
1378 let val = sol.var_value(Var::new(2));
1379 assert_eq!(val, TernaryVal::DontCare);
1380 }
1381
1382 #[test]
1383 fn sol_lit_val() {
1384 let sol = Assignment::from(vec![
1385 TernaryVal::True,
1386 TernaryVal::False,
1387 TernaryVal::DontCare,
1388 ]);
1389 let val = sol.lit_value(Lit::negative(0));
1390 assert_eq!(val, TernaryVal::False);
1391 let val = sol.lit_value(Lit::positive(0));
1392 assert_eq!(val, TernaryVal::True);
1393 let val = sol.lit_value(Lit::negative(1));
1394 assert_eq!(val, TernaryVal::True);
1395 let val = sol.lit_value(Lit::positive(1));
1396 assert_eq!(val, TernaryVal::False);
1397 let val = sol.lit_value(Lit::negative(2));
1398 assert_eq!(val, TernaryVal::DontCare);
1399 let val = sol.lit_value(Lit::positive(2));
1400 assert_eq!(val, TernaryVal::DontCare);
1401 }
1402
1403 #[test]
1404 fn sol_repl_dont_care() {
1405 let mut sol = Assignment::from(vec![
1406 TernaryVal::True,
1407 TernaryVal::False,
1408 TernaryVal::DontCare,
1409 ]);
1410 sol.replace_dont_care(true);
1411 let val = sol.var_value(Var::new(0));
1412 assert_eq!(val, TernaryVal::True);
1413 let val = sol.var_value(Var::new(1));
1414 assert_eq!(val, TernaryVal::False);
1415 let val = sol.var_value(Var::new(2));
1416 assert_eq!(val, TernaryVal::True);
1417 }
1418
1419 #[test]
1420 fn sol_from_lits() {
1421 let true_sol = Assignment::from(vec![
1422 TernaryVal::True,
1423 TernaryVal::DontCare,
1424 TernaryVal::False,
1425 ]);
1426 let sol = Assignment::from_iter(vec![lit![0], !lit![2]]);
1427 assert_eq!(true_sol, sol);
1428 }
1429
1430 #[test]
1431 fn var_mem_size() {
1432 assert_eq!(size_of::<Var>(), size_of::<u32>());
1433 }
1434
1435 #[test]
1436 fn lit_mem_size() {
1437 assert_eq!(size_of::<Lit>(), size_of::<u32>());
1438 }
1439
1440 #[test]
1441 fn ternary_val_size() {
1442 assert_eq!(size_of::<TernaryVal>(), 1);
1443 }
1444
1445 #[test]
1446 fn parse_sat_comp_vline() {
1447 let vline = "v 1 -2 4 -5 6 0";
1448 let ground_truth = Assignment::from(vec![
1449 TernaryVal::True,
1450 TernaryVal::False,
1451 TernaryVal::DontCare,
1452 TernaryVal::True,
1453 TernaryVal::False,
1454 TernaryVal::True,
1455 ]);
1456 assert_eq!(Assignment::from_vline(vline).unwrap(), ground_truth);
1457 assert_eq!(
1458 {
1459 let mut assign = Assignment::default();
1460 assign.extend_from_vline(vline).unwrap();
1461 assign
1462 },
1463 ground_truth
1464 );
1465 }
1466
1467 #[test]
1468 fn parse_maxsat_eval_vline() {
1469 let vline = "v 100101";
1470 let ground_truth = Assignment::from(vec![
1471 TernaryVal::True,
1472 TernaryVal::False,
1473 TernaryVal::False,
1474 TernaryVal::True,
1475 TernaryVal::False,
1476 TernaryVal::True,
1477 ]);
1478 assert_eq!(Assignment::from_vline(vline).unwrap(), ground_truth);
1479 assert_eq!(
1480 {
1481 let mut assign = Assignment::default();
1482 assign.extend_from_vline(vline).unwrap();
1483 assign
1484 },
1485 ground_truth
1486 );
1487 }
1488
1489 #[test]
1490 fn parse_pb_comp_vline() {
1491 let vline = "v x1 -x2 x4 -x5 x6";
1492 let ground_truth = Assignment::from(vec![
1493 TernaryVal::True,
1494 TernaryVal::False,
1495 TernaryVal::DontCare,
1496 TernaryVal::True,
1497 TernaryVal::False,
1498 TernaryVal::True,
1499 ]);
1500 assert_eq!(Assignment::from_vline(vline).unwrap(), ground_truth);
1501 assert_eq!(
1502 {
1503 let mut assign = Assignment::default();
1504 assign.extend_from_vline(vline).unwrap();
1505 assign
1506 },
1507 ground_truth
1508 );
1509 }
1510
1511 #[test]
1512 fn vline_invalid_lit_from() {
1513 let vline = "v 1 -2 4 foo -5 bar 6 0";
1514 let res = Assignment::from_vline(vline);
1515 res.unwrap_err().downcast::<ParseIntError>().unwrap();
1516 }
1517
1518 #[test]
1519 fn vline_invalid_lit_extend() {
1520 let vline = "v 1 -2 4 foo -5 bar 6 0";
1521 let mut assign = Assignment::default();
1522 let res = assign.extend_from_vline(vline);
1523 res.unwrap_err().downcast::<ParseIntError>().unwrap();
1524 }
1525
1526 #[test]
1527 fn vline_invalid_tag_from() {
1528 let vline = "b 1 -2 4 -5 6 0";
1529 let res = Assignment::from_vline(vline);
1530 assert!(matches!(
1531 res.unwrap_err().downcast::<InvalidVLine>(),
1532 Ok(InvalidVLine::InvalidTag('b'))
1533 ));
1534 }
1535
1536 #[test]
1537 fn vline_invalid_tag_extend() {
1538 let vline = "b 1 -2 4 -5 6 0";
1539 let mut assign = Assignment::default();
1540 let res = assign.extend_from_vline(vline);
1541 assert!(matches!(
1542 res.unwrap_err().downcast::<InvalidVLine>(),
1543 Ok(InvalidVLine::InvalidTag('b'))
1544 ));
1545 }
1546
1547 #[test]
1548 fn vline_invalid_empty() {
1549 let vline = "";
1550 let res = Assignment::from_vline(vline);
1551 assert!(matches!(
1552 res.unwrap_err().downcast::<InvalidVLine>(),
1553 Ok(InvalidVLine::EmptyLine)
1554 ));
1555 }
1556
1557 #[test]
1558 fn multi_vline() {
1559 let vline = "v 1 2 3\nv 4 5 6 0";
1560 let ground_truth = Assignment::from(vec![
1561 TernaryVal::True,
1562 TernaryVal::True,
1563 TernaryVal::True,
1564 TernaryVal::True,
1565 TernaryVal::True,
1566 TernaryVal::True,
1567 ]);
1568 let res = Assignment::from_vline(vline).unwrap();
1569 assert_eq!(res, ground_truth);
1570 }
1571
1572 #[cfg(feature = "serde")]
1573 #[test]
1574 fn serde_var() {
1575 let var = Var::new(5);
1576 let json = serde_json::to_string(&var).unwrap();
1577 assert_eq!(json, r#"{"idx":5}"#);
1578 let roundtrip: Var = serde_json::from_str(&json).unwrap();
1579 assert_eq!(roundtrip, var);
1580 }
1581
1582 #[cfg(feature = "serde")]
1583 #[test]
1584 fn serde_var_invalid() {
1585 let failure: Result<Var, _> = serde_json::from_str("");
1586 assert!(failure.is_err());
1587 }
1588
1589 #[cfg(feature = "serde")]
1590 #[test]
1591 fn serde_lit() {
1592 let lit = Lit::new(5, true);
1593 let json = serde_json::to_string(&lit).unwrap();
1594 assert_eq!(json, r#"{"lidx":11}"#);
1595 let roundtrip: Lit = serde_json::from_str(&json).unwrap();
1596 assert_eq!(roundtrip, lit);
1597 }
1598
1599 #[test]
1600 fn assign_into_iter() {
1601 let assign = Assignment::from(vec![
1602 TernaryVal::True,
1603 TernaryVal::False,
1604 TernaryVal::True,
1605 TernaryVal::DontCare,
1606 TernaryVal::True,
1607 TernaryVal::False,
1608 ]);
1609 let lits: Vec<_> = assign.into_iter().collect();
1610 assert_eq!(lits, vec![lit![0], !lit![1], lit![2], lit![4], !lit![5]]);
1611 }
1612
1613 #[test]
1614 fn assign_ref_into_iter() {
1615 let assign = Assignment::from(vec![
1616 TernaryVal::True,
1617 TernaryVal::False,
1618 TernaryVal::True,
1619 TernaryVal::DontCare,
1620 TernaryVal::True,
1621 TernaryVal::False,
1622 ]);
1623 let lits: Vec<_> = (&assign).into_iter().collect();
1624 assert_eq!(lits, vec![lit![0], !lit![1], lit![2], lit![4], !lit![5]]);
1625 assert_eq!(assign.len(), 6);
1627 }
1628}
1629
1630#[cfg(kani)]
1631mod proofs {
1632 #[kani::proof]
1633 fn pos_lit() {
1634 let var: super::Var = kani::any();
1635 let lit = var.pos_lit();
1636 assert_eq!(var, lit.var());
1637 }
1638
1639 #[kani::proof]
1640 fn neg_lit() {
1641 let var: super::Var = kani::any();
1642 let lit = var.neg_lit();
1643 assert_eq!(var, lit.var());
1644 }
1645}