use std::collections::{BTreeMap, BTreeSet};
use snafu::{OptionExt, ResultExt, Snafu};
use uuid::Uuid;
use crate::{EdgeStore, Graph, Meta, MetadataFilter, Node, NodeStore};
#[derive(Clone, Debug, Snafu)]
pub enum MdmError {
NodeNotOnAxis { id: Uuid },
NodeStore { source: crate::node::NodeStoreError },
EdgeStore { source: crate::edge::EdgeStoreError },
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeSelectionMode {
#[default]
Independent,
Dependent(BTreeSet<String>),
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeDisplayMode {
#[default]
Binary,
Kinds,
Labels,
Weights,
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeDivisionMode {
Absolute,
#[default]
Equal,
Relative,
}
pub fn mdm_axis_nodes(
graph: &Graph,
node_filter: &MetadataFilter,
edge_filter: &MetadataFilter,
root_ids: Option<&Vec<Uuid>>,
node_depth: Option<usize>,
node_selection_mode: &NodeSelectionMode,
) -> Result<Vec<Uuid>, MdmError> {
let root_ids = root_ids
.cloned()
.unwrap_or_else(|| graph.root_ids().map(|id| id.to_owned()).collect());
let node_kinds_vec = node_filter.kinds.clone().unwrap_or_else(|| {
graph
.node_aggregate()
.kinds
.keys()
.map(|x| x.to_owned())
.collect::<Vec<_>>()
});
let node_kind_order = node_kinds_vec
.into_iter()
.enumerate()
.map(|(i, kind)| (kind, i))
.collect::<BTreeMap<String, usize>>();
let (mut ind_roots, mut dep_roots) =
split_dependent_roots(graph, root_ids, node_selection_mode)?;
ind_roots.sort_by_key(|id| {
graph
.get_node(id)
.map(|n| node_kind_order.get(&n.get_meta().kind))
});
dep_roots.sort_by_key(|id| {
graph
.get_node(id)
.map(|n| node_kind_order.get(&n.get_meta().kind))
});
let ind_leaf_vec: Vec<Uuid> =
independent_leafs(graph, &ind_roots, node_filter, node_depth).collect();
if node_selection_mode == &NodeSelectionMode::Independent {
return Ok(ind_leaf_vec);
}
let mut result = ind_leaf_vec.clone();
let ind_leaf_set: BTreeSet<Uuid> = ind_leaf_vec.iter().copied().collect();
let dep_leaf_iter =
independent_leafs(graph, &dep_roots, node_filter, node_depth).filter(|&leaf_id| {
let this = BTreeSet::from([leaf_id]);
graph
.edges_between_all(&this, &ind_leaf_set)
.any(|e| edge_filter.satisfies(e))
|| graph
.edges_between_all(&ind_leaf_set, &this)
.any(|e| edge_filter.satisfies(e))
});
result.extend(dep_leaf_iter);
Ok(result)
}
pub fn mdm_axis_expand(
axis: &mut Vec<Uuid>,
node_id: &Uuid,
graph: &Graph,
node_filter: &MetadataFilter,
edge_filter: &MetadataFilter,
node_selection_mode: &NodeSelectionMode,
) -> Result<(), MdmError> {
let position = axis
.iter()
.position(|id| id == node_id)
.with_context(|| NodeNotOnAxisSnafu { id: *node_id })?;
let children = mdm_axis_children(
axis,
node_id,
graph,
node_filter,
edge_filter,
node_selection_mode,
)?;
axis.splice(position..position + 1, children);
Ok(())
}
pub fn mdm_axis_children<'a>(
axis: &[Uuid],
node_id: &Uuid,
graph: &'a Graph,
node_filter: &'a MetadataFilter,
edge_filter: &'a MetadataFilter,
node_selection_mode: &NodeSelectionMode,
) -> Result<impl Iterator<Item = Uuid> + 'a, MdmError> {
let children = graph
.get_node_err(node_id)
.with_context(|_| NodeStoreSnafu)?
.children
.iter()
.filter(|&child_id| {
graph
.get_node(child_id)
.map(|c| node_filter.satisfies(c))
.unwrap_or_default()
});
let children: Box<dyn Iterator<Item = &Uuid>> = match node_selection_mode {
NodeSelectionMode::Independent => Box::new(children),
NodeSelectionMode::Dependent(kinds) => {
let ind_leaf_set = axis
.iter()
.filter(|&id| {
graph
.get_node(id)
.map(|n| kinds.contains(&n.get_meta().kind))
.unwrap_or_default()
})
.copied()
.collect::<BTreeSet<_>>();
Box::new(children.filter(move |&child_id| {
let this = BTreeSet::from([*child_id]);
graph
.edges_between_all(&this, &ind_leaf_set)
.any(|e| edge_filter.satisfies(e))
|| graph
.edges_between_all(&ind_leaf_set, &this)
.any(|e| edge_filter.satisfies(e))
}))
}
};
Ok(children.copied())
}
pub fn mdm_axis_collapse(
axis: &mut Vec<Uuid>,
node_id: &Uuid,
graph: &Graph,
) -> Result<(), MdmError> {
let leaf_set = axis.iter().copied().collect::<BTreeSet<_>>();
let mut to_replace = graph
.descendant_ids(node_id, true, true, Some(&leaf_set), None)
.filter_map(|id| id.ok())
.filter(|&id| leaf_set.contains(id))
.filter_map(|id| axis.iter().position(|e| e == id));
if let Some(first) = to_replace.next() {
let (start, end) =
to_replace.fold((first, first), |(start, end), c| (start.min(c), end.max(c)));
axis.splice(start..end + 1, [*node_id]);
}
Ok(())
}
fn split_dependent_roots(
graph: &Graph,
root_ids: Vec<Uuid>,
mode: &NodeSelectionMode,
) -> Result<(Vec<Uuid>, Vec<Uuid>), MdmError> {
Ok(match mode {
NodeSelectionMode::Independent => (root_ids, vec![]),
NodeSelectionMode::Dependent(lead_kinds) => {
let mut ind_roots = vec![];
let mut dep_roots = vec![];
for root_id in root_ids.into_iter() {
if lead_kinds.contains(
graph
.get_node_err(&root_id)
.with_context(|_| NodeStoreSnafu)?
.kind(),
) {
ind_roots.push(root_id);
} else {
dep_roots.push(root_id);
}
}
(ind_roots, dep_roots)
}
})
}
fn independent_leafs<'a>(
graph: &'a Graph,
root_ids: &'a [Uuid],
node_filter: &'a MetadataFilter,
node_depth: Option<usize>,
) -> impl Iterator<Item = Uuid> + 'a {
root_ids.iter().flat_map(move |root_id| {
let descendants: Box<dyn Iterator<Item = &Uuid>> = if node_depth == Some(0)
|| graph
.get_node(root_id)
.map(|r| r.is_leaf())
.unwrap_or_default()
{
Box::new(Some(root_id).into_iter())
} else {
Box::new(
graph
.descendant_ids(root_id, true, true, None, node_depth)
.filter_map(|id| id.ok()),
)
};
descendants.filter_map(|id| {
graph.get_node(id).and_then(|node| {
if node_filter.satisfies(node) {
Some(*id)
} else {
None
}
})
})
})
}
pub fn display_sort(
graph: &Graph,
node_ids: &[Uuid],
node_kind_order: Option<&BTreeMap<String, usize>>,
leaf_ids: Option<&BTreeSet<Uuid>>,
) -> Vec<usize> {
let mut nodes: Vec<(usize, &Node)> = node_ids
.iter()
.filter_map(|x| graph.get_node(x))
.enumerate()
.collect();
nodes.sort_unstable_by_key(|&(_, node)| {
(
node_kind_order.map(|x| x.get(node.kind())),
graph.node_width(node.id(), false, leaf_ids, None).unwrap(),
node.name(),
node.id(),
)
});
nodes.into_iter().map(|x| x.0).collect()
}