1use crate::{ChangeReason, Explanation, VariableId};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub struct NogoodLiteral {
6 pub variable: VariableId,
8 pub value: i32,
10}
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Nogood {
15 literals: Vec<NogoodLiteral>,
16}
17
18impl Nogood {
19 #[must_use]
21 pub fn new(literals: Vec<NogoodLiteral>) -> Self {
22 Self { literals }
23 }
24
25 #[must_use]
27 pub fn literals(&self) -> &[NogoodLiteral] {
28 &self.literals
29 }
30
31 #[must_use]
33 pub fn is_satisfied_by(&self, assignment: &[(VariableId, i32)]) -> bool {
34 self.literals.iter().all(|literal| {
35 assignment
36 .iter()
37 .any(|&(var, value)| var == literal.variable && value == literal.value)
38 })
39 }
40
41 #[must_use]
43 pub fn would_be_satisfied_by(
44 &self,
45 assignment: &[(VariableId, i32)],
46 var: VariableId,
47 value: i32,
48 ) -> bool {
49 self.literals.iter().all(|literal| {
50 if literal.variable == var {
51 literal.value == value
52 } else {
53 assignment
54 .iter()
55 .any(|&(v, val)| v == literal.variable && val == literal.value)
56 }
57 })
58 }
59}
60
61impl Explanation {
62 pub fn branch_literals(&self) -> impl Iterator<Item = NogoodLiteral> + '_ {
64 self.entries().iter().filter_map(|entry| match entry {
65 ChangeReason::Branch { variable, value } => Some(NogoodLiteral {
66 variable: *variable,
67 value: *value,
68 }),
69 ChangeReason::Propagator { .. } | ChangeReason::PropagatorConflict { .. } => None,
70 })
71 }
72
73 #[must_use]
75 pub fn propagator_conflict_literals(&self) -> Option<Vec<NogoodLiteral>> {
76 self.entries().iter().rev().find_map(|entry| match entry {
77 ChangeReason::PropagatorConflict { literals } => Some(
78 literals
79 .iter()
80 .map(|&(variable, value)| NogoodLiteral { variable, value })
81 .collect(),
82 ),
83 _ => None,
84 })
85 }
86
87 #[must_use]
89 pub fn unique_branch_literals(&self) -> Vec<NogoodLiteral> {
90 let mut literals = Vec::new();
91 for literal in self.branch_literals() {
92 if let Some(existing) = literals
93 .iter_mut()
94 .find(|l: &&mut NogoodLiteral| l.variable == literal.variable)
95 {
96 *existing = literal;
97 } else {
98 literals.push(literal);
99 }
100 }
101 literals
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::{ChangeReason, Explanation, VariableId};
109 use slotmap::SlotMap;
110
111 fn make_var() -> VariableId {
112 let mut sm: SlotMap<crate::VariableKey, ()> = SlotMap::with_key();
113 VariableId::from_key(sm.insert(()))
114 }
115
116 #[test]
117 fn nogood_satisfied_by_matching_assignment() {
118 let v0 = make_var();
119 let v1 = make_var();
120 let nogood = Nogood::new(vec![
121 NogoodLiteral {
122 variable: v0,
123 value: 1,
124 },
125 NogoodLiteral {
126 variable: v1,
127 value: 2,
128 },
129 ]);
130 assert!(nogood.is_satisfied_by(&[(v0, 1), (v1, 2)]));
131 assert!(!nogood.is_satisfied_by(&[(v0, 1), (v1, 3)]));
132 }
133
134 #[test]
135 fn nogood_would_be_satisfied_by_extension() {
136 let v0 = make_var();
137 let nogood = Nogood::new(vec![NogoodLiteral {
138 variable: v0,
139 value: 5,
140 }]);
141 assert!(nogood.would_be_satisfied_by(&[], v0, 5));
142 assert!(!nogood.would_be_satisfied_by(&[], v0, 4));
143 }
144
145 #[test]
146 fn branch_literals_skip_propagator_entries() {
147 let v0 = make_var();
148 let mut explanation = Explanation::new();
149 explanation.record(ChangeReason::Branch {
150 variable: v0,
151 value: 3,
152 });
153 explanation.record(ChangeReason::Propagator {
154 propagator: crate::PropagatorId::from_key({
155 let mut sm: SlotMap<crate::PropagatorKey, ()> = SlotMap::with_key();
156 sm.insert(())
157 }),
158 variable: v0,
159 removed_value: Some(2),
160 bound: None,
161 });
162 let literals: Vec<_> = explanation.branch_literals().collect();
163 assert_eq!(literals.len(), 1);
164 assert_eq!(literals[0].value, 3);
165 }
166
167 #[test]
168 fn unique_branch_literals_keep_latest_per_variable() {
169 let v0 = make_var();
170 let mut explanation = Explanation::new();
171 explanation.record(ChangeReason::Branch {
172 variable: v0,
173 value: 1,
174 });
175 explanation.record(ChangeReason::Branch {
176 variable: v0,
177 value: 2,
178 });
179 let literals = explanation.unique_branch_literals();
180 assert_eq!(literals.len(), 1);
181 assert_eq!(literals[0].value, 2);
182 }
183
184 #[test]
185 fn propagator_conflict_literals_from_latest_conflict() {
186 let v0 = make_var();
187 let v1 = make_var();
188 let mut explanation = Explanation::new();
189 explanation.record(ChangeReason::PropagatorConflict {
190 literals: vec![(v0, 1), (v1, 2)],
191 });
192 let literals = explanation
193 .propagator_conflict_literals()
194 .expect("conflict literals");
195 assert_eq!(literals.len(), 2);
196 }
197}