use super::canonicalize;
use crate::core::{Graph, IgraphResult};
pub fn count_automorphisms(graph: &Graph, colors: Option<&[u32]>) -> IgraphResult<f64> {
Ok(canonicalize(graph, colors)?.group_order)
}
#[cfg(test)]
#[allow(clippy::cast_precision_loss)] mod tests {
use super::*;
fn cycle(n: u32, directed: bool) -> Graph {
let mut g = Graph::new(n, directed).expect("graph");
for i in 0..n {
g.add_edge(i, (i + 1) % n).expect("edge");
}
g
}
fn complete(n: u32) -> Graph {
let mut g = Graph::new(n, false).expect("graph");
for u in 0..n {
for v in (u + 1)..n {
g.add_edge(u, v).expect("edge");
}
}
g
}
fn path(n: u32) -> Graph {
let mut g = Graph::new(n, false).expect("graph");
for i in 0..n.saturating_sub(1) {
g.add_edge(i, i + 1).expect("edge");
}
g
}
fn petersen() -> Graph {
let mut g = Graph::new(10, false).expect("graph");
for i in 0..5 {
g.add_edge(i, (i + 1) % 5).expect("edge"); g.add_edge(i + 5, ((i + 2) % 5) + 5).expect("edge"); g.add_edge(i, i + 5).expect("edge"); }
g
}
#[test]
fn complete_graph_is_factorial() {
let fact = [1u64, 1, 2, 6, 24, 120, 720];
for n in 1u32..=6 {
let got = count_automorphisms(&complete(n), None).expect("count");
assert!((got - fact[n as usize] as f64).abs() < 0.5, "K_{n}");
}
}
#[test]
fn cycle_is_dihedral() {
for n in 3u32..=8 {
let got = count_automorphisms(&cycle(n, false), None).expect("count");
assert!((got - f64::from(2 * n)).abs() < 0.5, "C_{n}");
}
}
#[test]
fn path_is_two() {
for n in 2u32..=8 {
let got = count_automorphisms(&path(n), None).expect("count");
assert!((got - 2.0).abs() < 0.5, "path_{n}");
}
}
#[test]
fn petersen_is_120() {
let got = count_automorphisms(&petersen(), None).expect("count");
assert!((got - 120.0).abs() < 0.5);
}
#[test]
fn directed_cycle_is_n() {
for n in 3u32..=8 {
let got = count_automorphisms(&cycle(n, true), None).expect("count");
assert!((got - f64::from(n)).abs() < 0.5, "directed C_{n}");
}
}
#[test]
fn empty_graph_is_factorial() {
let g = Graph::new(4, false).expect("graph");
let got = count_automorphisms(&g, None).expect("count");
assert!((got - 24.0).abs() < 0.5);
}
#[test]
fn null_graph_is_one() {
let g = Graph::new(0, false).expect("graph");
let got = count_automorphisms(&g, None).expect("count");
assert!((got - 1.0).abs() < 0.5);
}
#[test]
fn colors_restrict_the_group() {
let g = complete(4);
let colors = [1u32, 0, 0, 0];
let got = count_automorphisms(&g, Some(&colors)).expect("count");
assert!((got - 6.0).abs() < 0.5);
}
#[test]
fn rejects_multigraph() {
let mut g = Graph::new(2, false).expect("graph");
g.add_edge(0, 1).expect("edge");
g.add_edge(0, 1).expect("edge");
assert!(count_automorphisms(&g, None).is_err());
}
#[test]
fn rejects_wrong_color_length() {
let g = cycle(3, false);
let colors = [0u32, 1];
assert!(count_automorphisms(&g, Some(&colors)).is_err());
}
}