use crate::Weight;
use crate::nodes::*;
use crate::paths::collect_raw_paths;
use crate::segment::Segment;
use crate::weight_regions;
use rustc_hash::FxHashMap;
use std::fmt;
use std::hash::Hash;
use std::sync::Arc;
pub type Gss<S> = WeightedGss<S, ()>;
pub struct WeightedGss<S, W> {
pub(crate) root: WRef<S, W>,
}
impl<S, W> Clone for WeightedGss<S, W> {
fn clone(&self) -> Self {
Self {
root: self.root.clone(),
}
}
}
impl<S, W> Default for WeightedGss<S, W> {
fn default() -> Self {
Self::new()
}
}
impl<S, W> WeightedGss<S, W> {
#[must_use]
pub fn new() -> Self {
Self { root: w_empty() }
}
#[must_use]
pub fn is_empty(&self) -> bool {
w_is_empty(&self.root)
}
#[must_use]
pub fn max_depth(&self) -> usize {
self.root.max_depth
}
}
impl<S, W> WeightedGss<S, W>
where
S: Clone + Eq + Hash,
W: Weight,
{
fn from_stack_with_end(stack: impl IntoIterator<Item = S>, weight: W, end: &URef<S>) -> Self {
let values: Vec<S> = stack.into_iter().collect();
let stacks = if values.is_empty() {
end.clone()
} else {
u_segment(
Segment::from_top_first(values.into_iter().rev().collect()),
end.clone(),
)
};
Self {
root: w_shared(Arc::new(weight), stacks),
}
}
#[must_use]
pub fn from_stack(stack: impl IntoIterator<Item = S>, weight: W) -> Self {
Self::from_stack_with_end(stack, weight, &u_end())
}
#[must_use]
pub fn from_stacks_with_weight<I, T>(stacks: I, weight: W) -> Self
where
I: IntoIterator<Item = T>,
T: IntoIterator<Item = S>,
{
let end = u_end();
let stacks = u_merge_all(stacks.into_iter().map(|stack| {
let values: Vec<S> = stack.into_iter().collect();
if values.is_empty() {
end.clone()
} else {
u_segment(
Segment::from_top_first(values.into_iter().rev().collect()),
end.clone(),
)
}
}));
Self {
root: w_shared(Arc::new(weight), stacks),
}
}
#[must_use]
pub fn from_stacks<I, T>(entries: I) -> Self
where
I: IntoIterator<Item = (T, W)>,
T: IntoIterator<Item = S>,
{
let end = u_end();
Self::merge_all(
entries
.into_iter()
.map(|(stack, weight)| Self::from_stack_with_end(stack, weight, &end)),
)
}
#[cfg(feature = "python")]
pub(crate) fn with_stack(&self, stack: impl IntoIterator<Item = S>, weight: W) -> Self {
self.merge(&Self::from_stack(stack, weight))
}
#[must_use]
pub fn merge(&self, other: &Self) -> Self {
Self {
root: w_merge(&self.root, &other.root),
}
}
pub(crate) fn merge_all(values: impl IntoIterator<Item = Self>) -> Self {
Self {
root: w_merge_all(values.into_iter().map(|value| value.root)),
}
}
#[must_use]
pub fn push(&self, symbol: S) -> Self {
Self {
root: w_push(&self.root, symbol),
}
}
#[must_use]
pub fn pop(&self) -> Self {
Self {
root: w_pop(&self.root),
}
}
#[must_use]
pub fn popn(&self, count: usize) -> Self {
Self {
root: w_popn(&self.root, count),
}
}
#[must_use]
pub fn top(&self) -> Option<S> {
w_single_exclusive_top(&self.root)
}
pub fn tops(&self) -> impl Iterator<Item = S> {
w_tops(&self.root).into_iter()
}
#[must_use]
pub fn has_empty_stack(&self) -> bool {
w_has_empty(&self.root)
}
#[must_use]
pub fn retain_top(&self, top: &S) -> Self {
Self {
root: w_retain_top(&self.root, top),
}
}
#[must_use]
pub fn retain_empty(&self) -> Self {
Self {
root: w_retain_empty(&self.root),
}
}
#[must_use]
pub fn pop_top(&self, top: &S) -> Self {
Self {
root: w_pop_top(&self.root, top),
}
}
#[cfg(feature = "python")]
pub(crate) fn pop_branches(&self) -> Vec<(S, Self)> {
self.tops()
.map(|top| {
let remainder = self.pop_top(&top);
(top, remainder)
})
.collect()
}
pub fn weights(&self) -> impl Iterator<Item = &W> {
weight_regions::iter(self)
}
#[must_use]
pub fn map_weights<V>(&self, mut transform: impl FnMut(&W) -> V) -> WeightedGss<S, V>
where
V: Weight,
{
self.filter_map_weights(|weight| Some(transform(weight)))
}
#[must_use]
pub fn filter_map_weights<V>(&self, transform: impl FnMut(&W) -> Option<V>) -> WeightedGss<S, V>
where
V: Weight,
{
weight_regions::filter_map(self, transform)
}
#[must_use]
pub fn joined_weight(&self) -> Option<W> {
w_joined_weight(&self.root)
}
#[cfg(feature = "python")]
pub(crate) fn empty_weight(&self) -> Option<W> {
w_empty_weight(&self.root)
}
pub fn to_stacks(&self, max_paths: usize) -> Result<Vec<(Vec<S>, W)>, PathLimitExceeded> {
let raw = collect_raw_paths(&self.root, max_paths)?;
let mut canonical: FxHashMap<Vec<S>, W> = FxHashMap::default();
for (stack, weight) in raw {
canonical
.entry(stack)
.and_modify(|current| *current = current.join(&weight))
.or_insert(weight);
}
Ok(canonical.into_iter().collect())
}
}
impl<S, W> fmt::Debug for WeightedGss<S, W> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("WeightedGss")
.field("is_empty", &self.is_empty())
.field("max_depth", &self.root.max_depth)
.finish()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PathLimitExceeded {
pub limit: usize,
}
impl fmt::Display for PathLimitExceeded {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"the weighted GSS contains more than {} structural paths",
self.limit
)
}
}
impl std::error::Error for PathLimitExceeded {}