use crate::{Ddnnf, NodeType};
use serde::Serialize;
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub struct Statistics {
pub nodes: NodeCount,
pub child_connections: ChildConnections,
pub paths: Paths,
}
impl From<&Ddnnf> for Statistics {
fn from(ddnnf: &Ddnnf) -> Self {
Self {
nodes: NodeCount::from(ddnnf),
child_connections: ChildConnections::from(ddnnf),
paths: Paths::from(ddnnf),
}
}
}
impl Ddnnf {
pub fn statistics(&self) -> Statistics {
Statistics::from(self)
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize)]
pub struct NodeCount {
pub total: usize,
pub and: usize,
pub or: usize,
pub literal: usize,
pub r#true: usize,
pub r#false: usize,
}
impl From<&Ddnnf> for NodeCount {
fn from(ddnnf: &Ddnnf) -> Self {
let mut counts = Self {
total: ddnnf.nodes.len(),
..Default::default()
};
ddnnf.nodes.iter().for_each(|node| match node.ntype {
NodeType::And { .. } => counts.and += 1,
NodeType::Or { .. } => counts.or += 1,
NodeType::Literal { .. } => counts.literal += 1,
NodeType::True => counts.r#true += 1,
NodeType::False => counts.r#false += 1,
});
counts
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize)]
pub struct ChildConnections {
pub total: usize,
pub and: usize,
pub or: usize,
}
impl From<&Ddnnf> for ChildConnections {
fn from(ddnnf: &Ddnnf) -> Self {
let mut connections = Self::default();
ddnnf.nodes.iter().for_each(|node| match &node.ntype {
NodeType::And { children } => connections.and += children.len(),
NodeType::Or { children } => connections.or += children.len(),
_ => {}
});
connections.total = connections.and + connections.or;
connections
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Serialize)]
pub struct Paths {
pub amount: usize,
pub shortest: usize,
pub longest: usize,
pub mean: f64,
pub deviation: f64,
}
impl From<&Ddnnf> for Paths {
fn from(ddnnf: &Ddnnf) -> Self {
let depths = calculate_depths(ddnnf, ddnnf.nodes.len() - 1, 0);
let amount = depths.len();
let &shortest = depths.iter().min().unwrap();
let &longest = depths.iter().max().unwrap();
let mean = depths.iter().sum::<usize>() as f64 / amount as f64;
let variance: f64 = depths
.iter()
.map(|&depth| (depth as f64 - mean).powi(2))
.sum();
let deviation = (variance / amount as f64).sqrt();
Self {
amount,
shortest,
longest,
mean,
deviation,
}
}
}
fn calculate_depths(ddnnf: &Ddnnf, index: usize, current_depth: usize) -> Vec<usize> {
match &ddnnf.nodes[index].ntype {
NodeType::And { children } | NodeType::Or { children } => children
.iter()
.flat_map(|&child| calculate_depths(ddnnf, child, current_depth + 1))
.collect(),
_ => vec![current_depth],
}
}