1use std::{cell::RefCell, ops::Range, rc::Rc};
2
3use ahash::AHashSet;
4use veripb_formula::prelude::*;
5use veripb_propagator::propagation_engine::PropagationEngine;
6
7use crate::{
8 context::{Context, CORE, DERIVED},
9 error::CheckingError,
10 occurrence_list::OccurrenceList,
11};
12
13#[inline]
15pub fn move_to_derived_propagator(
16 context: &mut Context,
17 constraint: &Rc<DBConstraint>,
18) -> Result<(), CheckingError> {
19 context.propagation_engine.detach(
20 CORE,
21 constraint,
22 context.propagation_engine.only_core_trail,
23 )?;
24 context.propagation_engine.attach(DERIVED, constraint)?;
25 Ok(())
26}
27
28#[derive(Debug, Default)]
30pub struct Database {
31 pub entries: Vec<Option<Rc<DBConstraint>>>,
33 pub unique_constraints: AHashSet<Rc<DBConstraint>>,
35 occurrences: OccurrenceList,
37 next_unique_index_id: usize,
39 next_occurrences_index_id: usize,
41 next_propagation_index_id: usize,
43}
44
45impl Database {
46 pub fn new() -> Self {
48 Database {
49 entries: vec![None],
50 ..Default::default()
51 }
52 }
53
54 pub fn from_formula(constraints: Vec<PBConstraintEnum>) -> Self {
56 let mut entries = Vec::with_capacity(constraints.len() + 1);
57 entries.push(None);
58 let mut unique_constraints = AHashSet::with_capacity(constraints.len());
59 let mut occurrences = OccurrenceList::default();
60 for constraint in constraints {
61 let db_constraint = Rc::new(DBConstraint {
62 header: RefCell::new(DBHeader::default()),
63 constraint,
64 });
65 if unique_constraints.insert(Rc::clone(&db_constraint)) {
66 db_constraint.add_id(entries.len(), true);
67 occurrences.add(&db_constraint);
68 entries.push(Some(db_constraint));
69 } else {
70 let entry = unique_constraints.get(&db_constraint).unwrap();
72 entry.add_id(entries.len(), true);
73 entries.push(Some(Rc::clone(entry)));
74 }
75 }
76 let first_non_indexed_constraint_id = entries.len();
77
78 Database {
79 entries,
80 unique_constraints,
81 occurrences,
82 next_unique_index_id: first_non_indexed_constraint_id,
83 next_occurrences_index_id: first_non_indexed_constraint_id,
84 next_propagation_index_id: first_non_indexed_constraint_id,
85 }
86 }
87
88 #[inline]
92 pub fn add_constraint(&mut self, constraint: Rc<DBConstraint>, add_to_core: bool) {
93 constraint.add_id(self.len(), add_to_core);
94 self.entries.push(Some(constraint));
95 }
96
97 #[inline]
101 pub fn delete_constraint(
102 &mut self,
103 context: &mut Context,
104 constraint_id: usize,
105 ) -> Result<(), CheckingError> {
106 match self.entries.get_mut(constraint_id) {
107 None => Err(CheckingError::deletion(
108 constraint_id,
109 &format!(
110 "there are only {} constraints in the database",
111 self.entries.len()
112 ),
113 )),
114 Some(None) => Err(CheckingError::deletion(
115 constraint_id,
116 "the constraint has already been deleted",
117 )),
118 Some(entry) => {
119 let db_constraint = entry.as_mut().unwrap();
120 let was_core = db_constraint.is_core_constraint();
121 db_constraint.remove_id(constraint_id);
122 if db_constraint.all_constraint_ids_empty() {
123 if constraint_id < self.next_unique_index_id {
124 self.unique_constraints.remove(db_constraint.as_ref());
125 }
126 if db_constraint.header.borrow().is_in_occurrences {
127 self.occurrences.remove(db_constraint);
128 }
129 context.propagation_engine.detach(
130 if was_core { CORE } else { DERIVED },
131 db_constraint,
132 true,
133 )?;
134 } else if was_core && !db_constraint.is_core_constraint() {
135 move_to_derived_propagator(context, db_constraint)?;
136 }
137 *entry = None;
138 Ok(())
139 }
140 }
141 }
142
143 pub fn update_unique_index(
153 &mut self,
154 prop_engine: &mut PropagationEngine,
155 ) -> Result<(), CheckingError> {
156 for constraint_id in self.next_unique_index_id..self.len() {
157 if let Some(Some(constraint)) = self.entries.get(constraint_id) {
159 let add_to_core = constraint.is_core_constraint();
160 if !self.unique_constraints.insert(constraint.clone()) {
161 let entry = self.unique_constraints.get(constraint).unwrap();
163 entry.add_id(constraint_id, add_to_core);
164 if let Some(out_id) = constraint.get_out_id(constraint_id) {
165 entry.set_out_id(constraint_id, out_id);
166 }
167 if constraint_id < self.next_occurrences_index_id {
169 self.occurrences.remove(constraint);
170 }
171 if constraint_id < self.next_propagation_index_id {
172 prop_engine.detach(
173 if add_to_core { CORE } else { DERIVED },
174 constraint,
175 true,
176 )?;
177 }
178 self.entries[constraint_id] = Some(Rc::clone(entry));
180 }
181 }
182 }
183 self.next_unique_index_id = self.len();
184 Ok(())
185 }
186
187 pub fn update_occurrences_index(&mut self) {
189 for constraint_id in self.next_occurrences_index_id..self.len() {
190 if let Some(Some(constraint)) = self.entries.get(constraint_id) {
192 if !constraint.header.borrow().is_in_occurrences {
193 self.occurrences.add(constraint);
194 }
195 }
196 }
197 self.next_occurrences_index_id = self.len();
198 }
199
200 pub fn update_propagation_index(
206 &mut self,
207 prop_engine: &mut PropagationEngine,
208 ) -> Result<(), CheckingError> {
209 for constraint_id in self.next_propagation_index_id..self.len() {
210 if let Some(Some(constraint)) = self.entries.get(constraint_id) {
212 if constraint.header.borrow().propagator_id.is_none() {
213 let add_to_core = constraint.is_core_constraint();
214 prop_engine.attach(if add_to_core { CORE } else { DERIVED }, constraint)?;
215 }
216 }
217 }
218 self.next_propagation_index_id = self.len();
219 Ok(())
220 }
221
222 #[inline]
224 pub fn lookup(&self, constraint: &Rc<DBConstraint>) -> Option<&Rc<DBConstraint>> {
225 debug_assert!(self.len() == self.next_unique_index_id);
226 self.unique_constraints.get(constraint)
227 }
228
229 #[inline]
231 pub fn contains_contradiction(&self) -> Option<usize> {
232 for constraint in self.unique_constraints.iter() {
233 if constraint.is_contradicting() {
234 return Some(constraint.get_some_id());
235 }
236 }
237 for constraint_id in self.next_unique_index_id..self.len() {
238 if let Some(Some(constraint)) = self.entries.get(constraint_id) {
239 if constraint.is_contradicting() {
240 return Some(constraint_id);
241 }
242 }
243 }
244 None
245 }
246
247 #[inline]
248 pub fn normalize_id(&self, index: isize) -> isize {
249 if index < 0 {
250 self.len() as isize + index
251 } else {
252 index
253 }
254 }
255
256 #[inline]
257 pub fn get_entry(&self, index: isize) -> Result<&Rc<DBConstraint>, CheckingError> {
258 let entry = if index < 0 {
259 match self
260 .entries
261 .get(((self.entries.len() as isize) + index) as usize)
262 {
263 Some(entry) => entry,
264 None => {
265 return Err(CheckingError::out_of_bounds(
266 index,
267 -(self.entries.len() as isize) + 1,
268 (self.entries.len() as isize) - 1,
269 ))
270 }
271 }
272 } else {
273 match self.entries.get(index as usize) {
274 Some(entry) => entry,
275 None => {
276 return Err(CheckingError::out_of_bounds(
277 index,
278 -(self.entries.len() as isize),
279 (self.entries.len() as isize) - 1,
280 ))
281 }
282 }
283 };
284
285 match entry {
286 Some(constraint) => Ok(constraint),
287 None => Err(CheckingError::access_deleted(index)),
288 }
289 }
290
291 #[inline]
292 pub fn get_entry_usize(&self, index: usize) -> Result<&Rc<DBConstraint>, CheckingError> {
293 let entry = match self.entries.get(index) {
294 Some(entry) => entry,
295 None => {
296 return Err(CheckingError::out_of_bounds(
297 index as isize,
298 -(self.entries.len() as isize),
299 (self.entries.len() as isize) - 1,
300 ))
301 }
302 };
303
304 match entry {
305 Some(constraint) => Ok(constraint),
306 None => Err(CheckingError::access_deleted(index as isize)),
307 }
308 }
309
310 #[inline]
311 pub fn get_entry_optionally_deleted_usize(
312 &self,
313 index: usize,
314 ) -> Result<Option<&Rc<DBConstraint>>, CheckingError> {
315 let entry = match self.entries.get(index) {
316 Some(entry) => entry,
317 None => {
318 return Err(CheckingError::out_of_bounds(
319 index as isize,
320 -(self.entries.len() as isize),
321 (self.entries.len() as isize) - 1,
322 ))
323 }
324 };
325
326 match entry {
327 Some(constraint) => Ok(Some(constraint)),
328 None => Ok(None),
329 }
330 }
331
332 #[inline]
334 pub fn is_undeleted(&self, index: usize) -> Result<bool, CheckingError> {
335 if let Some(entry) = self.entries.get(index) {
336 return Ok(entry.is_some());
337 }
338 Err(CheckingError::out_of_bounds(
339 index as isize,
340 -(self.entries.len() as isize),
341 (self.entries.len() as isize) - 1,
342 ))
343 }
344
345 #[inline]
347 pub fn move_to_core(
348 &self,
349 prop_engine: &mut PropagationEngine,
350 index: usize,
351 ) -> Result<(), CheckingError> {
352 let constraint = self.get_entry_usize(index)?;
354
355 if !constraint.is_core_constraint() && constraint.header.borrow().propagator_id.is_some() {
357 prop_engine.detach(DERIVED, constraint, false)?;
358 prop_engine.attach(CORE, constraint)?;
359 }
360
361 constraint.move_id_to_core(index);
363
364 Ok(())
365 }
366
367 pub fn move_to_core_all(
369 &self,
370 prop_engine: &mut PropagationEngine,
371 ) -> Result<(), CheckingError> {
372 for constraint in self.unique_constraints.iter() {
374 let header = &mut *constraint.header.borrow_mut();
375 header.core_ids.append(&mut header.derived_ids);
376 }
377 for constraint_id in self.next_unique_index_id..self.len() {
378 if let Some(Some(constraint)) = self.entries.get(constraint_id) {
379 let header = &mut *constraint.header.borrow_mut();
380 header.core_ids.append(&mut header.derived_ids);
381 }
382 }
383
384 for constraint in prop_engine.detach_all(DERIVED) {
386 prop_engine.attach(CORE, &constraint)?;
387 }
388
389 Ok(())
390 }
391
392 #[inline]
394 pub fn get_used_vars(&mut self) -> Vec<VarIdx> {
395 self.update_occurrences_index();
396 self.occurrences.get_used_vars()
397 }
398
399 #[inline]
403 fn add_non_obvious_proofgoals(
404 &self,
405 substitution: &Substitution,
406 lit: Lit,
407 unique_substituted_constraints: &mut AHashSet<Rc<DBConstraint>>,
408 add_derived_goals: bool,
409 exclude_database_autoproving: bool,
410 only_core_subproof: bool,
411 ) {
412 if let Some(constraints) = self.occurrences.get_constraints_for_lit(lit) {
413 for constraint in constraints {
414 if add_derived_goals || constraint.is_core_constraint() {
415 let substituted_constraint = Rc::new(constraint.substitute(substitution));
416 if !substituted_constraint.is_trivial()
423 && !constraint.implies(&substituted_constraint)
424 && (exclude_database_autoproving
425 || self
426 .lookup(&substituted_constraint)
427 .is_none_or(|c| only_core_subproof && !c.is_core_constraint()))
428 {
429 substituted_constraint.copy_ids(constraint);
431 unique_substituted_constraints.insert(substituted_constraint);
432 }
433 }
434 }
435 }
436 }
437
438 #[inline]
442 pub fn get_proofgoals(
443 &mut self,
444 substitution: &Substitution,
445 add_derived_goals: bool,
446 exclude_database_autoproving: bool,
447 only_core_subproof: bool,
448 ) -> AHashSet<Rc<DBConstraint>> {
449 debug_assert_eq!(self.len(), self.next_unique_index_id);
450
451 self.update_occurrences_index();
452 let mut unique_substituted_constraints = AHashSet::new();
453 for &var_idx in substitution.support.iter() {
454 match substitution.get(var_idx).unwrap() {
456 SubstitutionValue::TRUE => {
457 self.add_non_obvious_proofgoals(
458 substitution,
459 Lit::from_var(var_idx, true),
460 &mut unique_substituted_constraints,
461 add_derived_goals,
462 exclude_database_autoproving,
463 only_core_subproof,
464 );
465 }
466 SubstitutionValue::FALSE => {
467 self.add_non_obvious_proofgoals(
468 substitution,
469 Lit::from_var(var_idx, false),
470 &mut unique_substituted_constraints,
471 add_derived_goals,
472 exclude_database_autoproving,
473 only_core_subproof,
474 );
475 }
476 _ => {
477 self.add_non_obvious_proofgoals(
478 substitution,
479 Lit::from_var(var_idx, false),
480 &mut unique_substituted_constraints,
481 add_derived_goals,
482 exclude_database_autoproving,
483 only_core_subproof,
484 );
485 self.add_non_obvious_proofgoals(
486 substitution,
487 Lit::from_var(var_idx, true),
488 &mut unique_substituted_constraints,
489 add_derived_goals,
490 exclude_database_autoproving,
491 only_core_subproof,
492 );
493 }
494 }
495 }
496
497 unique_substituted_constraints
498 }
499
500 #[inline]
501 pub fn get_undeleted(
502 &self,
503 range: Range<usize>,
504 ) -> impl '_ + Iterator<Item = Result<usize, CheckingError>> {
505 range
506 .into_iter()
507 .filter_map(|id| match self.is_undeleted(id) {
508 Ok(true) => Some(Ok(id)),
509 Ok(false) => None,
510 Err(err) => Some(Err(err)),
511 })
512 }
513
514 #[inline]
515 pub fn get_undeleted_non_unique_indexed(&self) -> impl '_ + Iterator<Item = &Rc<DBConstraint>> {
516 self.entries[self.next_unique_index_id..self.len()]
517 .iter()
518 .flatten()
519 }
520
521 #[inline]
523 pub fn len(&self) -> usize {
524 self.entries.len()
525 }
526
527 #[inline]
528 pub fn is_empty(&self) -> bool {
529 self.entries.len() == 0
530 }
531
532 #[inline]
534 pub fn size(&self) -> usize {
535 self.entries.iter().flatten().count()
536 }
537}