use crate::algorithms::paths::distances::distances;
use crate::core::{Graph, IgraphResult};
pub fn density(graph: &Graph) -> IgraphResult<Option<f64>> {
let n = graph.vcount();
if n == 0 || n == 1 {
return Ok(None);
}
let m = graph.ecount();
let directed = graph.is_directed();
let n_f = f64::from(n);
#[allow(clippy::cast_precision_loss)]
let m_f = m as f64;
let result = if directed {
m_f / n_f / (n_f - 1.0)
} else {
m_f / n_f * 2.0 / (n_f - 1.0)
};
Ok(Some(result))
}
pub fn mean_distance(graph: &Graph) -> IgraphResult<Option<f64>> {
let n = graph.vcount();
if n < 2 {
return Ok(None);
}
let mut sum: u64 = 0;
let mut count: u64 = 0;
for v in 0..n {
let d = distances(graph, v)?;
let v_us = v as usize;
for (target, &val) in d.iter().enumerate() {
if target == v_us {
continue;
}
if let Some(dist) = val {
sum += u64::from(dist);
count += 1;
}
}
}
if count == 0 {
return Ok(None);
}
#[allow(clippy::cast_precision_loss)]
let mean = (sum as f64) / (count as f64);
Ok(Some(mean))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn density_empty_graph_is_none() {
let g = Graph::with_vertices(0);
assert_eq!(density(&g).unwrap(), None);
}
#[test]
fn density_singleton_is_none() {
let g = Graph::with_vertices(1);
assert_eq!(density(&g).unwrap(), None);
}
#[test]
fn density_complete_undirected_is_one() {
let mut g = Graph::with_vertices(4);
for u in 0..4u32 {
for v in (u + 1)..4 {
g.add_edge(u, v).unwrap();
}
}
assert_eq!(density(&g).unwrap(), Some(1.0));
}
#[test]
fn density_no_edges_is_zero() {
let g = Graph::with_vertices(5);
assert_eq!(density(&g).unwrap(), Some(0.0));
}
#[test]
fn density_directed_complete_is_one() {
let mut g = Graph::new(3, true).unwrap();
for u in 0..3u32 {
for v in 0..3u32 {
if u != v {
g.add_edge(u, v).unwrap();
}
}
}
assert_eq!(density(&g).unwrap(), Some(1.0));
}
#[test]
fn density_path_5_is_2_over_10() {
let mut g = Graph::with_vertices(5);
for i in 0..4 {
g.add_edge(i, i + 1).unwrap();
}
assert_eq!(density(&g).unwrap(), Some(0.4));
}
#[test]
fn mean_distance_n_lt_2_is_none() {
let g = Graph::with_vertices(0);
assert_eq!(mean_distance(&g).unwrap(), None);
let g = Graph::with_vertices(1);
assert_eq!(mean_distance(&g).unwrap(), None);
}
#[test]
fn mean_distance_isolated_vertices_is_none() {
let g = Graph::with_vertices(5);
assert_eq!(mean_distance(&g).unwrap(), None);
}
#[test]
fn mean_distance_path_5_is_2() {
let mut g = Graph::with_vertices(5);
for i in 0..4 {
g.add_edge(i, i + 1).unwrap();
}
assert_eq!(mean_distance(&g).unwrap(), Some(2.0));
}
#[test]
fn mean_distance_triangle_is_1() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
assert_eq!(mean_distance(&g).unwrap(), Some(1.0));
}
#[test]
fn mean_distance_two_components_only_within_components() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(3, 4).unwrap();
assert_eq!(mean_distance(&g).unwrap(), Some(1.25));
}
#[test]
fn mean_distance_directed_uses_out_edges() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let four_thirds = 4.0_f64 / 3.0;
assert_eq!(mean_distance(&g).unwrap(), Some(four_thirds));
}
}