use crate::canonical::ir::{
ArrayLeaf, BoundCardinality, Bounds, ContainsFacet, LengthBounds, Schema,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ArrayLeaves {
leaves: Vec<ArrayLeaf>,
canonical: bool,
}
impl Default for ArrayLeaves {
fn default() -> Self {
Self {
leaves: Vec::new(),
canonical: true,
}
}
}
impl ArrayLeaves {
pub(crate) fn insert(&mut self, leaf: ArrayLeaf) {
self.leaves.push(leaf);
self.canonical = false;
}
fn canonicalize(&mut self) {
if self.canonical {
return;
}
let was_empty = self.leaves.is_empty();
self.leaves = merge(std::mem::take(&mut self.leaves));
extend_over_bare_windows(&mut self.leaves);
hand_off_empty(&mut self.leaves);
self.leaves = merge(std::mem::take(&mut self.leaves));
absorb_trivially_distinct(&mut self.leaves);
absorb_trivially_conforming(&mut self.leaves);
drop_subsumed(&mut self.leaves);
self.canonical = true;
debug_assert_eq!(
self.leaves.is_empty(),
was_empty,
"merging emptied the leaves"
);
}
pub(crate) fn clear(&mut self) {
self.leaves.clear();
self.canonical = true;
}
pub(crate) fn retain(&mut self, keep: impl FnMut(&ArrayLeaf) -> bool) {
self.canonicalize();
self.leaves.retain(keep);
}
pub(crate) fn is_empty(&self) -> bool {
self.leaves.is_empty()
}
pub(crate) fn as_slice(&mut self) -> &[ArrayLeaf] {
self.canonicalize();
&self.leaves
}
}
impl IntoIterator for ArrayLeaves {
type Item = ArrayLeaf;
type IntoIter = std::vec::IntoIter<ArrayLeaf>;
fn into_iter(mut self) -> Self::IntoIter {
self.canonicalize();
self.leaves.into_iter()
}
}
fn merge(mut leaves: Vec<ArrayLeaf>) -> Vec<ArrayLeaf> {
if leaves.len() < 2 {
return leaves;
}
leaves.sort_by(|left, right| {
(left.unique, &left.prefix, &left.items, &left.contains).cmp(&(
right.unique,
&right.prefix,
&right.items,
&right.contains,
))
});
let mut merged: Vec<ArrayLeaf> = Vec::with_capacity(leaves.len());
let mut windows: Vec<LengthBounds> = Vec::new();
let mut facets: Option<Facets> = None;
for leaf in leaves {
if facets.as_ref().is_none_or(|group| {
group.unique != leaf.unique
|| group.prefix != leaf.prefix
|| group.items != leaf.items
|| group.contains != leaf.contains
}) {
flush_group(&mut merged, facets.take(), &mut windows);
facets = Some(Facets {
unique: leaf.unique,
prefix: leaf.prefix,
items: leaf.items,
contains: leaf.contains,
});
}
windows.push(leaf.lengths);
}
flush_group(&mut merged, facets, &mut windows);
merged
}
struct Facets {
unique: bool,
prefix: Vec<Schema>,
items: Option<Schema>,
contains: Vec<ContainsFacet>,
}
fn flush_group(
merged: &mut Vec<ArrayLeaf>,
facets: Option<Facets>,
windows: &mut Vec<LengthBounds>,
) {
let Some(Facets {
unique,
prefix,
items,
contains,
}) = facets
else {
return;
};
let mut lengths = Bounds::merge_all(std::mem::take(windows));
let last = lengths.pop().expect("a group holds at least one window");
for window in lengths {
merged.push(ArrayLeaf {
lengths: window,
unique,
prefix: prefix.clone(),
items: items.clone(),
contains: contains.clone(),
});
}
merged.push(ArrayLeaf {
lengths: last,
unique,
prefix,
items,
contains,
});
}
fn extend_over_bare_windows(leaves: &mut [ArrayLeaf]) {
let bare: Vec<LengthBounds> = leaves
.iter()
.filter(|leaf| {
!leaf.unique
&& leaf.prefix.is_empty()
&& leaf.items.is_none()
&& leaf.contains.is_empty()
})
.map(|leaf| leaf.lengths.clone())
.collect();
if bare.is_empty() {
return;
}
for leaf in leaves.iter_mut() {
if !leaf.unique
&& leaf.prefix.is_empty()
&& leaf.items.is_none()
&& leaf.contains.is_empty()
{
continue;
}
loop {
let mut grown = false;
for window in &bare {
let merged = Bounds::merge_all(vec![leaf.lengths.clone(), window.clone()]);
if let Ok([merged]) = <[_; 1]>::try_from(merged) {
if merged != leaf.lengths {
leaf.lengths = merged;
grown = true;
}
}
}
if !grown {
break;
}
}
}
}
fn hand_off_empty(leaves: &mut [ArrayLeaf]) {
if !leaves.iter().any(|leaf| {
leaf.lengths
.minimum
.as_ref()
.is_none_or(BoundCardinality::is_zero)
&& leaf
.contains
.iter()
.all(|facet| facet.effective_minimum().is_zero())
}) {
return;
}
let one = BoundCardinality::from(1);
for leaf in leaves.iter_mut() {
if leaf.lengths.minimum.as_ref() == Some(&one) {
leaf.lengths.minimum = None;
}
}
}
fn absorb_trivially_distinct(leaves: &mut Vec<ArrayLeaf>) {
let Some(trivial) = leaves.iter().position(|leaf| {
!leaf.unique
&& leaf.prefix.is_empty()
&& leaf.items.is_none()
&& leaf.contains.is_empty()
&& leaf
.lengths
.maximum
.as_ref()
.is_some_and(|max| *max <= BoundCardinality::from(1))
}) else {
return;
};
let window = leaves[trivial].lengths.clone();
let Some((target, widened)) = leaves.iter().enumerate().find_map(|(index, leaf)| {
if !leaf.unique
|| leaf.items.is_some()
|| !leaf.prefix.is_empty()
|| !leaf.contains.is_empty()
{
return None;
}
let mut merged = Bounds::merge_all(vec![leaf.lengths.clone(), window.clone()]);
(merged.len() == 1).then(|| (index, merged.pop().expect("a merged window")))
}) else {
return;
};
leaves[target].lengths = widened;
leaves.remove(trivial);
}
fn absorb_trivially_conforming(leaves: &mut Vec<ArrayLeaf>) {
let Some(trivial) = leaves.iter().position(|leaf| {
!leaf.unique
&& leaf.prefix.is_empty()
&& leaf.items.is_none()
&& leaf.contains.is_empty()
&& leaf
.lengths
.maximum
.as_ref()
.is_some_and(BoundCardinality::is_zero)
}) else {
return;
};
let window = leaves[trivial].lengths.clone();
let Some((target, widened)) = leaves
.iter()
.enumerate()
.filter(|(_, leaf)| {
(leaf.items.is_some() || !leaf.prefix.is_empty()) && leaf.contains.is_empty()
})
.find_map(|(index, leaf)| {
let merged = Bounds::merge_all(vec![leaf.lengths.clone(), window.clone()]);
match <[_; 1]>::try_from(merged) {
Ok([widened]) => Some((index, widened)),
Err(_) => None,
}
})
else {
return;
};
leaves[target].lengths = widened;
leaves.remove(trivial);
}
fn drop_subsumed(leaves: &mut Vec<ArrayLeaf>) {
if leaves.len() < 2 {
return;
}
let mut keep = vec![true; leaves.len()];
for (index, leaf) in leaves.iter().enumerate() {
for (other_index, other) in leaves.iter().enumerate() {
if index == other_index || !keep[other_index] || !keep[index] {
continue;
}
let looser_items = other.items.is_none()
&& other.contains.is_empty()
&& leaf.prefix.starts_with(&other.prefix)
&& (leaf.items.is_some()
|| leaf.prefix.len() > other.prefix.len()
|| !leaf.contains.is_empty());
let same_elements = other.prefix == leaf.prefix
&& other.items == leaf.items
&& other.contains == leaf.contains;
let wider = other.lengths.covers(&leaf.lengths)
&& (!other.unique || leaf.unique)
&& (looser_items || same_elements);
debug_assert!(
!wider || other.unique != leaf.unique || !same_elements,
"merging left two leaves carrying the same facets"
);
if wider && ((leaf.unique && !other.unique) || looser_items) {
keep[index] = false;
}
}
}
let mut index = 0;
leaves.retain(|_| {
let keeps = keep[index];
index += 1;
keeps
});
}