1use crate::basic_types::Trail;
2use crate::containers::HashMap;
3use crate::containers::KeyedVec;
4use crate::engine::cp::reason::ReasonRef;
5use crate::engine::notifications::NotificationEngine;
6use crate::engine::predicates::predicate::Predicate;
7use crate::engine::predicates::predicate::PredicateType;
8use crate::engine::variables::DomainGeneratorIterator;
9use crate::engine::variables::DomainId;
10use crate::predicate;
11use crate::pumpkin_assert_eq_moderate;
12use crate::pumpkin_assert_eq_simple;
13use crate::pumpkin_assert_moderate;
14use crate::pumpkin_assert_simple;
15use crate::variables::IntegerVariable;
16
17#[derive(Clone, Debug)]
18pub struct Assignments {
19 pub(crate) trail: Trail<ConstraintProgrammingTrailEntry>,
20 bounds: KeyedVec<DomainId, (i32, i32)>,
23 domains: KeyedVec<DomainId, IntegerDomain>,
24 pruned_values: u64,
26}
27
28impl Default for Assignments {
29 fn default() -> Self {
30 let mut assignments = Self {
31 trail: Default::default(),
32 bounds: Default::default(),
33 domains: Default::default(),
34 pruned_values: 0,
35 };
36
37 let dummy_variable = assignments.grow(1, 1);
40 assert_eq!(dummy_variable.id(), 0);
41
42 assignments
43 }
44}
45
46#[derive(Clone, Copy, Debug)]
47pub struct EmptyDomain;
48
49impl Assignments {
50 #[allow(unused, reason = "Could be used in the future")]
51 pub(crate) fn get_holes_at_checkpoint(
53 &self,
54 domain_id: DomainId,
55 checkpoint: usize,
56 ) -> impl Iterator<Item = i32> + '_ {
57 self.domains[domain_id].get_holes_at_checkpoint(checkpoint)
58 }
59
60 pub(crate) fn get_holes_at_current_checkpoint(
62 &self,
63 domain_id: DomainId,
64 ) -> impl Iterator<Item = i32> + '_ {
65 self.domains[domain_id].get_holes_from_current_checkpoint(self.get_checkpoint())
66 }
67
68 pub(crate) fn get_holes(&self, domain_id: DomainId) -> impl Iterator<Item = i32> + '_ {
71 self.domains[domain_id].get_holes()
72 }
73
74 pub(crate) fn new_checkpoint(&mut self) {
75 self.trail.new_checkpoint()
76 }
77
78 pub(crate) fn find_last_decision(&self) -> Option<Predicate> {
79 if self.get_checkpoint() == 0 {
80 None
81 } else {
82 let values_at_current_checkpoint =
83 self.trail.values_at_checkpoint(self.get_checkpoint());
84 let entry = &values_at_current_checkpoint[0];
85 pumpkin_assert_eq_simple!(None, entry.reason);
86
87 Some(entry.predicate)
88 }
89 }
90
91 pub(crate) fn get_checkpoint(&self) -> usize {
92 self.trail.get_checkpoint()
93 }
94
95 pub(crate) fn num_domains(&self) -> u32 {
96 self.domains.len() as u32
97 }
98
99 pub(crate) fn get_domains(&self) -> DomainGeneratorIterator {
100 DomainGeneratorIterator::new(1, self.num_domains())
103 }
104
105 pub(crate) fn num_trail_entries(&self) -> usize {
106 self.trail.len()
107 }
108
109 pub(crate) fn get_trail_entry(&self, index: usize) -> ConstraintProgrammingTrailEntry {
110 self.trail[index].clone()
111 }
112
113 pub(crate) fn grow(&mut self, lower_bound: i32, upper_bound: i32) -> DomainId {
118 pumpkin_assert_simple!(
122 self.get_checkpoint() == 0,
123 "can only create variables at the root"
124 );
125
126 let id = DomainId::new(self.num_domains());
127
128 let lower_bound_position = self.trail.len();
129 self.trail.push(ConstraintProgrammingTrailEntry {
130 predicate: predicate!(id >= lower_bound),
131 old_lower_bound: lower_bound,
132 old_upper_bound: upper_bound,
133 reason: None,
134 });
135 let upper_bound_position = self.trail.len();
136 self.trail.push(ConstraintProgrammingTrailEntry {
137 predicate: predicate!(id <= upper_bound),
138 old_lower_bound: lower_bound,
139 old_upper_bound: upper_bound,
140 reason: None,
141 });
142
143 let _ = self.domains.push(IntegerDomain::new(
144 lower_bound,
145 lower_bound_position,
146 upper_bound,
147 upper_bound_position,
148 id,
149 ));
150
151 let _ = self.bounds.push((lower_bound, upper_bound));
152
153 id
154 }
155 pub fn create_new_integer_variable_sparse(&mut self, mut values: Vec<i32>) -> DomainId {
156 assert!(
157 !values.is_empty(),
158 "cannot create a variable with an empty domain"
159 );
160
161 values.sort();
162 values.dedup();
163
164 let lower_bound = values[0];
165 let upper_bound = values[values.len() - 1];
166
167 let domain_id = self.grow(lower_bound, upper_bound);
168
169 let mut next_idx = 0;
170 for value in lower_bound..=upper_bound {
171 if value == values[next_idx] {
172 next_idx += 1;
173 } else {
174 let _ = self
175 .remove_value_from_domain(domain_id, value, None)
176 .expect("the domain should not be empty");
177
178 self.domains[domain_id].initial_holes.push(value);
179 }
180 }
181 self.domains[domain_id].initial_bounds_below_trail = self.trail.len() - 1;
182 pumpkin_assert_simple!(
183 next_idx == values.len(),
184 "Expected all values to have been processed"
185 );
186
187 self.update_bounds_snapshot(domain_id);
188
189 domain_id
190 }
191
192 pub(crate) fn debug_create_empty_clone(&self) -> Self {
193 let mut new_assignment = Assignments::default();
194
195 for domain_id in self.domains.keys().skip(1) {
197 let lower_bound = self.get_initial_lower_bound(domain_id);
198 let upper_bound = self.get_initial_upper_bound(domain_id);
199 let holes = self.get_initial_holes(domain_id);
200
201 let new_domain_id = new_assignment.grow(lower_bound, upper_bound);
202 assert_eq!(new_domain_id, domain_id);
203
204 for hole in holes {
205 let changed_domain = new_assignment
206 .remove_value_from_domain(domain_id, hole, None)
207 .expect("initial domain cannot be empty");
208
209 assert!(changed_domain);
210 }
211
212 new_assignment.domains[new_domain_id].initial_bounds_below_trail =
213 new_assignment.trail.len() - 1;
214 }
215
216 new_assignment
217 }
218
219 pub(crate) fn is_initial_bound(&self, predicate: Predicate) -> bool {
220 let domain_id = predicate.get_domain();
221
222 let Some(trail_position) = self.get_trail_position(&predicate) else {
223 return false;
224 };
225
226 trail_position <= self.domains[domain_id].initial_bounds_below_trail
227 }
228}
229
230impl Assignments {
232 pub(crate) fn get_lower_bound(&self, domain_id: DomainId) -> i32 {
233 let (lower_bound, _) = self.bounds[domain_id];
234
235 pumpkin_assert_eq_moderate!(
236 lower_bound,
237 self.domains[domain_id].lower_bound(),
238 "bounds for {domain_id} out of sync"
239 );
240
241 lower_bound
242 }
243
244 pub(crate) fn get_lower_bound_at_trail_position(
245 &self,
246 domain_id: DomainId,
247 trail_position: usize,
248 ) -> i32 {
249 self.domains[domain_id].lower_bound_at_trail_position(trail_position)
250 }
251
252 pub(crate) fn get_upper_bound(&self, domain_id: DomainId) -> i32 {
253 let (_, upper_bound) = self.bounds[domain_id];
254
255 pumpkin_assert_eq_moderate!(
256 upper_bound,
257 self.domains[domain_id].upper_bound(),
258 "bounds for {domain_id} out of sync"
259 );
260
261 upper_bound
262 }
263
264 pub(crate) fn get_upper_bound_at_trail_position(
265 &self,
266 domain_id: DomainId,
267 trail_position: usize,
268 ) -> i32 {
269 self.domains[domain_id].upper_bound_at_trail_position(trail_position)
270 }
271
272 pub(crate) fn get_initial_lower_bound(&self, domain_id: DomainId) -> i32 {
273 self.domains[domain_id].initial_lower_bound()
274 }
275
276 pub(crate) fn get_initial_upper_bound(&self, domain_id: DomainId) -> i32 {
277 self.domains[domain_id].initial_upper_bound()
278 }
279
280 pub(crate) fn get_initial_holes(&self, domain_id: DomainId) -> Vec<i32> {
281 self.domains[domain_id].initial_holes.clone()
282 }
283
284 pub(crate) fn get_assigned_value<Var: IntegerVariable>(&self, var: &Var) -> Option<i32> {
285 self.is_domain_assigned(var).then(|| var.lower_bound(self))
286 }
287
288 pub(crate) fn is_decision_predicate(&self, predicate: &Predicate) -> bool {
289 let domain = predicate.get_domain();
290 if let Some(trail_position) = self.get_trail_position(predicate)
291 && trail_position > self.domains[domain].initial_bounds_below_trail
292 {
293 self.trail[trail_position].reason.is_none()
294 && self.trail[trail_position].predicate == *predicate
295 } else {
296 false
297 }
298 }
299
300 pub(crate) fn get_domain_iterator(&self, domain_id: DomainId) -> IntegerDomainIterator<'_> {
301 self.domains[domain_id].domain_iterator()
302 }
303
304 pub(crate) fn get_domain_description(&self, domain_id: DomainId) -> Vec<Predicate> {
307 let mut predicates = Vec::new();
308 let domain = &self.domains[domain_id];
309
310 if domain.lower_bound() == domain.upper_bound()
312 && domain.lower_bound_checkpoint() > 0
313 && domain.checkpoint() > 0
314 {
315 predicates.push(predicate![domain_id == domain.lower_bound()]);
316 return predicates;
317 }
318
319 if domain.lower_bound_checkpoint() > 0 {
321 predicates.push(predicate![domain_id >= domain.lower_bound()]);
322 }
323
324 if domain.checkpoint() > 0 {
325 predicates.push(predicate![domain_id <= domain.upper_bound()]);
326 }
327
328 for hole in &self.domains[domain_id].holes {
330 if hole.1.checkpoint > 0
335 && domain.lower_bound() < *hole.0
336 && *hole.0 < domain.upper_bound()
337 {
338 predicates.push(predicate![domain_id != *hole.0]);
339 }
340 }
341 predicates
342 }
343
344 pub(crate) fn is_value_in_domain(&self, domain_id: DomainId, value: i32) -> bool {
345 let (lower_bound, upper_bound) = self.bounds[domain_id];
346
347 if value < lower_bound || value > upper_bound {
348 return false;
349 }
350
351 let domain = &self.domains[domain_id];
352 domain.contains(value)
353 }
354
355 pub(crate) fn is_value_in_domain_at_trail_position(
356 &self,
357 domain_id: DomainId,
358 value: i32,
359 trail_position: usize,
360 ) -> bool {
361 self.domains[domain_id].contains_at_trail_position(value, trail_position)
362 }
363
364 pub(crate) fn is_domain_assigned<Var: IntegerVariable>(&self, var: &Var) -> bool {
365 var.lower_bound(self) == var.upper_bound(self)
366 }
367
368 pub(crate) fn get_trail_position(&self, predicate: &Predicate) -> Option<usize> {
374 self.domains[predicate.get_domain()]
375 .get_update_info(predicate)
376 .map(|u| u.trail_position)
377 }
378
379 pub(crate) fn get_checkpoint_for_predicate(&self, predicate: &Predicate) -> Option<usize> {
382 self.domains[predicate.get_domain()]
383 .get_update_info(predicate)
384 .map(|u| u.checkpoint)
385 }
386
387 pub fn get_domain_descriptions(&self) -> Vec<Predicate> {
388 let mut descriptions: Vec<Predicate> = vec![];
389 for domain in self.domains.iter().enumerate() {
390 let domain_id = DomainId::new(domain.0 as u32);
391 descriptions.append(&mut self.get_domain_description(domain_id));
392 }
393 descriptions
394 }
395}
396
397impl Assignments {
399 fn tighten_lower_bound(
400 &mut self,
401 domain_id: DomainId,
402 new_lower_bound: i32,
403 reason: Option<ReasonRef>,
404 ) -> Result<bool, EmptyDomain> {
405 if new_lower_bound <= self.get_lower_bound(domain_id) {
407 return self.domains[domain_id].verify_consistency();
408 }
409
410 let predicate = predicate!(domain_id >= new_lower_bound);
411
412 let old_lower_bound = self.get_lower_bound(domain_id);
413 let old_upper_bound = self.get_upper_bound(domain_id);
414
415 let trail_position = self.trail.len();
417
418 self.trail.push(ConstraintProgrammingTrailEntry {
419 predicate,
420 old_lower_bound,
421 old_upper_bound,
422 reason,
423 });
424
425 let checkpoint = self.get_checkpoint();
426 let domain = &mut self.domains[domain_id];
427
428 let update_took_place = domain.set_lower_bound(new_lower_bound, checkpoint, trail_position);
429
430 self.bounds[domain_id].0 = domain.lower_bound();
431
432 self.pruned_values += domain.lower_bound().abs_diff(old_lower_bound) as u64;
433
434 let _ = domain.verify_consistency()?;
435
436 Ok(update_took_place)
437 }
438
439 fn tighten_upper_bound(
440 &mut self,
441 domain_id: DomainId,
442 new_upper_bound: i32,
443 reason: Option<ReasonRef>,
444 ) -> Result<bool, EmptyDomain> {
445 if new_upper_bound >= self.get_upper_bound(domain_id) {
447 return self.domains[domain_id].verify_consistency();
448 }
449
450 let predicate = predicate!(domain_id <= new_upper_bound);
451
452 let old_lower_bound = self.get_lower_bound(domain_id);
453 let old_upper_bound = self.get_upper_bound(domain_id);
454
455 let trail_position = self.trail.len();
457
458 self.trail.push(ConstraintProgrammingTrailEntry {
459 predicate,
460 old_lower_bound,
461 old_upper_bound,
462 reason,
463 });
464
465 let checkpoint = self.get_checkpoint();
466 let domain = &mut self.domains[domain_id];
467
468 let update_took_place = domain.set_upper_bound(new_upper_bound, checkpoint, trail_position);
469
470 self.bounds[domain_id].1 = domain.upper_bound();
471
472 self.pruned_values += old_upper_bound.abs_diff(domain.upper_bound()) as u64;
473
474 let _ = domain.verify_consistency()?;
475
476 Ok(update_took_place)
477 }
478
479 fn make_assignment(
480 &mut self,
481 domain_id: DomainId,
482 assigned_value: i32,
483 reason: Option<ReasonRef>,
484 ) -> Result<bool, EmptyDomain> {
485 let mut update_took_place = false;
486
487 let predicate = predicate!(domain_id == assigned_value);
488
489 let old_lower_bound = self.get_lower_bound(domain_id);
490 let old_upper_bound = self.get_upper_bound(domain_id);
491
492 if old_lower_bound == assigned_value && old_upper_bound == assigned_value {
493 return self.domains[domain_id].verify_consistency();
494 }
495
496 let trail_position = self.trail.len();
498
499 self.trail.push(ConstraintProgrammingTrailEntry {
500 predicate,
501 old_lower_bound,
502 old_upper_bound,
503 reason,
504 });
505
506 let checkpoint = self.get_checkpoint();
507 let domain = &mut self.domains[domain_id];
508
509 if old_lower_bound < assigned_value {
510 update_took_place |= domain.set_lower_bound(assigned_value, checkpoint, trail_position);
511 self.bounds[domain_id].0 = domain.lower_bound();
512 self.pruned_values += domain.lower_bound().abs_diff(old_lower_bound) as u64;
513 }
514
515 if old_upper_bound > assigned_value {
516 update_took_place |= domain.set_upper_bound(assigned_value, checkpoint, trail_position);
517 self.bounds[domain_id].1 = domain.upper_bound();
518 self.pruned_values += domain.upper_bound().abs_diff(old_upper_bound) as u64;
519 }
520
521 let _ = self.domains[domain_id].verify_consistency()?;
522
523 Ok(update_took_place)
524 }
525
526 fn remove_value_from_domain(
527 &mut self,
528 domain_id: DomainId,
529 removed_value_from_domain: i32,
530 reason: Option<ReasonRef>,
531 ) -> Result<bool, EmptyDomain> {
532 if !self.domains[domain_id].contains(removed_value_from_domain) {
534 return self.domains[domain_id].verify_consistency();
535 }
536
537 let predicate = predicate!(domain_id != removed_value_from_domain);
538
539 let old_lower_bound = self.get_lower_bound(domain_id);
540 let old_upper_bound = self.get_upper_bound(domain_id);
541
542 let trail_position = self.trail.len();
544
545 self.trail.push(ConstraintProgrammingTrailEntry {
546 predicate,
547 old_lower_bound,
548 old_upper_bound,
549 reason,
550 });
551
552 let checkpoint = self.get_checkpoint();
553 let domain = &mut self.domains[domain_id];
554
555 let _ = domain.remove_value(removed_value_from_domain, checkpoint, trail_position);
556
557 let changed_lower_bound = domain.lower_bound().abs_diff(old_lower_bound) as u64;
558 let changed_upper_bound = old_upper_bound.abs_diff(domain.upper_bound()) as u64;
559
560 if changed_lower_bound + changed_upper_bound > 0 {
561 self.pruned_values += changed_upper_bound + changed_lower_bound;
562 } else {
563 self.pruned_values += 1;
564 }
565
566 self.update_bounds_snapshot(domain_id);
567 let _ = self.domains[domain_id].verify_consistency()?;
568
569 Ok(true)
570 }
571
572 pub(crate) fn post_predicate(
579 &mut self,
580 predicate: Predicate,
581 reason: Option<ReasonRef>,
582 notification_engine: &mut NotificationEngine,
583 ) -> Result<bool, EmptyDomain> {
584 let (lower_bound_before, upper_bound_before) = self.bounds[predicate.get_domain()];
585
586 let mut removal_took_place = false;
587
588 let domain_id = predicate.get_domain();
589 let value = predicate.get_right_hand_side();
590
591 let update_took_place = match predicate.get_predicate_type() {
592 PredicateType::LowerBound => self.tighten_lower_bound(domain_id, value, reason)?,
593 PredicateType::UpperBound => self.tighten_upper_bound(domain_id, value, reason)?,
594 PredicateType::NotEqual => {
595 removal_took_place = self.remove_value_from_domain(domain_id, value, reason)?;
596 removal_took_place
597 }
598 PredicateType::Equal => self.make_assignment(domain_id, value, reason)?,
599 };
600
601 if update_took_place {
602 notification_engine.event_occurred(
603 lower_bound_before,
604 upper_bound_before,
605 self.domains[predicate.get_domain()].lower_bound(),
606 self.domains[predicate.get_domain()].upper_bound(),
607 removal_took_place,
608 predicate.get_domain(),
609 );
610 }
611
612 Ok(update_took_place)
613 }
614
615 pub(crate) fn evaluate_predicate(&self, predicate: Predicate) -> Option<bool> {
619 let domain_id = predicate.get_domain();
620 let value = predicate.get_right_hand_side();
621
622 match predicate.get_predicate_type() {
623 PredicateType::LowerBound => {
624 if self.get_lower_bound(domain_id) >= value {
625 Some(true)
626 } else if self.get_upper_bound(domain_id) < value {
627 Some(false)
628 } else {
629 None
630 }
631 }
632 PredicateType::UpperBound => {
633 if self.get_upper_bound(domain_id) <= value {
634 Some(true)
635 } else if self.get_lower_bound(domain_id) > value {
636 Some(false)
637 } else {
638 None
639 }
640 }
641 PredicateType::NotEqual => {
642 if !self.is_value_in_domain(domain_id, value) {
643 Some(true)
644 } else if let Some(assigned_value) = self.get_assigned_value(&domain_id) {
645 pumpkin_assert_simple!(assigned_value == value);
648 Some(false)
649 } else {
650 None
651 }
652 }
653 PredicateType::Equal => {
654 if !self.is_value_in_domain(domain_id, value) {
655 Some(false)
656 } else if let Some(assigned_value) = self.get_assigned_value(&domain_id) {
657 pumpkin_assert_moderate!(assigned_value == value);
658 Some(true)
659 } else {
660 None
661 }
662 }
663 }
664 }
665
666 pub(crate) fn is_predicate_satisfied(&self, predicate: Predicate) -> bool {
667 self.evaluate_predicate(predicate)
668 .is_some_and(|truth_value| truth_value)
669 }
670
671 #[allow(unused, reason = "makes sense to have in this API")]
672 pub(crate) fn is_predicate_falsified(&self, predicate: Predicate) -> bool {
673 self.evaluate_predicate(predicate)
674 .is_some_and(|truth_value| !truth_value)
675 }
676
677 pub(crate) fn synchronise(
682 &mut self,
683 new_checkpoint: usize,
684 notification_engine: &mut NotificationEngine,
685 ) -> Vec<(DomainId, i32)> {
686 let mut unfixed_variables = Vec::new();
687 let num_trail_entries_before_synchronisation = self.num_trail_entries();
688
689 pumpkin_assert_simple!(
690 new_checkpoint <= self.trail.get_checkpoint(),
691 "Expected the new decision level {new_checkpoint} to be less than or equal to the current decision level {}",
692 self.trail.get_checkpoint(),
693 );
694
695 self.trail
696 .synchronise(new_checkpoint)
697 .enumerate()
698 .for_each(|(index, entry)| {
699 let domain_id = entry.predicate.get_domain();
701 let lower_bound_before = self.domains[domain_id].lower_bound();
702 let upper_bound_before = self.domains[domain_id].upper_bound();
703
704 let trail_index = num_trail_entries_before_synchronisation - index - 1;
705
706 let add_on_upper_bound = entry.old_upper_bound.abs_diff(upper_bound_before) as u64;
707 let add_on_lower_bound = entry.old_lower_bound.abs_diff(lower_bound_before) as u64;
708 self.pruned_values -= add_on_upper_bound + add_on_lower_bound;
709
710 if entry.predicate.is_not_equal_predicate()
711 && add_on_lower_bound + add_on_upper_bound == 0
712 {
713 self.pruned_values -= 1;
714 }
715
716 let fixed_before =
717 self.domains[domain_id].lower_bound() == self.domains[domain_id].upper_bound();
718 self.domains[domain_id].undo_trail_entry(&entry);
719
720 let new_lower_bound = self.domains[domain_id].lower_bound();
721 let new_upper_bound = self.domains[domain_id].upper_bound();
722 self.bounds[domain_id] = (new_lower_bound, new_upper_bound);
723
724 notification_engine.undo_trail_entry(
725 fixed_before,
726 lower_bound_before,
727 upper_bound_before,
728 new_lower_bound,
729 new_upper_bound,
730 trail_index,
731 entry.predicate,
732 );
733
734 if new_lower_bound != new_upper_bound {
735 unfixed_variables.push((domain_id, lower_bound_before));
737 }
738 });
739
740 notification_engine.clear_events();
743
744 unfixed_variables
745 }
746
747 pub(crate) fn remove_last_trail_element(&mut self) -> (Predicate, ReasonRef) {
749 let entry = self.trail.pop().unwrap();
750 let domain_id = entry.predicate.get_domain();
751 self.domains[domain_id].undo_trail_entry(&entry);
752 self.update_bounds_snapshot(domain_id);
753
754 let reason_ref = entry.reason.unwrap();
755
756 (entry.predicate, reason_ref)
757 }
758
759 pub(crate) fn get_pruned_value_count(&self) -> u64 {
761 self.pruned_values
762 }
763
764 fn update_bounds_snapshot(&mut self, domain_id: DomainId) {
765 self.bounds[domain_id] = (
766 self.domains[domain_id].lower_bound(),
767 self.domains[domain_id].upper_bound(),
768 );
769 }
770}
771
772impl Assignments {
773 #[deprecated]
774 pub(crate) fn get_reason_for_predicate_brute_force(&self, predicate: Predicate) -> ReasonRef {
775 self.trail
776 .iter()
777 .find_map(|entry| {
778 if entry.predicate == predicate {
779 entry.reason
780 } else {
781 None
782 }
783 })
784 .unwrap_or_else(|| panic!("could not find a reason for predicate {predicate}"))
785 }
786}
787
788#[derive(Clone, Debug)]
789pub(crate) struct ConstraintProgrammingTrailEntry {
790 pub predicate: Predicate,
791 pub(crate) old_lower_bound: i32,
794 pub(crate) old_upper_bound: i32,
795 pub(crate) reason: Option<ReasonRef>,
799}
800
801#[derive(Clone, Copy, Debug)]
802struct PairDecisionLevelTrailPosition {
803 checkpoint: usize,
804 trail_position: usize,
805}
806
807#[derive(Clone, Debug)]
808struct BoundUpdateInfo {
809 bound: i32,
810 checkpoint: usize,
811 trail_position: usize,
812}
813
814#[derive(Clone, Debug)]
815struct HoleUpdateInfo {
816 removed_value: i32,
817
818 checkpoint: usize,
819
820 triggered_lower_bound_update: bool,
821 triggered_upper_bound_update: bool,
822}
823
824#[derive(Clone, Debug)]
830struct IntegerDomain {
831 id: DomainId,
832 lower_bound_updates: Vec<BoundUpdateInfo>,
834 upper_bound_updates: Vec<BoundUpdateInfo>,
835 hole_updates: Vec<HoleUpdateInfo>,
836 holes: HashMap<i32, PairDecisionLevelTrailPosition>,
841 initial_bounds_below_trail: usize,
843 initial_holes: Vec<i32>,
845}
846
847impl IntegerDomain {
848 fn new(
849 lower_bound: i32,
850 lower_bound_position: usize,
851 upper_bound: i32,
852 upper_bound_position: usize,
853 id: DomainId,
854 ) -> IntegerDomain {
855 pumpkin_assert_simple!(lower_bound <= upper_bound, "Cannot create an empty domain.");
856
857 let lower_bound_updates = vec![BoundUpdateInfo {
858 bound: lower_bound,
859 checkpoint: 0,
860 trail_position: lower_bound_position,
861 }];
862
863 let upper_bound_updates = vec![BoundUpdateInfo {
864 bound: upper_bound,
865 checkpoint: 0,
866 trail_position: upper_bound_position,
867 }];
868
869 IntegerDomain {
870 id,
871 initial_holes: vec![],
872 lower_bound_updates,
873 upper_bound_updates,
874 hole_updates: vec![],
875 holes: Default::default(),
876 initial_bounds_below_trail: std::cmp::max(lower_bound_position, upper_bound_position),
877 }
878 }
879
880 fn lower_bound(&self) -> i32 {
881 self.lower_bound_updates
883 .last()
884 .expect("Cannot be empty.")
885 .bound
886 }
887
888 fn lower_bound_checkpoint(&self) -> usize {
889 self.lower_bound_updates
890 .last()
891 .expect("Cannot be empty.")
892 .checkpoint
893 }
894
895 fn initial_lower_bound(&self) -> i32 {
896 self.lower_bound_updates[0].bound
899 }
900
901 fn lower_bound_at_trail_position(&self, trail_position: usize) -> i32 {
902 let index = self
914 .lower_bound_updates
915 .partition_point(|u| u.trail_position <= trail_position);
916
917 self.lower_bound_updates[index.saturating_sub(1)].bound
918 }
919
920 fn upper_bound(&self) -> i32 {
921 self.upper_bound_updates
923 .last()
924 .expect("Cannot be empty.")
925 .bound
926 }
927
928 fn checkpoint(&self) -> usize {
929 self.upper_bound_updates
930 .last()
931 .expect("Cannot be empty.")
932 .checkpoint
933 }
934
935 fn initial_upper_bound(&self) -> i32 {
936 self.upper_bound_updates[0].bound
939 }
940
941 fn upper_bound_at_trail_position(&self, trail_position: usize) -> i32 {
942 let index = self
954 .upper_bound_updates
955 .partition_point(|u| u.trail_position <= trail_position)
956 .saturating_sub(1);
957
958 self.upper_bound_updates[index].bound
959 }
960
961 fn domain_iterator(&self) -> IntegerDomainIterator<'_> {
962 IntegerDomainIterator::new(self)
967 }
968
969 fn contains(&self, value: i32) -> bool {
970 self.lower_bound() <= value
971 && value <= self.upper_bound()
972 && !self.holes.contains_key(&value)
973 }
974
975 fn contains_at_trail_position(&self, value: i32, trail_position: usize) -> bool {
976 if self.lower_bound_at_trail_position(trail_position) > value
979 || self.upper_bound_at_trail_position(trail_position) < value
980 {
981 return false;
982 }
983 if let Some(hole_info) = self.holes.get(&value)
988 && hole_info.trail_position <= trail_position
989 {
990 return false;
991 }
992
993 true
995 }
996
997 fn remove_value(
998 &mut self,
999 removed_value: i32,
1000 checkpoint: usize,
1001 trail_position: usize,
1002 ) -> bool {
1003 if removed_value < self.lower_bound()
1004 || removed_value > self.upper_bound()
1005 || self.holes.contains_key(&removed_value)
1006 {
1007 return false;
1008 }
1009
1010 self.hole_updates.push(HoleUpdateInfo {
1011 removed_value,
1012 checkpoint,
1013 triggered_lower_bound_update: false,
1014 triggered_upper_bound_update: false,
1015 });
1016 let old_none_entry = self.holes.insert(
1019 removed_value,
1020 PairDecisionLevelTrailPosition {
1021 checkpoint,
1022 trail_position,
1023 },
1024 );
1025 pumpkin_assert_moderate!(old_none_entry.is_none());
1026
1027 if self.lower_bound() == removed_value {
1029 let _ = self.set_lower_bound(removed_value + 1, checkpoint, trail_position);
1030 self.hole_updates
1031 .last_mut()
1032 .expect("we just pushed a value, so must be present")
1033 .triggered_lower_bound_update = true;
1034 }
1035 if self.upper_bound() == removed_value {
1037 let _ = self.set_upper_bound(removed_value - 1, checkpoint, trail_position);
1038 self.hole_updates
1039 .last_mut()
1040 .expect("we just pushed a value, so must be present")
1041 .triggered_upper_bound_update = true;
1042 }
1043
1044 true
1045 }
1046
1047 fn debug_is_valid_upper_bound_domain_update(
1048 &self,
1049 checkpoint: usize,
1050 trail_position: usize,
1051 ) -> bool {
1052 self.upper_bound_updates.last().unwrap().checkpoint <= checkpoint
1053 && self.upper_bound_updates.last().unwrap().trail_position < trail_position
1054 }
1055
1056 fn set_upper_bound(
1057 &mut self,
1058 new_upper_bound: i32,
1059 checkpoint: usize,
1060 trail_position: usize,
1061 ) -> bool {
1062 pumpkin_assert_moderate!(
1063 self.debug_is_valid_upper_bound_domain_update(checkpoint, trail_position)
1064 );
1065
1066 if new_upper_bound >= self.upper_bound() {
1067 return false;
1068 }
1069
1070 self.upper_bound_updates.push(BoundUpdateInfo {
1071 bound: new_upper_bound,
1072 checkpoint,
1073 trail_position,
1074 });
1075 self.update_upper_bound_with_respect_to_holes();
1076
1077 true
1078 }
1079
1080 fn update_upper_bound_with_respect_to_holes(&mut self) {
1081 while self.holes.contains_key(&self.upper_bound())
1082 && self.lower_bound() <= self.upper_bound()
1083 {
1084 self.upper_bound_updates.last_mut().unwrap().bound -= 1;
1085 }
1086 }
1087
1088 fn debug_is_valid_lower_bound_domain_update(
1089 &self,
1090 checkpoint: usize,
1091 trail_position: usize,
1092 ) -> bool {
1093 trail_position == 0
1094 || self.lower_bound_updates.last().unwrap().checkpoint <= checkpoint
1095 && self.lower_bound_updates.last().unwrap().trail_position < trail_position
1096 }
1097
1098 fn set_lower_bound(
1099 &mut self,
1100 new_lower_bound: i32,
1101 checkpoint: usize,
1102 trail_position: usize,
1103 ) -> bool {
1104 pumpkin_assert_moderate!(
1105 self.debug_is_valid_lower_bound_domain_update(checkpoint, trail_position)
1106 );
1107
1108 if new_lower_bound <= self.lower_bound() {
1109 return false;
1110 }
1111
1112 self.lower_bound_updates.push(BoundUpdateInfo {
1113 bound: new_lower_bound,
1114 checkpoint,
1115 trail_position,
1116 });
1117 self.update_lower_bound_with_respect_to_holes();
1118
1119 true
1120 }
1121
1122 fn update_lower_bound_with_respect_to_holes(&mut self) {
1123 while self.holes.contains_key(&self.lower_bound())
1124 && self.lower_bound() <= self.upper_bound()
1125 {
1126 self.lower_bound_updates.last_mut().unwrap().bound += 1;
1127 }
1128 }
1129
1130 fn debug_bounds_check(&self) -> bool {
1131 if self.lower_bound() > self.upper_bound() {
1133 true
1134 } else {
1135 self.lower_bound() >= self.initial_lower_bound()
1136 && self.upper_bound() <= self.initial_upper_bound()
1137 && !self.holes.contains_key(&self.lower_bound())
1138 && !self.holes.contains_key(&self.upper_bound())
1139 }
1140 }
1141
1142 fn verify_consistency(&self) -> Result<bool, EmptyDomain> {
1143 if self.lower_bound() > self.upper_bound() {
1144 Err(EmptyDomain)
1145 } else {
1146 Ok(false)
1147 }
1148 }
1149
1150 fn undo_trail_entry(&mut self, entry: &ConstraintProgrammingTrailEntry) {
1151 let domain_id = entry.predicate.get_domain();
1152 match entry.predicate.get_predicate_type() {
1153 PredicateType::LowerBound => {
1154 pumpkin_assert_moderate!(domain_id == self.id);
1155
1156 let _ = self.lower_bound_updates.pop();
1157 pumpkin_assert_moderate!(!self.lower_bound_updates.is_empty());
1158 }
1159 PredicateType::UpperBound => {
1160 pumpkin_assert_moderate!(domain_id == self.id);
1161
1162 let _ = self.upper_bound_updates.pop();
1163 pumpkin_assert_moderate!(!self.upper_bound_updates.is_empty());
1164 }
1165 PredicateType::NotEqual => {
1166 pumpkin_assert_moderate!(domain_id == self.id);
1167
1168 let not_equal_constant = entry.predicate.get_right_hand_side();
1169
1170 let hole_update = self
1171 .hole_updates
1172 .pop()
1173 .expect("Must have record of domain removal.");
1174 pumpkin_assert_moderate!(hole_update.removed_value == not_equal_constant);
1175
1176 let _ = self
1177 .holes
1178 .remove(¬_equal_constant)
1179 .expect("Must be present.");
1180
1181 if hole_update.triggered_lower_bound_update {
1182 let _ = self.lower_bound_updates.pop();
1183 pumpkin_assert_moderate!(!self.lower_bound_updates.is_empty());
1184 }
1185
1186 if hole_update.triggered_upper_bound_update {
1187 let _ = self.upper_bound_updates.pop();
1188 pumpkin_assert_moderate!(!self.upper_bound_updates.is_empty());
1189 }
1190 }
1191 PredicateType::Equal => {
1192 let lower_bound_update = self.lower_bound_updates.last().unwrap();
1193 let upper_bound_update = self.upper_bound_updates.last().unwrap();
1194
1195 if lower_bound_update.trail_position > upper_bound_update.trail_position {
1196 let _ = self.lower_bound_updates.pop();
1197 } else if upper_bound_update.trail_position > lower_bound_update.trail_position {
1198 let _ = self.upper_bound_updates.pop();
1199 } else {
1200 let _ = self.lower_bound_updates.pop();
1201 let _ = self.upper_bound_updates.pop();
1202 }
1203 }
1204 };
1205
1206 pumpkin_assert_eq_simple!(self.lower_bound(), entry.old_lower_bound);
1209 pumpkin_assert_eq_simple!(self.upper_bound(), entry.old_upper_bound);
1210
1211 pumpkin_assert_moderate!(self.debug_bounds_check());
1212 }
1213
1214 fn get_update_info(&self, predicate: &Predicate) -> Option<PairDecisionLevelTrailPosition> {
1215 let domain_id = predicate.get_domain();
1219 let value = predicate.get_right_hand_side();
1220
1221 match predicate.get_predicate_type() {
1222 PredicateType::LowerBound => {
1223 let position = self
1229 .lower_bound_updates
1230 .partition_point(|u| u.bound < value);
1231
1232 (position < self.lower_bound_updates.len()).then(|| {
1233 let u = &self.lower_bound_updates[position];
1234 PairDecisionLevelTrailPosition {
1235 checkpoint: u.checkpoint,
1236 trail_position: u.trail_position,
1237 }
1238 })
1239 }
1240 PredicateType::UpperBound => {
1241 let position = self
1247 .upper_bound_updates
1248 .partition_point(|u| u.bound > value);
1249
1250 (position < self.upper_bound_updates.len()).then(|| {
1251 let u = &self.upper_bound_updates[position];
1252 PairDecisionLevelTrailPosition {
1253 checkpoint: u.checkpoint,
1254 trail_position: u.trail_position,
1255 }
1256 })
1257 }
1258 PredicateType::NotEqual => {
1259 if let Some(hole_info) = self.holes.get(&value) {
1263 Some(*hole_info)
1264 } else {
1265 if let Some(trail_position) =
1274 self.get_update_info(&predicate!(domain_id >= value + 1))
1275 {
1276 Some(trail_position)
1279 } else {
1280 self.get_update_info(&predicate!(domain_id <= value - 1))
1283 }
1284 }
1285 }
1286 PredicateType::Equal => {
1287 if let Some(lb_trail_position) =
1290 self.get_update_info(&predicate!(domain_id >= value))
1291 {
1292 self.get_update_info(&predicate!(domain_id <= value))
1299 .map(|ub_trail_position| {
1300 if lb_trail_position.trail_position > ub_trail_position.trail_position {
1301 lb_trail_position
1302 } else {
1303 ub_trail_position
1304 }
1305 })
1306 }
1307 else {
1310 None
1311 }
1312 }
1313 }
1314 }
1315
1316 pub(crate) fn get_holes_at_checkpoint(
1318 &self,
1319 checkpoint: usize,
1320 ) -> impl Iterator<Item = i32> + '_ {
1321 self.hole_updates
1322 .iter()
1323 .filter(move |entry| entry.checkpoint == checkpoint)
1324 .map(|entry| entry.removed_value)
1325 }
1326
1327 pub(crate) fn get_holes_from_current_checkpoint(
1329 &self,
1330 current_checkpoint: usize,
1331 ) -> impl Iterator<Item = i32> + '_ {
1332 self.hole_updates
1333 .iter()
1334 .rev()
1335 .take_while(move |entry| entry.checkpoint == current_checkpoint)
1336 .map(|entry| entry.removed_value)
1337 }
1338
1339 pub(crate) fn get_holes(&self) -> impl Iterator<Item = i32> + '_ {
1342 self.holes.keys().copied()
1343 }
1344}
1345
1346#[derive(Debug)]
1347pub(crate) struct IntegerDomainIterator<'a> {
1348 domain: &'a IntegerDomain,
1349 current_value: i32,
1350}
1351
1352impl IntegerDomainIterator<'_> {
1353 fn new(domain: &IntegerDomain) -> IntegerDomainIterator<'_> {
1354 IntegerDomainIterator {
1355 domain,
1356 current_value: domain.lower_bound(),
1357 }
1358 }
1359}
1360
1361impl Iterator for IntegerDomainIterator<'_> {
1362 type Item = i32;
1363 fn next(&mut self) -> Option<i32> {
1364 if self.domain.verify_consistency().is_err() {
1367 return None;
1368 }
1369
1370 let result = if self.current_value <= self.domain.upper_bound() {
1375 Some(self.current_value)
1376 } else {
1377 None
1378 };
1379
1380 self.current_value += 1;
1381 while self.current_value <= self.domain.upper_bound()
1384 && !self.domain.contains(self.current_value)
1385 {
1386 self.current_value += 1;
1387 }
1388 result
1389 }
1390}
1391
1392#[cfg(test)]
1393mod tests {
1394 use super::*;
1395 use crate::engine::notifications::DomainEvent;
1396
1397 #[test]
1398 fn jump_in_bound_change_lower_and_upper_bound_event_backtrack() {
1399 let mut notification_engine = NotificationEngine::test_default();
1400 let mut assignment = Assignments::default();
1401 let d1 = assignment.grow(1, 5);
1402 notification_engine.grow();
1403
1404 assignment.new_checkpoint();
1405
1406 let _ = assignment
1407 .post_predicate(predicate!(d1 != 1), None, &mut notification_engine)
1408 .expect("non-empty domain");
1409 let _ = assignment
1410 .post_predicate(predicate!(d1 != 5), None, &mut notification_engine)
1411 .expect("non-empty domain");
1412
1413 let _ = assignment.synchronise(0, &mut notification_engine);
1414
1415 let events = notification_engine
1416 .drain_backtrack_domain_events()
1417 .collect::<Vec<_>>();
1418 assert_eq!(events.len(), 3);
1419
1420 assert_contains_events(&events, d1, [DomainEvent::LowerBound]);
1421 assert_contains_events(&events, d1, [DomainEvent::UpperBound]);
1422 assert_contains_events(&events, d1, [DomainEvent::Removal]);
1423 }
1424
1425 #[test]
1426 fn jump_in_bound_change_assign_event_backtrack() {
1427 let mut notification_engine = NotificationEngine::test_default();
1428 let mut assignment = Assignments::default();
1429 let d1 = assignment.grow(1, 5);
1430 notification_engine.grow();
1431
1432 assignment.new_checkpoint();
1433
1434 let _ = assignment
1435 .post_predicate(predicate!(d1 != 2), None, &mut notification_engine)
1436 .expect("non-empty domain");
1437 let _ = assignment
1438 .post_predicate(predicate!(d1 != 3), None, &mut notification_engine)
1439 .expect("non-empty domain");
1440 let _ = assignment
1441 .post_predicate(predicate!(d1 != 4), None, &mut notification_engine)
1442 .expect("non-empty domain");
1443 let _ = assignment
1444 .post_predicate(predicate!(d1 != 5), None, &mut notification_engine)
1445 .expect("non-empty domain");
1446 let _ = assignment
1447 .post_predicate(predicate!(d1 != 1), None, &mut notification_engine)
1448 .expect_err("empty domain");
1449
1450 let _ = assignment.synchronise(0, &mut notification_engine);
1451
1452 let events = notification_engine
1453 .drain_backtrack_domain_events()
1454 .collect::<Vec<_>>();
1455 assert_eq!(events.len(), 4);
1456
1457 assert_contains_events(&events, d1, [DomainEvent::LowerBound]);
1458 assert_contains_events(&events, d1, [DomainEvent::UpperBound]);
1459 assert_contains_events(&events, d1, [DomainEvent::Removal]);
1460 assert_contains_events(&events, d1, [DomainEvent::Assign]);
1461 }
1462
1463 #[test]
1464 fn jump_in_bound_change_upper_bound_event_backtrack() {
1465 let mut notification_engine = NotificationEngine::test_default();
1466 let mut assignment = Assignments::default();
1467 let d1 = assignment.grow(1, 5);
1468 notification_engine.grow();
1469
1470 assignment.new_checkpoint();
1471
1472 let _ = assignment
1473 .post_predicate(predicate!(d1 != 3), None, &mut notification_engine)
1474 .expect("non-empty domain");
1475 let _ = assignment
1476 .post_predicate(predicate!(d1 != 4), None, &mut notification_engine)
1477 .expect("non-empty domain");
1478 let _ = assignment
1479 .post_predicate(predicate!(d1 != 5), None, &mut notification_engine)
1480 .expect("non-empty domain");
1481
1482 let _ = assignment.synchronise(0, &mut notification_engine);
1483
1484 let events = notification_engine
1485 .drain_backtrack_domain_events()
1486 .collect::<Vec<_>>();
1487 assert_eq!(events.len(), 2);
1488
1489 assert_contains_events(&events, d1, [DomainEvent::UpperBound]);
1490 assert_contains_events(&events, d1, [DomainEvent::Removal]);
1491 }
1492
1493 #[test]
1494 fn jump_in_bound_change_lower_bound_event_backtrack() {
1495 let mut notification_engine = NotificationEngine::test_default();
1496 let mut assignment = Assignments::default();
1497 let d1 = assignment.grow(1, 5);
1498 notification_engine.grow();
1499
1500 assignment.new_checkpoint();
1501
1502 let _ = assignment
1503 .remove_value_from_domain(d1, 3, None)
1504 .expect("non-empty domain");
1505 let _ = assignment
1506 .remove_value_from_domain(d1, 2, None)
1507 .expect("non-empty domain");
1508 let _ = assignment
1509 .remove_value_from_domain(d1, 1, None)
1510 .expect("non-empty domain");
1511
1512 let _ = assignment.synchronise(0, &mut notification_engine);
1513
1514 let events = notification_engine
1515 .drain_backtrack_domain_events()
1516 .collect::<Vec<_>>();
1517 assert_eq!(events.len(), 2);
1518
1519 assert_contains_events(&events, d1, [DomainEvent::LowerBound]);
1520 assert_contains_events(&events, d1, [DomainEvent::Removal]);
1521 }
1522
1523 #[test]
1524 fn lower_bound_change_lower_bound_event() {
1525 let mut notification_engine = NotificationEngine::default();
1526 let mut assignment = Assignments::default();
1527 let d1 = assignment.grow(1, 5);
1528 notification_engine.grow();
1529
1530 let _ = assignment
1531 .post_predicate(predicate!(d1 >= 2), None, &mut notification_engine)
1532 .expect("non-empty domain");
1533
1534 let events = notification_engine
1535 .drain_domain_events()
1536 .collect::<Vec<_>>();
1537 assert_eq!(events.len(), 1);
1538
1539 assert_contains_events(&events, d1, [DomainEvent::LowerBound]);
1540 }
1541
1542 #[test]
1543 fn upper_bound_change_triggers_upper_bound_event() {
1544 let mut notification_engine = NotificationEngine::default();
1545 let mut assignment = Assignments::default();
1546 let d1 = assignment.grow(1, 5);
1547 notification_engine.grow();
1548
1549 let _ = assignment
1550 .post_predicate(predicate!(d1 <= 2), None, &mut notification_engine)
1551 .expect("non-empty domain");
1552
1553 let events = notification_engine
1554 .drain_domain_events()
1555 .collect::<Vec<_>>();
1556 assert_eq!(events.len(), 1);
1557 assert_contains_events(&events, d1, [DomainEvent::UpperBound]);
1558 }
1559
1560 #[test]
1561 fn bounds_change_can_also_trigger_assign_event() {
1562 let mut notification_engine = NotificationEngine::default();
1563 let mut assignment = Assignments::default();
1564
1565 let d1 = assignment.grow(1, 5);
1566 let d2 = assignment.grow(1, 5);
1567 notification_engine.grow();
1568 notification_engine.grow();
1569
1570 let _ = assignment
1571 .post_predicate(predicate!(d1 >= 5), None, &mut notification_engine)
1572 .expect("non-empty domain");
1573 let _ = assignment
1574 .post_predicate(predicate!(d2 <= 1), None, &mut notification_engine)
1575 .expect("non-empty domain");
1576
1577 let events = notification_engine
1578 .drain_domain_events()
1579 .collect::<Vec<_>>();
1580 assert_eq!(events.len(), 4, "expected more than 4 events: {events:?}");
1581
1582 assert_contains_events(&events, d1, [DomainEvent::LowerBound, DomainEvent::Assign]);
1583 assert_contains_events(&events, d2, [DomainEvent::UpperBound, DomainEvent::Assign]);
1584 }
1585
1586 #[test]
1587 fn making_assignment_triggers_appropriate_events() {
1588 let mut notification_engine = NotificationEngine::default();
1589 let mut assignment = Assignments::default();
1590
1591 let d1 = assignment.grow(1, 5);
1592 let d2 = assignment.grow(1, 5);
1593 let d3 = assignment.grow(1, 5);
1594 notification_engine.grow();
1595 notification_engine.grow();
1596 notification_engine.grow();
1597
1598 let _ = assignment
1599 .post_predicate(predicate!(d1 == 1), None, &mut notification_engine)
1600 .expect("non-empty domain");
1601 let _ = assignment
1602 .post_predicate(predicate!(d2 == 5), None, &mut notification_engine)
1603 .expect("non-empty domain");
1604 let _ = assignment
1605 .post_predicate(predicate!(d3 == 3), None, &mut notification_engine)
1606 .expect("non-empty domain");
1607
1608 let events = notification_engine
1609 .drain_domain_events()
1610 .collect::<Vec<_>>();
1611 assert_eq!(events.len(), 7);
1612
1613 assert_contains_events(&events, d1, [DomainEvent::Assign, DomainEvent::UpperBound]);
1614 assert_contains_events(&events, d2, [DomainEvent::Assign, DomainEvent::LowerBound]);
1615 assert_contains_events(
1616 &events,
1617 d3,
1618 [
1619 DomainEvent::Assign,
1620 DomainEvent::LowerBound,
1621 DomainEvent::UpperBound,
1622 ],
1623 );
1624 }
1625
1626 #[test]
1627 fn removal_triggers_removal_event() {
1628 let mut notification_engine = NotificationEngine::default();
1629 let mut assignment = Assignments::default();
1630 let d1 = assignment.grow(1, 5);
1631 notification_engine.grow();
1632
1633 let _ = assignment
1634 .post_predicate(predicate!(d1 != 2), None, &mut notification_engine)
1635 .expect("non-empty domain");
1636
1637 let events = notification_engine
1638 .drain_domain_events()
1639 .collect::<Vec<_>>();
1640 assert_eq!(events.len(), 1);
1641 assert!(events.contains(&(DomainEvent::Removal, d1)));
1642 }
1643
1644 #[test]
1645 fn value_can_be_removed_from_domains() {
1646 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1647 let _ = domain.remove_value(1, 1, 2);
1648
1649 assert!(domain.contains(2));
1650 assert!(!domain.contains(1));
1651 }
1652
1653 #[test]
1654 fn removing_the_lower_bound_updates_that_lower_bound() {
1655 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1656 let _ = domain.remove_value(1, 1, 1);
1657 let _ = domain.remove_value(2, 1, 2);
1658
1659 assert_eq!(3, domain.lower_bound());
1660 }
1661
1662 #[test]
1663 fn removing_the_upper_bound_updates_the_upper_bound() {
1664 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1665 let _ = domain.remove_value(4, 0, 1);
1666 let _ = domain.remove_value(5, 0, 2);
1667
1668 assert_eq!(3, domain.upper_bound());
1669 }
1670
1671 #[test]
1672 fn an_empty_domain_accepts_removal_operations() {
1673 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1674 let _ = domain.remove_value(4, 0, 1);
1675 let _ = domain.remove_value(1, 0, 2);
1676 let _ = domain.remove_value(1, 0, 3);
1677 }
1678
1679 #[test]
1680 fn setting_lower_bound_rounds_up_to_nearest_value_in_domain() {
1681 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1682 let _ = domain.remove_value(2, 1, 2);
1683 let _ = domain.remove_value(3, 1, 3);
1684 let _ = domain.set_lower_bound(2, 1, 4);
1685
1686 assert_eq!(4, domain.lower_bound());
1687 }
1688
1689 #[test]
1690 fn setting_upper_bound_rounds_down_to_nearest_value_in_domain() {
1691 let mut domain = IntegerDomain::new(1, 0, 5, 1, DomainId::new(0));
1692 let _ = domain.remove_value(4, 0, 1);
1693 let _ = domain.set_upper_bound(4, 0, 2);
1694
1695 assert_eq!(3, domain.upper_bound());
1696 }
1697
1698 #[test]
1699 fn undo_removal_at_bounds_indexes_into_values_domain_correctly() {
1700 let mut notification_engine = NotificationEngine::default();
1701 let mut assignment = Assignments::default();
1702 let d1 = assignment.grow(1, 5);
1703 notification_engine.grow();
1704
1705 assignment.new_checkpoint();
1706
1707 let _ = assignment
1708 .post_predicate(predicate!(d1 != 5), None, &mut notification_engine)
1709 .expect("non-empty domain");
1710
1711 let _ = assignment.synchronise(0, &mut notification_engine);
1712
1713 assert_eq!(5, assignment.get_upper_bound(d1));
1714 }
1715
1716 fn assert_contains_events(
1717 slice: &[(DomainEvent, DomainId)],
1718 domain: DomainId,
1719 required_events: impl IntoIterator<Item = DomainEvent>,
1720 ) {
1721 for event in required_events {
1722 assert!(slice.contains(&(event, domain)));
1723 }
1724 }
1725
1726 fn get_domain1() -> (DomainId, IntegerDomain) {
1727 let domain_id = DomainId::new(0);
1728 let mut domain = IntegerDomain::new(0, 0, 100, 1, domain_id);
1729 let _ = domain.set_lower_bound(1, 0, 1);
1730 let _ = domain.set_lower_bound(5, 1, 2);
1731 let _ = domain.set_lower_bound(10, 2, 10);
1732 let _ = domain.set_lower_bound(20, 5, 50);
1733 let _ = domain.set_lower_bound(50, 10, 70);
1734
1735 (domain_id, domain)
1736 }
1737
1738 #[test]
1739 fn lower_bound_trail_position_inbetween_value() {
1740 let (domain_id, domain) = get_domain1();
1741
1742 assert_eq!(
1743 domain
1744 .get_update_info(&predicate!(domain_id >= 12))
1745 .unwrap()
1746 .trail_position,
1747 50
1748 );
1749 }
1750
1751 #[test]
1752 fn lower_bound_trail_position_last_bound() {
1753 let (domain_id, domain) = get_domain1();
1754
1755 assert_eq!(
1756 domain
1757 .get_update_info(&predicate!(domain_id >= 50))
1758 .unwrap()
1759 .trail_position,
1760 70
1761 );
1762 }
1763
1764 #[test]
1765 fn lower_bound_trail_position_beyond_value() {
1766 let (domain_id, domain) = get_domain1();
1767
1768 assert!(
1769 domain
1770 .get_update_info(&predicate!(domain_id >= 101))
1771 .is_none()
1772 );
1773 }
1774
1775 #[test]
1776 fn lower_bound_trail_position_trivial() {
1777 let (domain_id, domain) = get_domain1();
1778
1779 assert_eq!(
1780 domain
1781 .get_update_info(&predicate!(domain_id >= -10))
1782 .unwrap()
1783 .trail_position,
1784 0
1785 );
1786 }
1787
1788 #[test]
1789 fn lower_bound_trail_position_with_removals() {
1790 let (domain_id, mut domain) = get_domain1();
1791 let _ = domain.remove_value(50, 11, 75);
1792 let _ = domain.remove_value(51, 11, 77);
1793 let _ = domain.remove_value(52, 11, 80);
1794
1795 assert_eq!(
1796 domain
1797 .get_update_info(&predicate!(domain_id >= 52))
1798 .unwrap()
1799 .trail_position,
1800 77
1801 );
1802 }
1803
1804 #[test]
1805 fn removal_trail_position() {
1806 let (domain_id, mut domain) = get_domain1();
1807 let _ = domain.remove_value(50, 11, 75);
1808 let _ = domain.remove_value(51, 11, 77);
1809 let _ = domain.remove_value(52, 11, 80);
1810
1811 assert_eq!(
1812 domain
1813 .get_update_info(&predicate!(domain_id != 50))
1814 .unwrap()
1815 .trail_position,
1816 75
1817 );
1818 }
1819
1820 #[test]
1821 fn removal_trail_position_after_lower_bound() {
1822 let (domain_id, mut domain) = get_domain1();
1823 let _ = domain.remove_value(50, 11, 75);
1824 let _ = domain.remove_value(51, 11, 77);
1825 let _ = domain.remove_value(52, 11, 80);
1826 let _ = domain.set_lower_bound(60, 11, 150);
1827
1828 assert_eq!(
1829 domain
1830 .get_update_info(&predicate!(domain_id != 55))
1831 .unwrap()
1832 .trail_position,
1833 150
1834 );
1835 }
1836
1837 #[test]
1838 fn lower_bound_change_backtrack() {
1839 let mut notification_engine = NotificationEngine::default();
1840 let mut assignment = Assignments::default();
1841 let domain_id1 = assignment.grow(0, 100);
1842 let domain_id2 = assignment.grow(0, 50);
1843 notification_engine.grow();
1844 notification_engine.grow();
1845
1846 assignment.new_checkpoint();
1848 let _ = assignment
1849 .post_predicate(predicate!(domain_id1 >= 2), None, &mut notification_engine)
1850 .expect("");
1851 let _ = assignment
1852 .post_predicate(predicate!(domain_id2 >= 25), None, &mut notification_engine)
1853 .expect("");
1854
1855 assignment.new_checkpoint();
1857 let _ = assignment
1858 .post_predicate(predicate!(domain_id1 >= 5), None, &mut notification_engine)
1859 .expect("");
1860
1861 assignment.new_checkpoint();
1863 let _ = assignment
1864 .post_predicate(predicate!(domain_id1 >= 7), None, &mut notification_engine)
1865 .expect("");
1866
1867 assert_eq!(assignment.get_lower_bound(domain_id1), 7);
1868
1869 let _ = assignment.synchronise(1, &mut notification_engine);
1870
1871 assert_eq!(assignment.get_lower_bound(domain_id1), 2);
1872 }
1873
1874 #[test]
1875 fn lower_bound_inbetween_updates() {
1876 let (_, domain) = get_domain1();
1877 assert_eq!(domain.lower_bound_at_trail_position(25), 10);
1878 }
1879
1880 #[test]
1881 fn lower_bound_beyond_trail_position() {
1882 let (_, domain) = get_domain1();
1883 assert_eq!(domain.lower_bound_at_trail_position(1000), 50);
1884 }
1885
1886 #[test]
1887 fn lower_bound_at_update() {
1888 let (_, domain) = get_domain1();
1889 assert_eq!(domain.lower_bound_at_trail_position(50), 20);
1890 }
1891
1892 #[test]
1893 fn lower_bound_at_trail_position_after_removals() {
1894 let (_, mut domain) = get_domain1();
1895 let _ = domain.remove_value(50, 11, 75);
1896 let _ = domain.remove_value(51, 11, 77);
1897 let _ = domain.remove_value(52, 11, 80);
1898
1899 assert_eq!(domain.lower_bound_at_trail_position(77), 52);
1900 }
1901
1902 #[test]
1903 fn lower_bound_at_trail_position_after_removals_and_bound_update() {
1904 let (_, mut domain) = get_domain1();
1905 let _ = domain.remove_value(50, 11, 75);
1906 let _ = domain.remove_value(51, 11, 77);
1907 let _ = domain.remove_value(52, 11, 80);
1908 let _ = domain.set_lower_bound(60, 11, 150);
1909
1910 assert_eq!(domain.lower_bound_at_trail_position(100), 53);
1911 }
1912
1913 #[test]
1914 fn inconsistent_bound_updates() {
1915 let domain_id = DomainId::new(0);
1916 let mut domain = IntegerDomain::new(0, 0, 2, 1, domain_id);
1917 let _ = domain.set_lower_bound(2, 1, 1);
1918 let _ = domain.set_upper_bound(1, 1, 2);
1919 assert!(domain.verify_consistency().is_err());
1920 }
1921
1922 #[test]
1923 fn inconsistent_domain_removals() {
1924 let domain_id = DomainId::new(0);
1925 let mut domain = IntegerDomain::new(0, 0, 2, 1, domain_id);
1926 let _ = domain.remove_value(1, 1, 1);
1927 let _ = domain.remove_value(2, 1, 2);
1928 let _ = domain.remove_value(0, 1, 3);
1929 assert!(domain.verify_consistency().is_err());
1930 }
1931
1932 #[test]
1933 fn domain_iterator_simple() {
1934 let domain_id = DomainId::new(0);
1935 let domain = IntegerDomain::new(0, 0, 5, 1, domain_id);
1936 let mut iter = domain.domain_iterator();
1937 assert_eq!(iter.next(), Some(0));
1938 assert_eq!(iter.next(), Some(1));
1939 assert_eq!(iter.next(), Some(2));
1940 assert_eq!(iter.next(), Some(3));
1941 assert_eq!(iter.next(), Some(4));
1942 assert_eq!(iter.next(), Some(5));
1943 assert_eq!(iter.next(), None);
1944 }
1945
1946 #[test]
1947 fn domain_iterator_skip_holes() {
1948 let domain_id = DomainId::new(0);
1949 let mut domain = IntegerDomain::new(0, 0, 5, 1, domain_id);
1950 let _ = domain.remove_value(1, 0, 5);
1951 let _ = domain.remove_value(4, 0, 10);
1952
1953 let mut iter = domain.domain_iterator();
1954 assert_eq!(iter.next(), Some(0));
1955 assert_eq!(iter.next(), Some(2));
1956 assert_eq!(iter.next(), Some(3));
1957 assert_eq!(iter.next(), Some(5));
1958 assert_eq!(iter.next(), None);
1959 }
1960
1961 #[test]
1962 fn domain_iterator_removed_bounds() {
1963 let domain_id = DomainId::new(0);
1964 let mut domain = IntegerDomain::new(0, 0, 5, 1, domain_id);
1965 let _ = domain.remove_value(0, 0, 1);
1966 let _ = domain.remove_value(5, 0, 10);
1967
1968 let mut iter = domain.domain_iterator();
1969 assert_eq!(iter.next(), Some(1));
1970 assert_eq!(iter.next(), Some(2));
1971 assert_eq!(iter.next(), Some(3));
1972 assert_eq!(iter.next(), Some(4));
1973 assert_eq!(iter.next(), None);
1974 }
1975
1976 #[test]
1977 fn domain_iterator_removed_values_present_beyond_bounds() {
1978 let domain_id = DomainId::new(0);
1979 let mut domain = IntegerDomain::new(0, 0, 10, 1, domain_id);
1980 let _ = domain.remove_value(7, 0, 1);
1981 let _ = domain.remove_value(9, 0, 5);
1982 let _ = domain.remove_value(2, 0, 10);
1983 let _ = domain.set_upper_bound(6, 1, 10);
1984
1985 let mut iter = domain.domain_iterator();
1986 assert_eq!(iter.next(), Some(0));
1987 assert_eq!(iter.next(), Some(1));
1988 assert_eq!(iter.next(), Some(3));
1989 assert_eq!(iter.next(), Some(4));
1990 assert_eq!(iter.next(), Some(5));
1991 assert_eq!(iter.next(), Some(6));
1992 assert_eq!(iter.next(), None);
1993 }
1994
1995 #[test]
1996 fn various_tests_evaluate_predicate() {
1997 let mut notification_engine = NotificationEngine::default();
1998 let mut assignments = Assignments::default();
1999 let domain_id = assignments.grow(0, 10);
2001 notification_engine.grow();
2002
2003 let _ =
2004 assignments.post_predicate(predicate!(domain_id != 7), None, &mut notification_engine);
2005 let _ =
2006 assignments.post_predicate(predicate!(domain_id != 9), None, &mut notification_engine);
2007 let _ =
2008 assignments.post_predicate(predicate!(domain_id != 2), None, &mut notification_engine);
2009 let _ =
2010 assignments.post_predicate(predicate!(domain_id <= 6), None, &mut notification_engine);
2011
2012 let lb_predicate = |lower_bound: i32| -> Predicate { predicate!(domain_id >= lower_bound) };
2013 let ub_predicate = |upper_bound: i32| -> Predicate { predicate!(domain_id <= upper_bound) };
2014 let eq_predicate =
2015 |equality_constant: i32| -> Predicate { predicate!(domain_id == equality_constant) };
2016 let neq_predicate =
2017 |not_equal_constant: i32| -> Predicate { predicate!(domain_id != not_equal_constant) };
2018
2019 assert!(
2020 assignments
2021 .evaluate_predicate(lb_predicate(0))
2022 .is_some_and(|x| x)
2023 );
2024 assert!(assignments.evaluate_predicate(lb_predicate(1)).is_none());
2025 assert!(assignments.evaluate_predicate(lb_predicate(2)).is_none());
2026 assert!(assignments.evaluate_predicate(lb_predicate(3)).is_none());
2027 assert!(assignments.evaluate_predicate(lb_predicate(4)).is_none());
2028 assert!(assignments.evaluate_predicate(lb_predicate(5)).is_none());
2029 assert!(assignments.evaluate_predicate(lb_predicate(6)).is_none());
2030 assert!(
2031 assignments
2032 .evaluate_predicate(lb_predicate(7))
2033 .is_some_and(|x| !x)
2034 );
2035 assert!(
2036 assignments
2037 .evaluate_predicate(lb_predicate(8))
2038 .is_some_and(|x| !x)
2039 );
2040 assert!(
2041 assignments
2042 .evaluate_predicate(lb_predicate(9))
2043 .is_some_and(|x| !x)
2044 );
2045 assert!(
2046 assignments
2047 .evaluate_predicate(lb_predicate(10))
2048 .is_some_and(|x| !x)
2049 );
2050
2051 assert!(assignments.evaluate_predicate(ub_predicate(0)).is_none());
2052 assert!(assignments.evaluate_predicate(ub_predicate(1)).is_none());
2053 assert!(assignments.evaluate_predicate(ub_predicate(2)).is_none());
2054 assert!(assignments.evaluate_predicate(ub_predicate(3)).is_none());
2055 assert!(assignments.evaluate_predicate(ub_predicate(4)).is_none());
2056 assert!(assignments.evaluate_predicate(ub_predicate(5)).is_none());
2057 assert!(
2058 assignments
2059 .evaluate_predicate(ub_predicate(6))
2060 .is_some_and(|x| x)
2061 );
2062 assert!(
2063 assignments
2064 .evaluate_predicate(ub_predicate(7))
2065 .is_some_and(|x| x)
2066 );
2067 assert!(
2068 assignments
2069 .evaluate_predicate(ub_predicate(8))
2070 .is_some_and(|x| x)
2071 );
2072 assert!(
2073 assignments
2074 .evaluate_predicate(ub_predicate(9))
2075 .is_some_and(|x| x)
2076 );
2077 assert!(
2078 assignments
2079 .evaluate_predicate(ub_predicate(10))
2080 .is_some_and(|x| x)
2081 );
2082
2083 assert!(assignments.evaluate_predicate(neq_predicate(0)).is_none());
2084 assert!(assignments.evaluate_predicate(neq_predicate(1)).is_none());
2085 assert!(
2086 assignments
2087 .evaluate_predicate(neq_predicate(2))
2088 .is_some_and(|x| x)
2089 );
2090 assert!(assignments.evaluate_predicate(neq_predicate(3)).is_none());
2091 assert!(assignments.evaluate_predicate(neq_predicate(4)).is_none());
2092 assert!(assignments.evaluate_predicate(neq_predicate(5)).is_none());
2093 assert!(assignments.evaluate_predicate(neq_predicate(6)).is_none());
2094 assert!(
2095 assignments
2096 .evaluate_predicate(neq_predicate(7))
2097 .is_some_and(|x| x)
2098 );
2099 assert!(
2100 assignments
2101 .evaluate_predicate(neq_predicate(8))
2102 .is_some_and(|x| x)
2103 );
2104 assert!(
2105 assignments
2106 .evaluate_predicate(neq_predicate(9))
2107 .is_some_and(|x| x)
2108 );
2109 assert!(
2110 assignments
2111 .evaluate_predicate(neq_predicate(10))
2112 .is_some_and(|x| x)
2113 );
2114
2115 assert!(assignments.evaluate_predicate(eq_predicate(0)).is_none());
2116 assert!(assignments.evaluate_predicate(eq_predicate(1)).is_none());
2117 assert!(
2118 assignments
2119 .evaluate_predicate(eq_predicate(2))
2120 .is_some_and(|x| !x)
2121 );
2122 assert!(assignments.evaluate_predicate(eq_predicate(3)).is_none());
2123 assert!(assignments.evaluate_predicate(eq_predicate(4)).is_none());
2124 assert!(assignments.evaluate_predicate(eq_predicate(5)).is_none());
2125 assert!(assignments.evaluate_predicate(eq_predicate(6)).is_none());
2126 assert!(
2127 assignments
2128 .evaluate_predicate(eq_predicate(7))
2129 .is_some_and(|x| !x)
2130 );
2131 assert!(
2132 assignments
2133 .evaluate_predicate(eq_predicate(8))
2134 .is_some_and(|x| !x)
2135 );
2136 assert!(
2137 assignments
2138 .evaluate_predicate(eq_predicate(9))
2139 .is_some_and(|x| !x)
2140 );
2141 assert!(
2142 assignments
2143 .evaluate_predicate(eq_predicate(10))
2144 .is_some_and(|x| !x)
2145 );
2146
2147 let _ =
2148 assignments.post_predicate(predicate!(domain_id >= 6), None, &mut notification_engine);
2149
2150 assert!(
2151 assignments
2152 .evaluate_predicate(neq_predicate(6))
2153 .is_some_and(|x| !x)
2154 );
2155 assert!(
2156 assignments
2157 .evaluate_predicate(eq_predicate(6))
2158 .is_some_and(|x| x)
2159 );
2160 assert!(
2161 assignments
2162 .evaluate_predicate(lb_predicate(6))
2163 .is_some_and(|x| x)
2164 );
2165 assert!(
2166 assignments
2167 .evaluate_predicate(ub_predicate(6))
2168 .is_some_and(|x| x)
2169 );
2170 }
2171}