use std::collections::BTreeMap;
use crate::model::Ocel;
#[derive(Debug)]
pub struct ObjectGraph<'a> {
ids: Vec<&'a str>,
index: BTreeMap<&'a str, usize>,
adjacency: Vec<Vec<usize>>,
}
impl Ocel {
#[must_use]
pub fn object_graph(&self) -> ObjectGraph<'_> {
let mut ids: Vec<&str> = self.objects.iter().map(|o| o.id.as_str()).collect();
ids.sort_unstable();
ids.dedup();
let index: BTreeMap<&str, usize> = ids.iter().enumerate().map(|(i, id)| (*id, i)).collect();
let mut adjacency = vec![Vec::new(); ids.len()];
for event in &self.events {
let mut members: Vec<usize> = event
.relationships
.iter()
.filter_map(|r| index.get(r.object_id.as_str()).copied())
.collect();
members.sort_unstable();
members.dedup();
for (i, &a) in members.iter().enumerate() {
for &b in &members[i + 1..] {
adjacency[a].push(b);
adjacency[b].push(a);
}
}
}
for object in &self.objects {
let Some(&a) = index.get(object.id.as_str()) else {
continue;
};
for rel in &object.relationships {
if let Some(&b) = index.get(rel.object_id.as_str()) {
if a != b {
adjacency[a].push(b);
adjacency[b].push(a);
}
}
}
}
for list in &mut adjacency {
list.sort_unstable();
list.dedup();
}
ObjectGraph {
ids,
index,
adjacency,
}
}
}
impl<'a> ObjectGraph<'a> {
#[must_use]
pub fn len(&self) -> usize {
self.ids.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
#[must_use]
pub fn contains(&self, object_id: &str) -> bool {
self.index.contains_key(object_id)
}
pub fn neighbors(&self, object_id: &str) -> impl Iterator<Item = &'a str> + '_ {
self.index
.get(object_id)
.into_iter()
.flat_map(move |&i| self.adjacency[i].iter().map(move |&j| self.ids[j]))
}
#[must_use]
pub fn connected_components(&self) -> Vec<Vec<&'a str>> {
let mut seen = vec![false; self.ids.len()];
let mut components = Vec::new();
for start in 0..self.ids.len() {
if seen[start] {
continue;
}
seen[start] = true;
let mut queue = vec![start];
let mut component = Vec::new();
while let Some(node) = queue.pop() {
component.push(node);
for &next in &self.adjacency[node] {
if !seen[next] {
seen[next] = true;
queue.push(next);
}
}
}
component.sort_unstable();
components.push(component.into_iter().map(|i| self.ids[i]).collect());
}
components
}
}