mirror of
https://github.com/tlsnotary/tlsn-utils.git
synced 2026-01-09 20:57:56 -05:00
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:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user