#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct AdjacencyList {
neighbors: Vec<Vec<usize>>,
}
#[allow(dead_code)]
impl AdjacencyList {
pub fn from_triangles(vertex_count: usize, indices: &[u32]) -> Self {
let mut neighbors: Vec<Vec<usize>> = vec![vec![]; vertex_count];
for tri in indices.chunks_exact(3) {
let (a, b, c) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
if !neighbors[a].contains(&b) {
neighbors[a].push(b);
}
if !neighbors[a].contains(&c) {
neighbors[a].push(c);
}
if !neighbors[b].contains(&a) {
neighbors[b].push(a);
}
if !neighbors[b].contains(&c) {
neighbors[b].push(c);
}
if !neighbors[c].contains(&a) {
neighbors[c].push(a);
}
if !neighbors[c].contains(&b) {
neighbors[c].push(b);
}
}
Self { neighbors }
}
pub fn vertex_count(&self) -> usize {
self.neighbors.len()
}
pub fn neighbors_of(&self, vertex: usize) -> &[usize] {
&self.neighbors[vertex]
}
pub fn valence(&self, vertex: usize) -> usize {
self.neighbors[vertex].len()
}
pub fn are_neighbors(&self, a: usize, b: usize) -> bool {
self.neighbors[a].contains(&b)
}
pub fn edge_count(&self) -> usize {
let sum: usize = self.neighbors.iter().map(|n| n.len()).sum();
sum / 2
}
pub fn average_valence(&self) -> f32 {
if self.neighbors.is_empty() {
return 0.0;
}
let sum: usize = self.neighbors.iter().map(|n| n.len()).sum();
sum as f32 / self.neighbors.len() as f32
}
pub fn vertices_with_valence(&self, val: usize) -> Vec<usize> {
self.neighbors
.iter()
.enumerate()
.filter(|(_, n)| n.len() == val)
.map(|(i, _)| i)
.collect()
}
pub fn is_empty(&self) -> bool {
self.neighbors.is_empty()
}
}
#[allow(dead_code)]
pub fn build_adjacency_list(vertex_count: usize, indices: &[u32]) -> AdjacencyList {
AdjacencyList::from_triangles(vertex_count, indices)
}
#[allow(dead_code)]
pub fn count_edges(adj: &AdjacencyList) -> usize {
adj.edge_count()
}
#[allow(dead_code)]
pub fn max_valence(adj: &AdjacencyList) -> usize {
adj.neighbors.iter().map(|n| n.len()).max().unwrap_or(0)
}
#[allow(dead_code)]
pub fn min_valence(adj: &AdjacencyList) -> usize {
adj.neighbors
.iter()
.filter(|n| !n.is_empty())
.map(|n| n.len())
.min()
.unwrap_or(0)
}
#[allow(dead_code)]
pub fn adjacency_to_json(adj: &AdjacencyList) -> String {
let mut s = String::from("{");
for (i, nbrs) in adj.neighbors.iter().enumerate() {
if i > 0 {
s.push(',');
}
s.push_str(&format!("\"{}\":[", i));
for (j, n) in nbrs.iter().enumerate() {
if j > 0 {
s.push(',');
}
s.push_str(&n.to_string());
}
s.push(']');
}
s.push('}');
s
}
#[cfg(test)]
mod tests {
use super::*;
fn triangle_indices() -> Vec<u32> {
vec![0, 1, 2]
}
fn quad_indices() -> Vec<u32> {
vec![0, 1, 2, 0, 2, 3]
}
#[test]
fn test_single_triangle() {
let adj = build_adjacency_list(3, &triangle_indices());
assert_eq!(adj.vertex_count(), 3);
assert_eq!(adj.valence(0), 2);
assert_eq!(adj.valence(1), 2);
assert_eq!(adj.valence(2), 2);
}
#[test]
fn test_edge_count_triangle() {
let adj = build_adjacency_list(3, &triangle_indices());
assert_eq!(count_edges(&adj), 3);
}
#[test]
fn test_neighbors() {
let adj = build_adjacency_list(3, &triangle_indices());
assert!(adj.are_neighbors(0, 1));
assert!(adj.are_neighbors(1, 2));
assert!(adj.are_neighbors(0, 2));
}
#[test]
fn test_quad_valence() {
let adj = build_adjacency_list(4, &quad_indices());
assert_eq!(adj.valence(0), 3);
assert_eq!(adj.valence(2), 3);
}
#[test]
fn test_empty() {
let adj = build_adjacency_list(0, &[]);
assert!(adj.is_empty());
assert_eq!(adj.edge_count(), 0);
}
#[test]
fn test_average_valence() {
let adj = build_adjacency_list(3, &triangle_indices());
assert!((adj.average_valence() - 2.0).abs() < 1e-6);
}
#[test]
fn test_max_min_valence() {
let adj = build_adjacency_list(4, &quad_indices());
assert_eq!(max_valence(&adj), 3);
assert_eq!(min_valence(&adj), 2);
}
#[test]
fn test_vertices_with_valence() {
let adj = build_adjacency_list(3, &triangle_indices());
let v2 = adj.vertices_with_valence(2);
assert_eq!(v2.len(), 3);
}
#[test]
fn test_adjacency_to_json() {
let adj = build_adjacency_list(3, &triangle_indices());
let json = adjacency_to_json(&adj);
assert!(json.contains("\"0\":["));
}
#[test]
fn test_no_duplicate_neighbors() {
let indices = vec![0, 1, 2, 0, 1, 2];
let adj = build_adjacency_list(3, &indices);
assert_eq!(adj.valence(0), 2);
}
}