Skip to content

Commit c241d36

Browse files
committed
Preparation for Set constraints
1 parent 458c682 commit c241d36

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

src/variables/domain/sparse_set.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,21 @@ impl SparseSet {
318318
}
319319

320320
/// Set intersection - modify this set to contain only elements in both sets
321+
///
322+
/// **Note**: This operates on the **domain** of integer variables, not on set-valued variables.
323+
/// In Selen, `SparseSet` is used as the domain representation for integer variables (including
324+
/// those created with `intset()`). These operations are useful for constraint propagation and
325+
/// domain manipulation, but they do not implement FlatZinc set constraints like `set_union(x,y,z)`
326+
/// which require true set-valued variables where the variable's value is itself a set.
327+
///
328+
/// # Examples
329+
/// ```
330+
/// use selen::variables::domain::sparse_set::SparseSet;
331+
/// let mut domain1 = SparseSet::new(1, 5); // Domain {1,2,3,4,5}
332+
/// let mut domain2 = SparseSet::new(3, 7); // Domain {3,4,5,6,7}
333+
/// domain2.remove(6); // Domain {3,4,5,7}
334+
/// domain1.intersect_with(&domain2); // domain1 becomes {3,4,5}
335+
/// ```
321336
pub fn intersect_with(&mut self, other: &SparseSet) {
322337
// Create a list of values to remove to avoid modifying while iterating
323338
let mut to_remove = Vec::with_capacity(self.size as usize);
@@ -334,6 +349,10 @@ impl SparseSet {
334349
}
335350

336351
/// Set union - add all elements from other set to this set
352+
///
353+
/// **Note**: This operates on the **domain** of integer variables, not on set-valued variables.
354+
/// See `intersect_with()` for more details on the distinction.
355+
///
337356
/// Note: This requires that both sets have compatible universes
338357
pub fn union_with(&mut self, other: &SparseSet) {
339358
for val in other.iter() {
@@ -371,6 +390,35 @@ impl SparseSet {
371390
}
372391
}
373392

393+
/// Set difference - remove all elements from this set that are in other set
394+
///
395+
/// This computes `self = self \ other` (set difference).
396+
///
397+
/// **Note**: This operates on the **domain** of integer variables, not on set-valued variables.
398+
/// See `intersect_with()` for more details on the distinction.
399+
///
400+
/// # Examples
401+
/// ```
402+
/// use selen::variables::domain::sparse_set::SparseSet;
403+
/// let mut a = SparseSet::new(1, 5); // Domain {1, 2, 3, 4, 5}
404+
/// let b = SparseSet::new(3, 7); // Domain {3, 4, 5, 6, 7}
405+
/// a.diff_with(&b); // a becomes {1, 2}
406+
/// ```
407+
pub fn diff_with(&mut self, other: &SparseSet) {
408+
// Create a list of values to remove to avoid modifying while iterating
409+
let mut to_remove = Vec::with_capacity(self.size as usize);
410+
411+
for val in self.iter() {
412+
if other.contains(val) {
413+
to_remove.push(val);
414+
}
415+
}
416+
417+
for val in to_remove {
418+
self.remove(val);
419+
}
420+
}
421+
374422
/// Check if this set is a subset of another set
375423
pub fn is_subset_of(&self, other: &Self) -> bool {
376424
// Adjust for different offsets
@@ -987,6 +1035,125 @@ mod test {
9871035
assert!(!v1.contains(5));
9881036
}
9891037

1038+
#[test]
1039+
fn test_diff_with_basic() {
1040+
let mut a = SparseSet::new(1, 5); // {1, 2, 3, 4, 5}
1041+
let b = SparseSet::new(3, 7); // {3, 4, 5, 6, 7}
1042+
1043+
a.diff_with(&b);
1044+
1045+
// a should now be {1, 2} (elements in a but not in b)
1046+
assert_eq!(a.size(), 2);
1047+
assert!(a.contains(1));
1048+
assert!(a.contains(2));
1049+
assert!(!a.contains(3));
1050+
assert!(!a.contains(4));
1051+
assert!(!a.contains(5));
1052+
}
1053+
1054+
#[test]
1055+
fn test_diff_with_disjoint() {
1056+
let mut a = SparseSet::new(1, 3); // {1, 2, 3}
1057+
let b = SparseSet::new(4, 6); // {4, 5, 6}
1058+
1059+
a.diff_with(&b);
1060+
1061+
// a should be unchanged (disjoint sets)
1062+
assert_eq!(a.size(), 3);
1063+
assert!(a.contains(1));
1064+
assert!(a.contains(2));
1065+
assert!(a.contains(3));
1066+
}
1067+
1068+
#[test]
1069+
fn test_diff_with_subset() {
1070+
let mut a = SparseSet::new(1, 5); // {1, 2, 3, 4, 5}
1071+
let mut b = SparseSet::new(1, 5); // {1, 2, 3, 4, 5}
1072+
1073+
// Make b a subset: {2, 4}
1074+
b.remove(1);
1075+
b.remove(3);
1076+
b.remove(5);
1077+
1078+
a.diff_with(&b);
1079+
1080+
// a should now be {1, 3, 5}
1081+
assert_eq!(a.size(), 3);
1082+
assert!(a.contains(1));
1083+
assert!(a.contains(3));
1084+
assert!(a.contains(5));
1085+
assert!(!a.contains(2));
1086+
assert!(!a.contains(4));
1087+
}
1088+
1089+
#[test]
1090+
fn test_diff_with_empty() {
1091+
let mut a = SparseSet::new(1, 3); // {1, 2, 3}
1092+
let mut b = SparseSet::new(1, 3); // {1, 2, 3}
1093+
b.remove_all(); // Make it empty
1094+
1095+
a.diff_with(&b);
1096+
1097+
// a should be unchanged
1098+
assert_eq!(a.size(), 3);
1099+
assert!(a.contains(1));
1100+
assert!(a.contains(2));
1101+
assert!(a.contains(3));
1102+
}
1103+
1104+
#[test]
1105+
fn test_diff_with_becomes_empty() {
1106+
let mut a = SparseSet::new(1, 3); // {1, 2, 3}
1107+
let b = SparseSet::new(1, 5); // {1, 2, 3, 4, 5} (superset)
1108+
1109+
a.diff_with(&b);
1110+
1111+
// a should now be empty
1112+
assert_eq!(a.size(), 0);
1113+
assert!(a.is_empty());
1114+
}
1115+
1116+
#[test]
1117+
fn test_diff_with_sparse_domains() {
1118+
let a_values = vec![1, 3, 5, 7, 9];
1119+
let b_values = vec![2, 3, 5, 8];
1120+
1121+
let mut a = SparseSet::new_from_values(a_values);
1122+
let b = SparseSet::new_from_values(b_values);
1123+
1124+
a.diff_with(&b);
1125+
1126+
// a should now be {1, 7, 9} (removed 3 and 5)
1127+
assert_eq!(a.size(), 3);
1128+
assert!(a.contains(1));
1129+
assert!(a.contains(7));
1130+
assert!(a.contains(9));
1131+
assert!(!a.contains(3));
1132+
assert!(!a.contains(5));
1133+
}
1134+
1135+
#[test]
1136+
fn test_set_operations_combination() {
1137+
// Test combining union, intersect, and diff
1138+
let mut a = SparseSet::new(1, 5); // {1, 2, 3, 4, 5}
1139+
let b = SparseSet::new(3, 7); // {3, 4, 5, 6, 7}
1140+
let c = SparseSet::new(4, 8); // {4, 5, 6, 7, 8}
1141+
1142+
// a ∩ b = {3, 4, 5}
1143+
a.intersect_with(&b);
1144+
assert_eq!(a.size(), 3);
1145+
assert!(a.contains(3));
1146+
assert!(a.contains(4));
1147+
assert!(a.contains(5));
1148+
1149+
// (a ∩ b) \ c = {3} (remove 4 and 5)
1150+
a.diff_with(&c);
1151+
assert_eq!(a.size(), 1);
1152+
assert!(a.contains(3));
1153+
assert!(!a.contains(4));
1154+
assert!(!a.contains(5));
1155+
}
1156+
9901157
#[test]
9911158
fn test_performance_large_domain() {
9921159
// Test with a larger domain to ensure operations remain efficient

0 commit comments

Comments
 (0)