use std::collections::HashMap;
#[derive(Clone)]
pub struct UpSetIntersection {
pub mask: u64,
pub count: usize,
}
#[derive(Clone, Default)]
pub enum UpSetSort {
#[default]
ByFrequency,
ByDegree,
Natural,
}
pub struct UpSetPlot {
pub set_names: Vec<String>,
pub set_sizes: Vec<usize>,
pub intersections: Vec<UpSetIntersection>,
pub sort: UpSetSort,
pub max_visible: Option<usize>,
pub show_counts: bool,
pub show_set_sizes: bool,
pub bar_color: String,
pub dot_color: String,
pub dot_empty_color: String,
}
impl Default for UpSetPlot {
fn default() -> Self {
Self::new()
}
}
impl UpSetPlot {
pub fn new() -> Self {
Self {
set_names: Vec::new(),
set_sizes: Vec::new(),
intersections: Vec::new(),
sort: UpSetSort::ByFrequency,
max_visible: None,
show_counts: true,
show_set_sizes: true,
bar_color: "#333333".to_string(),
dot_color: "#333333".to_string(),
dot_empty_color: "#dddddd".to_string(),
}
}
pub fn with_sets<S, T, I, J>(mut self, sets: I) -> Self
where
S: Into<String>,
T: Eq + std::hash::Hash,
I: IntoIterator<Item = (S, J)>,
J: IntoIterator<Item = T>,
{
let named: Vec<(String, std::collections::HashSet<T>)> = sets
.into_iter()
.map(|(name, items)| (name.into(), items.into_iter().collect()))
.collect();
let n = named.len();
self.set_names = named.iter().map(|(name, _)| name.clone()).collect();
self.set_sizes = named.iter().map(|(_, s)| s.len()).collect();
let mut mask_counts: HashMap<u64, usize> = HashMap::new();
for (i, (_, set_i)) in named.iter().enumerate() {
for elem in set_i.iter() {
let already = named[..i].iter().any(|(_, sj)| sj.contains(elem));
if already {
continue;
}
let mut mask: u64 = 0;
for (j, (_, sj)) in named.iter().enumerate() {
if sj.contains(elem) {
mask |= 1u64 << j;
}
}
*mask_counts.entry(mask).or_insert(0) += 1;
}
}
let mut intersections: Vec<UpSetIntersection> = mask_counts
.into_iter()
.map(|(mask, count)| UpSetIntersection { mask, count })
.collect();
intersections.sort_by_key(|i| i.mask);
self.intersections = intersections;
let _ = n;
self
}
pub fn with_data<S: Into<String>>(
mut self,
set_names: impl IntoIterator<Item = S>,
set_sizes: impl IntoIterator<Item = usize>,
intersections: impl IntoIterator<Item = (u64, usize)>,
) -> Self {
self.set_names = set_names.into_iter().map(Into::into).collect();
self.set_sizes = set_sizes.into_iter().collect();
self.intersections = intersections
.into_iter()
.map(|(mask, count)| UpSetIntersection { mask, count })
.collect();
self
}
pub fn with_sort(mut self, sort: UpSetSort) -> Self {
self.sort = sort;
self
}
pub fn with_max_visible(mut self, max: usize) -> Self {
self.max_visible = Some(max);
self
}
pub fn without_set_sizes(mut self) -> Self {
self.show_set_sizes = false;
self
}
pub fn with_bar_color<S: Into<String>>(mut self, color: S) -> Self {
self.bar_color = color.into();
self
}
pub fn with_dot_color<S: Into<String>>(mut self, color: S) -> Self {
self.dot_color = color.into();
self
}
pub fn sorted_intersections(&self) -> Vec<&UpSetIntersection> {
let mut sorted: Vec<&UpSetIntersection> = self.intersections.iter().collect();
match self.sort {
UpSetSort::ByFrequency => {
sorted.sort_by(|a, b| b.count.cmp(&a.count));
}
UpSetSort::ByDegree => {
sorted.sort_by(|a, b| {
let da = a.mask.count_ones();
let db = b.mask.count_ones();
db.cmp(&da).then(b.count.cmp(&a.count))
});
}
UpSetSort::Natural => {}
}
if let Some(max) = self.max_visible {
sorted.truncate(max);
}
sorted
}
}