feat(rangeset): return partial cover and uncovered elements for set cover. (#61)

* Return partial cover.

* Revert accidental change.

* Fix clippy.

* Return tuple instead of result.
This commit is contained in:
yuroitaki
2025-04-16 17:53:33 +08:00
committed by GitHub
parent c1db19e6cc
commit 753b4b026f

View File

@@ -4,17 +4,30 @@ use crate::{
/// Set cover methods.
pub trait Cover<Rhs> {
/// Returns the positions of the fewest sets from `others` which exactly cover `self`.
fn find_cover<'a>(&self, others: impl IntoIterator<Item = &'a Rhs>) -> Option<Vec<usize>>
/// Finds the positions of the fewest sets from `others` which exactly
/// cover `self`.
///
/// Returns a tuple containing:
/// * A vector of indices of the sets that cover `self` (empty if no coverage at all).
/// * Any uncovered elements (empty if complete coverage is achieved).
fn find_cover<'a>(&self, others: impl IntoIterator<Item = &'a Rhs>) -> (Vec<usize>, Rhs)
where
Rhs: 'a;
/// Returns the fewest sets from `others` which exactly cover `self`.
fn cover<'a>(&self, others: impl IntoIterator<Item = &'a Rhs>) -> Option<Vec<&'a Rhs>>
/// Finds the fewest sets from `others` which exactly cover `self`.
///
/// Returns a tuple containing:
/// * A vector of sets that cover `self` (empty if no coverage at all).
/// * Any uncovered elements (empty if complete coverage is achieved).
fn cover<'a>(&self, others: impl IntoIterator<Item = &'a Rhs>) -> (Vec<&'a Rhs>, Rhs)
where
Rhs: 'a;
/// Returns the fewest sets from `others` which exactly cover `self`.
/// Finds the fewest sets from `others` which exactly cover `self`.
///
/// Returns a tuple containing:
/// * A vector of items that cover `self` (empty if no coverage at all).
/// * Any uncovered elements (empty if complete coverage is achieved).
///
/// # Arguments
///
@@ -24,7 +37,7 @@ pub trait Cover<Rhs> {
&self,
others: impl IntoIterator<Item = &'a T>,
f: impl Fn(&T) -> &Rhs,
) -> Option<Vec<&'a T>>
) -> (Vec<&'a T>, Rhs)
where
T: 'a;
}
@@ -37,32 +50,38 @@ where
fn find_cover<'a>(
&self,
others: impl IntoIterator<Item = &'a RangeSet<T>>,
) -> Option<Vec<usize>>
) -> (Vec<usize>, RangeSet<T>)
where
RangeSet<T>: 'a,
{
cover(self, |set| set, others).map(|sets| sets.into_iter().map(|(pos, _)| pos).collect())
let CoverResult { covered, uncovered } = cover(self, |set| set, others);
(covered.into_iter().map(|(pos, _)| pos).collect(), uncovered)
}
fn cover<'a>(
&self,
others: impl IntoIterator<Item = &'a RangeSet<T>>,
) -> Option<Vec<&'a RangeSet<T>>>
) -> (Vec<&'a RangeSet<T>>, RangeSet<T>)
where
RangeSet<T>: 'a,
{
cover(self, |set| set, others).map(|sets| sets.into_iter().map(|(_, set)| set).collect())
let CoverResult { covered, uncovered } = cover(self, |set| set, others);
(covered.into_iter().map(|(_, set)| set).collect(), uncovered)
}
fn cover_by<'a, U>(
&self,
others: impl IntoIterator<Item = &'a U>,
f: impl Fn(&U) -> &RangeSet<T>,
) -> Option<Vec<&'a U>>
) -> (Vec<&'a U>, RangeSet<T>)
where
T: 'a,
{
cover(self, f, others).map(|sets| sets.into_iter().map(|(_, item)| item).collect())
let CoverResult { covered, uncovered } = cover(self, f, others);
(
covered.into_iter().map(|(_, item)| item).collect(),
uncovered,
)
}
}
@@ -72,13 +91,28 @@ struct Candidate<'a, T> {
/// Position in the original collection.
pos: usize,
item: &'a T,
/// The number of elements in the intersection of the set and the uncovered elements.
/// The number of elements in the intersection of the set and the uncovered
/// elements.
cover: usize,
}
struct CoverResult<'a, T, U> {
covered: Vec<(usize, &'a U)>,
uncovered: RangeSet<T>,
}
impl<T: Copy + Ord, U> Default for CoverResult<'_, T, U> {
fn default() -> Self {
Self {
covered: Vec::default(),
uncovered: RangeSet::default(),
}
}
}
/// Greedy set cover algorithm.
///
/// Returns the fewest sets from `others` which exactly cover `query`.
/// Finds the fewest sets from `others` which exactly cover `query`.
///
/// # Arguments
///
@@ -89,12 +123,12 @@ fn cover<'a, T: Copy + Ord + 'static, U: 'a>(
query: &RangeSet<T>,
f: impl Fn(&U) -> &RangeSet<T>,
others: impl IntoIterator<Item = &'a U>,
) -> Option<Vec<(usize, &'a U)>>
) -> CoverResult<'a, T, U>
where
Range<T>: ExactSizeIterator<Item = T>,
{
if query.is_empty() {
return Some(Default::default());
return Default::default();
}
// Filter out rangesets that are not a subset of query.
@@ -111,7 +145,10 @@ where
.collect();
if others.is_empty() {
return None;
return CoverResult {
covered: Vec::default(),
uncovered: query.clone(),
};
}
let mut uncovered = query.clone();
@@ -121,7 +158,8 @@ where
// Find the set with the most coverage.
for (i, (pos, item)) in others.iter().enumerate() {
let cover = f(item).intersection(&uncovered).len();
// If cover is non-empty or greater than the current candidate, update the candidate.
// If cover is non-empty or greater than the current candidate, update the
// candidate.
if cover > candidate.as_ref().map_or(0, |c| c.cover) {
candidate = Some(Candidate {
i,
@@ -141,11 +179,17 @@ where
candidates.push((pos, item));
} else {
// If no set was found, we cannot cover the query.
return None;
return CoverResult {
covered: candidates,
uncovered,
};
}
}
Some(candidates)
CoverResult {
covered: candidates,
uncovered: RangeSet::default(),
}
}
#[cfg(test)]
@@ -157,8 +201,9 @@ mod tests {
let query = RangeSet::<u32>::default();
let others = [RangeSet::from(1..5), RangeSet::from(6..10)];
let result = query.cover(others.iter()).unwrap();
assert!(result.is_empty());
let (covered, uncovered) = query.cover(others.iter());
assert!(covered.is_empty());
assert!(uncovered.is_empty());
}
#[test]
@@ -166,22 +211,24 @@ mod tests {
let query = RangeSet::from(1..5);
let others: Vec<RangeSet<u32>> = vec![];
let result = query.cover(others.iter());
assert!(result.is_none());
let (covered, uncovered) = query.cover(others.iter());
assert!(covered.is_empty());
assert_eq!(uncovered, query);
}
#[test]
fn test_no_subset_in_others() {
let query = RangeSet::from(5..10);
let others = [
RangeSet::from(1..4), // Completely outside query
RangeSet::from(3..7), // Partially overlaps but not a subset
RangeSet::from(8..15), // Partially overlaps but not a subset
RangeSet::from(11..20),
RangeSet::from(1..4), // Completely outside query
RangeSet::from(3..7), // Partially overlaps but not a subset
RangeSet::from(8..15), // Partially overlaps but not a subset
RangeSet::from(11..20), // Completely outside query
];
let result = query.cover(others.iter());
assert!(result.is_none());
let (covered, uncovered) = query.cover(others.iter());
assert!(covered.is_empty());
assert_eq!(uncovered, query);
}
#[test]
@@ -189,9 +236,10 @@ mod tests {
let query = RangeSet::from(1..5);
let others = [query.clone()];
let result = query.cover(others.iter()).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0], &query);
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 1);
assert_eq!(covered[0], &query);
assert!(uncovered.is_empty());
}
#[test]
@@ -199,9 +247,10 @@ mod tests {
let query = RangeSet::from(vec![1..5, 10..15]);
let others = [query.clone()];
let result = query.cover(others.iter()).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0], &query);
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 1);
assert_eq!(covered[0], &query);
assert!(uncovered.is_empty());
}
#[test]
@@ -209,36 +258,37 @@ mod tests {
let query = RangeSet::from(1..10);
let others = [RangeSet::from(1..5), RangeSet::from(5..10)];
let result = query.cover(others.iter()).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains(&&others[0]));
assert!(result.contains(&&others[1]));
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 2);
assert!(covered.contains(&&others[0]));
assert!(covered.contains(&&others[1]));
assert!(uncovered.is_empty());
}
#[test]
fn test_multi_range_cover_with_multi_range_sets() {
// query with multiple disjoint ranges
// Query with multiple disjoint ranges
let query = RangeSet::from(vec![1..5, 10..15, 20..25]);
// Others with multiple ranges in each RangeSet
let others = [
RangeSet::from(vec![1..3, 20..23]), // Covers part of first and third ranges
RangeSet::from(vec![3..5, 10..12]), // Covers rest of first and part of second
RangeSet::from(vec![12..15, 23..25]),
RangeSet::from(vec![12..15, 23..25]), // Covers part of second and third ranges
];
let result = query.cover(others.iter()).unwrap();
assert_eq!(result.len(), 3);
assert!(result.contains(&&others[0]));
assert!(result.contains(&&others[1]));
assert!(result.contains(&&others[2]));
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 3);
assert!(covered.contains(&&others[0]));
assert!(covered.contains(&&others[1]));
assert!(covered.contains(&&others[2]));
assert!(uncovered.is_empty());
}
#[allow(clippy::single_range_in_vec_init)]
#[test]
fn test_complex_nested_subsets() {
// query with multiple ranges
// Query with multiple ranges
let query = RangeSet::from(vec![1..10, 15..20]);
// Collection with nested subsets
@@ -248,40 +298,42 @@ mod tests {
RangeSet::from(2..3),
RangeSet::from(8..20), // Not a subset
RangeSet::from(vec![9..10, 15..17]),
RangeSet::from(vec![21..30]),
RangeSet::from(vec![21..30]), // Not a subset
];
let result = query.cover(others.iter()).unwrap();
assert_eq!(result.len(), 2);
assert!(result.contains(&&others[0]));
assert!(result.contains(&&others[4]));
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 2);
assert!(covered.contains(&&others[0]));
assert!(covered.contains(&&others[4]));
assert!(uncovered.is_empty());
}
#[test]
fn test_unable_to_cover_simple() {
let query = RangeSet::from(1..10);
let others = [RangeSet::from(1..5), RangeSet::from(6..10)];
let others = [&RangeSet::from(1..5), &RangeSet::from(6..10)];
let result = query.cover(others.iter());
assert!(result.is_none());
let (covered, uncovered) = query.cover(others);
assert_eq!(covered, others);
assert_eq!(uncovered, RangeSet::from(5..6));
}
#[test]
fn test_unable_to_cover_multiple_ranges() {
// query with multiple ranges
// Query with multiple ranges
let query = RangeSet::from(vec![1..10, 15..25, 30..35]);
// Collection with multiple ranges in each RangeSet
let others = [
RangeSet::from(vec![1..5, 16..20]), // Covers part of first and second ranges
RangeSet::from(vec![5..8, 21..25]), // Covers part of first and second ranges
RangeSet::from(vec![15..16, 30..33]), // Covers part of second and third ranges
RangeSet::from(vec![9..10, 34..35]),
&RangeSet::from(vec![1..5, 16..20]), // Covers part of first and second ranges
&RangeSet::from(vec![5..8, 21..25]), // Covers part of first and second ranges
&RangeSet::from(vec![15..16, 30..33]), // Covers part of second and third ranges
&RangeSet::from(vec![9..10, 34..35]), // Covers part of first and third ranges
];
let result = query.cover(others.iter());
assert!(result.is_none());
let (covered, uncovered) = query.cover(others);
assert_eq!(covered, others);
assert_eq!(uncovered, RangeSet::from([8..9, 20..21, 33..34,]));
}
#[test]
@@ -292,7 +344,8 @@ mod tests {
// A subset with a range of length 1.
let others = [RangeSet::from(vec![1..2])];
let result = query.cover(others.iter());
assert!(result.is_some());
let (covered, uncovered) = query.cover(others.iter());
assert_eq!(covered.len(), 1);
assert!(uncovered.is_empty());
}
}