Skip to content

Commit bb6f871

Browse files
committed
Add is_subset() method to IntSet.
1 parent e0487b9 commit bb6f871

3 files changed

Lines changed: 294 additions & 5 deletions

File tree

read-fonts/src/collections/int_set/bitpage.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,30 @@ impl BitPage {
5252

5353
/// Returns true if this page has any members in common with `other`.
5454
pub(crate) fn intersects_set(&self, other: &BitPage) -> bool {
55-
for (a, b) in self.storage.iter().zip(other.storage.iter()) {
56-
if (*a & *b) != 0 {
57-
return true;
58-
}
55+
self.storage
56+
.iter()
57+
.zip(other.storage.iter())
58+
.any(|(a, b)| (*a & *b) != 0)
59+
}
60+
61+
/// Returns true if this page is a subset of `other`.
62+
pub(crate) fn is_subset(&self, other: &BitPage) -> bool {
63+
if self.len() > other.len() {
64+
return false;
5965
}
60-
false
66+
self.storage
67+
.iter()
68+
.zip(other.storage.iter())
69+
.all(|(a, b)| (*a & *b) == *a)
70+
}
71+
72+
/// Returns the number of members present in both `self` and `other`.
73+
pub(crate) fn intersection_len(&self, other: &BitPage) -> u32 {
74+
self.storage
75+
.iter()
76+
.zip(other.storage.iter())
77+
.map(|(a, b)| (*a & *b).count_ones())
78+
.sum()
6179
}
6280

6381
// TODO(garretrieger): iterator that starts after some value (similar to next in hb).
@@ -862,4 +880,60 @@ mod test {
862880

863881
assert_intersects!(c, d, false);
864882
}
883+
884+
#[test]
885+
fn is_subset() {
886+
let a = BitPage::new_zeroes();
887+
let b = BitPage::from_iter([32, 400]);
888+
let c = BitPage::from_iter([32]);
889+
let d = BitPage::from_iter([32, 200, 400]);
890+
let e = BitPage::from_iter([32, 300]);
891+
let f = BitPage::from_iter([32, 200, 300]);
892+
893+
assert!(a.is_subset(&a));
894+
assert!(a.is_subset(&b));
895+
assert!(a.is_subset(&c));
896+
assert!(a.is_subset(&d));
897+
898+
// self subset check
899+
assert!(b.is_subset(&b));
900+
assert!(c.is_subset(&c));
901+
assert!(d.is_subset(&d));
902+
assert!(e.is_subset(&e));
903+
assert!(f.is_subset(&f));
904+
905+
// is subset where len a < len b
906+
assert!(c.is_subset(&b));
907+
assert!(c.is_subset(&d));
908+
assert!(b.is_subset(&d));
909+
910+
// Fails via len a > len b
911+
assert!(!b.is_subset(&c));
912+
assert!(!d.is_subset(&b));
913+
assert!(!d.is_subset(&c));
914+
assert!(!b.is_subset(&a));
915+
916+
// Fails via bitwise check (len a <= len b)
917+
assert!(!b.is_subset(&e));
918+
assert!(!e.is_subset(&b));
919+
assert!(!b.is_subset(&f));
920+
}
921+
922+
#[test]
923+
fn intersection_len() {
924+
let a = BitPage::new_zeroes();
925+
let b = BitPage::from_iter([32, 400]);
926+
let c = BitPage::from_iter([32, 200]);
927+
let d = BitPage::from_iter([32]);
928+
929+
assert_eq!(a.intersection_len(&b), 0);
930+
assert_eq!(b.intersection_len(&a), 0);
931+
932+
assert_eq!(b.intersection_len(&c), 1);
933+
assert_eq!(c.intersection_len(&b), 1);
934+
assert_eq!(b.intersection_len(&d), 1);
935+
assert_eq!(d.intersection_len(&b), 1);
936+
937+
assert_eq!(b.intersection_len(&b), 2);
938+
}
865939
}

read-fonts/src/collections/int_set/bitset.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,69 @@ impl U32Set {
269269
false
270270
}
271271

272+
/// Returns true if this set is a subset of `other`.
273+
pub fn is_subset(&self, other: &U32Set) -> bool {
274+
if self.len() > other.len() {
275+
return false;
276+
}
277+
let mut it_b = other.page_map.iter().peekable();
278+
279+
for a_info in self.page_map.iter() {
280+
let page_a = &self.pages[a_info.index as usize];
281+
if page_a.is_empty() {
282+
continue;
283+
}
284+
285+
while let Some(b_info) = it_b.peek() {
286+
if b_info.major_value < a_info.major_value {
287+
it_b.next();
288+
} else {
289+
break;
290+
}
291+
}
292+
293+
match it_b.peek() {
294+
Some(b_info) if b_info.major_value == a_info.major_value => {
295+
let page_b = &other.pages[b_info.index as usize];
296+
if !page_a.is_subset(page_b) {
297+
return false;
298+
}
299+
it_b.next();
300+
}
301+
_ => return false,
302+
}
303+
}
304+
305+
true
306+
}
307+
308+
/// Returns the number of members present in both `self` and `other`.
309+
pub fn intersection_len(&self, other: &U32Set) -> u64 {
310+
let mut it_a = self.page_map.iter().peekable();
311+
let mut it_b = other.page_map.iter().peekable();
312+
let mut count = 0u64;
313+
314+
while let (Some(a), Some(b)) = (it_a.peek(), it_b.peek()) {
315+
match a.major_value.cmp(&b.major_value) {
316+
Ordering::Equal => {
317+
count += self.pages[a.index as usize]
318+
.intersection_len(&other.pages[b.index as usize])
319+
as u64;
320+
it_a.next();
321+
it_b.next();
322+
}
323+
Ordering::Less => {
324+
it_a.next();
325+
}
326+
Ordering::Greater => {
327+
it_b.next();
328+
}
329+
}
330+
}
331+
332+
count
333+
}
334+
272335
pub const fn empty() -> U32Set {
273336
U32Set {
274337
pages: Vec::new(),
@@ -1530,4 +1593,59 @@ mod test {
15301593

15311594
assert_intersects!(a, b, true);
15321595
}
1596+
1597+
#[test]
1598+
fn is_subset() {
1599+
let empty = U32Set::empty();
1600+
let a = U32Set::from_iter([2, 4, 5, 2057, 7000]);
1601+
let b = U32Set::from_iter([2, 4, 5, 2057, 7000, 8000]);
1602+
let c = U32Set::from_iter([2, 4, 5]);
1603+
let d = U32Set::from_iter([2, 4, 6, 2057, 7000]); // same len as a, different element on page 0
1604+
let e = U32Set::from_iter([2, 4, 5, 3000, 7000]); // same len as a, different major page
1605+
1606+
assert!(empty.is_subset(&empty));
1607+
assert!(empty.is_subset(&a));
1608+
assert!(a.is_subset(&a));
1609+
assert!(a.is_subset(&b));
1610+
assert!(c.is_subset(&a));
1611+
assert!(c.is_subset(&b));
1612+
1613+
// Fails via length check:
1614+
assert!(!b.is_subset(&a));
1615+
assert!(!a.is_subset(&c));
1616+
assert!(!a.is_subset(&empty));
1617+
1618+
// Fails via bitwise check (same len):
1619+
assert!(!a.is_subset(&d));
1620+
assert!(!d.is_subset(&a));
1621+
1622+
// Fails via page matching check (different major page, same len):
1623+
assert!(!a.is_subset(&e));
1624+
assert!(!e.is_subset(&a));
1625+
1626+
// Test with empty pages present in self
1627+
let mut a_with_empty_page = U32Set::empty();
1628+
a_with_empty_page.insert(100);
1629+
a_with_empty_page.insert(2057);
1630+
a_with_empty_page.remove(100); // page for 100 remains in page_map but is empty
1631+
assert!(a_with_empty_page.is_subset(&b));
1632+
}
1633+
1634+
#[test]
1635+
fn intersection_len() {
1636+
let empty = U32Set::empty();
1637+
let a = U32Set::from_iter([2, 4, 5, 2057, 7000]);
1638+
let b = U32Set::from_iter([4, 5, 2057, 9000]);
1639+
let c = U32Set::from_iter([1, 3, 10000]);
1640+
1641+
assert_eq!(empty.intersection_len(&a), 0);
1642+
assert_eq!(a.intersection_len(&empty), 0);
1643+
assert_eq!(a.intersection_len(&a), 5);
1644+
1645+
assert_eq!(a.intersection_len(&b), 3);
1646+
assert_eq!(b.intersection_len(&a), 3);
1647+
1648+
assert_eq!(a.intersection_len(&c), 0);
1649+
assert_eq!(c.intersection_len(&a), 0);
1650+
}
15331651
}

read-fonts/src/collections/int_set/mod.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,26 @@ impl<T: Domain> IntSet<T> {
394394
false
395395
}
396396

397+
/// Returns `true` if this set is a subset of `other`.
398+
pub fn is_subset(&self, other: &IntSet<T>) -> bool {
399+
if self.len() > other.len() {
400+
return false;
401+
}
402+
403+
match (&self.0, &other.0) {
404+
(Membership::Inclusive(a), Membership::Inclusive(b)) => a.is_subset(b),
405+
(Membership::Inclusive(a), Membership::Exclusive(b)) => !a.intersects_set(b),
406+
(Membership::Exclusive(a), Membership::Inclusive(b)) => {
407+
// For this (A) to be a subset of other (B):
408+
// - All members of the domain T must be in either A, B, or both
409+
// - So we check that A U B = T, which we can do by checking |A U B| = |T|
410+
// - |A U B| is given by |A| + |B| - |A n B| (inclusion exclusion principle)
411+
a.len() + b.len() - a.intersection_len(b) == T::count()
412+
}
413+
(Membership::Exclusive(a), Membership::Exclusive(b)) => b.is_subset(a),
414+
}
415+
}
416+
397417
/// Returns first element in the set, if any. This element is always the minimum of all elements in the set.
398418
pub fn first(&self) -> Option<T> {
399419
return self.iter().next();
@@ -2521,6 +2541,83 @@ mod test {
25212541
assert_intersects!(e, empty, false);
25222542
}
25232543

2544+
#[test]
2545+
fn is_subset() {
2546+
let empty = IntSet::<u32>::empty();
2547+
let a = IntSet::from([1u32, 5, 6, 7, 8, 12]);
2548+
let b = IntSet::from([1u32, 5, 6, 7, 8, 12, 15]);
2549+
let c = IntSet::from([1u32, 5, 6, 7, 8, 13]);
2550+
2551+
// Inclusive - Inclusive
2552+
assert!(empty.is_subset(&empty));
2553+
assert!(empty.is_subset(&a));
2554+
assert!(!a.is_subset(&empty));
2555+
2556+
assert!(a.is_subset(&a));
2557+
2558+
assert!(a.is_subset(&b));
2559+
assert!(!b.is_subset(&a)); // Rejection via length check (7 > 6)
2560+
2561+
assert!(!a.is_subset(&c)); // Rejection via member check (6 <= 6)
2562+
assert!(!c.is_subset(&a));
2563+
2564+
// Inclusive - Exclusive
2565+
let mut all_but_13 = IntSet::<u32>::all();
2566+
all_but_13.remove(13);
2567+
2568+
let mut all_but_5 = IntSet::<u32>::all();
2569+
all_but_5.remove(5);
2570+
2571+
assert!(a.is_subset(&all_but_13)); // a doesn't contain 13
2572+
assert!(!a.is_subset(&all_but_5)); // a contains 5 which is excluded in excl_5
2573+
assert!(empty.is_subset(&all_but_13));
2574+
2575+
// Exclusive - Inclusive
2576+
let mut all_but_1 = IntSet::<u8>::all();
2577+
all_but_1.remove(1u8);
2578+
2579+
let mut all_but_1_2 = IntSet::<u8>::all();
2580+
all_but_1_2.remove(1u8);
2581+
all_but_1_2.remove(2u8);
2582+
2583+
let mut incl_all_but_1 = IntSet::<u8>::empty();
2584+
incl_all_but_1.insert_range(0u8..=255);
2585+
incl_all_but_1.remove(1u8);
2586+
2587+
let mut incl_all_but_3 = IntSet::<u8>::empty();
2588+
incl_all_but_3.insert_range(0u8..=255);
2589+
incl_all_but_3.remove(3u8);
2590+
2591+
let mut incl_all = IntSet::<u8>::empty();
2592+
incl_all.insert_range(0u8..=255);
2593+
2594+
assert!(all_but_1.is_subset(&incl_all));
2595+
assert!(all_but_1.is_subset(&incl_all_but_1));
2596+
assert!(all_but_1_2.is_subset(&incl_all_but_1));
2597+
assert!(!all_but_1.is_subset(&IntSet::<u8>::from([0u8, 2, 3]))); // Rejection via length check (255 > 3)
2598+
assert!(!all_but_1_2.is_subset(&incl_all_but_3)); // Rejection via member check (254 <= 255)
2599+
2600+
// Exclusive - Exclusive
2601+
let mut all_but_2_3 = IntSet::<u8>::all();
2602+
all_but_2_3.remove(2u8);
2603+
all_but_2_3.remove(3u8);
2604+
2605+
assert!(all_but_1_2.is_subset(&all_but_1));
2606+
assert!(!all_but_1.is_subset(&all_but_1_2)); // Rejection via length check (255 > 254)
2607+
assert!(!all_but_1_2.is_subset(&all_but_2_3)); // Rejection via member (254 <= 254)
2608+
assert!(IntSet::<u32>::all().is_subset(&IntSet::<u32>::all()));
2609+
2610+
// Discontinuous Domain
2611+
let even_empty = IntSet::<EvenInts>::empty();
2612+
let even_a = IntSet::from([EvenInts(2), EvenInts(4)]);
2613+
let even_b = IntSet::from([EvenInts(2), EvenInts(4), EvenInts(6)]);
2614+
let even_c = IntSet::from([EvenInts(2), EvenInts(6)]);
2615+
assert!(even_empty.is_subset(&even_a));
2616+
assert!(even_a.is_subset(&even_b));
2617+
assert!(!even_b.is_subset(&even_a)); // Rejection via length check (3 > 2)
2618+
assert!(!even_c.is_subset(&even_a)); // Rejection via member check (2 <= 2)
2619+
}
2620+
25242621
#[test]
25252622
fn intersects_range_discontinuous() {
25262623
let mut set = IntSet::<EvenInts>::empty();

0 commit comments

Comments
 (0)