@@ -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