use std::collections::HashMap;
use compact_str::CompactString;
use fnv::FnvBuildHasher;
use crate::graph::GraphStorage;
type LabelCounts = HashMap<CompactString, usize, FnvBuildHasher>;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct GraphStatistics {
vertex_count: usize,
edge_count: usize,
vertex_labels: LabelCounts,
primary_vertex_labels: LabelCounts,
edge_labels: LabelCounts,
}
fn increment(counts: &mut LabelCounts, label: &str) {
match counts.get_mut(label) {
Some(count) => *count += 1,
None => {
counts.insert(CompactString::from(label), 1);
}
}
}
fn decrement(counts: &mut LabelCounts, label: &str) {
if let Some(count) = counts.get_mut(label) {
*count = count.saturating_sub(1);
if *count == 0 {
counts.remove(label);
}
}
}
fn sorted(counts: &LabelCounts) -> Vec<(&str, usize)> {
let mut entries: Vec<(&str, usize)> = counts.iter().map(|(l, c)| (l.as_str(), *c)).collect();
entries.sort_unstable_by(|a, b| a.0.cmp(b.0));
entries
}
impl GraphStatistics {
pub fn new() -> Self {
Self::default()
}
pub fn from_storage<S: GraphStorage + ?Sized>(storage: &S) -> Self {
let mut stats = Self::new();
for vertex in storage.vertices() {
stats.vertex_added(storage.vertex_labels(vertex));
}
for edge in storage.edges() {
stats.edge_added(storage.edge_label(edge));
}
stats
}
pub fn vertex_count(&self) -> usize {
self.vertex_count
}
pub fn edge_count(&self) -> usize {
self.edge_count
}
pub fn vertex_label_count(&self, label: &str) -> usize {
self.vertex_labels.get(label).copied().unwrap_or(0)
}
pub fn primary_vertex_label_count(&self, label: &str) -> usize {
self.primary_vertex_labels.get(label).copied().unwrap_or(0)
}
pub fn edge_label_count(&self, label: &str) -> usize {
self.edge_labels.get(label).copied().unwrap_or(0)
}
pub fn vertex_label_counts(&self) -> Vec<(&str, usize)> {
sorted(&self.vertex_labels)
}
pub fn primary_vertex_label_counts(&self) -> Vec<(&str, usize)> {
sorted(&self.primary_vertex_labels)
}
pub fn edge_label_counts(&self) -> Vec<(&str, usize)> {
sorted(&self.edge_labels)
}
pub(crate) fn vertex_added(&mut self, labels: &[CompactString]) {
self.vertex_count += 1;
self.labels_added(labels);
}
pub(crate) fn vertex_removed(&mut self, labels: &[CompactString]) {
self.vertex_count = self.vertex_count.saturating_sub(1);
self.labels_removed(labels);
}
pub(crate) fn vertex_relabeled(&mut self, old: &[CompactString], new: &[CompactString]) {
self.labels_removed(old);
self.labels_added(new);
}
pub(crate) fn edge_added(&mut self, label: Option<&str>) {
self.edge_count += 1;
if let Some(label) = label {
increment(&mut self.edge_labels, label);
}
}
pub(crate) fn edge_removed(&mut self, label: Option<&str>) {
self.edge_count = self.edge_count.saturating_sub(1);
if let Some(label) = label {
decrement(&mut self.edge_labels, label);
}
}
pub(crate) fn edge_relabeled(&mut self, old: Option<&str>, new: Option<&str>) {
if let Some(old) = old {
decrement(&mut self.edge_labels, old);
}
if let Some(new) = new {
increment(&mut self.edge_labels, new);
}
}
fn labels_added(&mut self, labels: &[CompactString]) {
for label in labels {
increment(&mut self.vertex_labels, label);
}
if let Some(primary) = labels.first() {
increment(&mut self.primary_vertex_labels, primary);
}
}
fn labels_removed(&mut self, labels: &[CompactString]) {
for label in labels {
decrement(&mut self.vertex_labels, label);
}
if let Some(primary) = labels.first() {
decrement(&mut self.primary_vertex_labels, primary);
}
}
}