toolbox_rs/
complete_graph.rs

1use std::fmt;
2use std::ops::{Index, IndexMut};
3
4/// A complete graph implementation where distances between nodes are stored in a
5/// matrix including diagonal elements with zero distances.
6///
7/// This structure efficiently stores pairwise distances between all nodes in a
8/// complete graph.
9pub struct CompleteGraph<T> {
10    /// Number of nodes in the graph
11    num_nodes: usize,
12    /// Vector to store the distances between nodes
13    /// Includes diagonal elements (i,i) which are set to the zero value of T
14    distances: Vec<T>,
15}
16
17impl<T: Clone + Default + PartialEq + fmt::Debug> CompleteGraph<T> {
18    /// Creates a new complete graph with the specified number of nodes.
19    ///
20    /// # Arguments
21    ///
22    /// * `num_nodes` - The number of nodes in the complete graph
23    ///
24    /// # Returns
25    ///
26    /// A new CompleteGraph with default values for all distances and zero for diagonal
27    pub fn new(num_nodes: usize) -> Self {
28        // For a complete graph with n nodes, we need n*n entries to store all distances
29        // This includes the diagonal elements (i,i)
30        let size = num_nodes * num_nodes;
31        Self {
32            num_nodes,
33            distances: vec![T::default(); size],
34        }
35    }
36
37    /// Creates a new complete graph from a vector of distances.
38    ///
39    /// # Arguments
40    ///
41    /// * `num_nodes` - The number of nodes in the complete graph
42    /// * `distances` - A vector containing all pairwise distances in triangular format
43    /// * `zero` - The value to use for diagonal elements
44    ///
45    /// # Returns
46    ///
47    /// A new CompleteGraph with distances from the provided vector
48    ///
49    /// # Panics
50    ///
51    /// Panics if the vector size doesn't match the expected size for num_nodes
52    pub fn from_vec(num_nodes: usize, distances: Vec<T>) -> Self {
53        let expected_size = num_nodes * num_nodes;
54        assert_eq!(
55            distances.len(),
56            expected_size,
57            "Vector length {} doesn't match expected size {} for {} nodes. For a complete graph with n nodes including diagonal elements, we need n*n distances.",
58            distances.len(),
59            expected_size,
60            num_nodes
61        );
62
63        Self {
64            num_nodes,
65            distances,
66        }
67    }
68
69    /// Returns the number of nodes in the graph.
70    pub fn num_nodes(&self) -> usize {
71        self.num_nodes
72    }
73
74    /// Converts a pair of node indices to a flat array index.
75    ///
76    /// # Arguments
77    ///
78    /// * `i` - First node index
79    /// * `j` - Second node index
80    ///
81    /// # Returns
82    ///
83    /// The flat array index
84    #[inline(always)]
85    fn get_index(&self, i: usize, j: usize) -> usize {
86        i * self.num_nodes + j
87    }
88
89    /// Gets a reference to the distance between two nodes.
90    ///
91    /// # Arguments
92    ///
93    /// * `i` - First node index
94    /// * `j` - Second node index
95    ///
96    /// # Returns
97    ///
98    /// A reference to the distance between nodes i and j
99    pub fn get(&self, i: usize, j: usize) -> &T {
100        debug_assert!(
101            i < self.num_nodes && j < self.num_nodes,
102            "Node indices out of bounds"
103        );
104
105        let idx = self.get_index(i, j);
106        &self.distances[idx]
107    }
108
109    /// Gets a mutable reference to the distance between two nodes.
110    ///
111    /// # Arguments
112    ///
113    /// * `i` - First node index
114    /// * `j` - Second node index
115    ///
116    /// # Returns
117    ///
118    /// A mutable reference to the distance between nodes i and j
119    pub fn get_mut(&mut self, i: usize, j: usize) -> &mut T {
120        debug_assert!(
121            i < self.num_nodes && j < self.num_nodes,
122            "Node indices out of bounds"
123        );
124
125        let idx = self.get_index(i, j);
126        &mut self.distances[idx]
127    }
128}
129
130// Implement Index and IndexMut to allow graph[i, j] syntax
131impl<T: Clone + Default + PartialEq + fmt::Debug> Index<(usize, usize)> for CompleteGraph<T> {
132    type Output = T;
133
134    fn index(&self, index: (usize, usize)) -> &Self::Output {
135        self.get(index.0, index.1)
136    }
137}
138
139impl<T: Clone + Default + PartialEq + fmt::Debug> IndexMut<(usize, usize)> for CompleteGraph<T> {
140    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
141        if index.0 == index.1 {
142            panic!("Cannot modify diagonal");
143        }
144        // If the index is not diagonal, return a mutable reference
145        self.get_mut(index.0, index.1)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_complete_graph_creation() {
155        let graph: CompleteGraph<f32> = CompleteGraph::new(5);
156        assert_eq!(graph.num_nodes(), 5);
157    }
158
159    #[test]
160    fn test_diagonal_elements() {
161        let graph: CompleteGraph<i32> = CompleteGraph::new(4);
162
163        // Check diagonal elements are zero
164        assert_eq!(graph[(0, 0)], 0);
165        assert_eq!(graph[(1, 1)], 0);
166        assert_eq!(graph[(2, 2)], 0);
167        assert_eq!(graph[(3, 3)], 0);
168    }
169
170    #[test]
171    fn test_get_set_distance() {
172        let mut graph: CompleteGraph<i32> = CompleteGraph::new(4);
173
174        // Set some distances
175        *graph.get_mut(0, 1) = 10;
176        *graph.get_mut(1, 2) = 20;
177        *graph.get_mut(2, 3) = 30;
178
179        // Check distances
180        assert_eq!(*graph.get(0, 1), 10);
181        assert_eq!(*graph.get(1, 2), 20);
182        assert_eq!(*graph.get(2, 3), 30);
183
184        // Check diagonal elements
185        assert_eq!(*graph.get(0, 0), 0);
186        assert_eq!(*graph.get(1, 1), 0);
187    }
188
189    #[test]
190    fn test_index_syntax() {
191        let mut graph: CompleteGraph<i32> = CompleteGraph::new(4);
192
193        // Set values using the index syntax
194        graph[(0, 1)] = 10;
195        graph[(1, 2)] = 20;
196
197        // Check values using the index syntax
198        assert_eq!(graph[(0, 1)], 10);
199        assert_eq!(graph[(1, 2)], 20);
200        assert_eq!(graph[(0, 0)], 0);
201    }
202
203    #[test]
204    fn test_from_vec_constructor() {
205        // For 4 nodes with diagonal, we need 10 distances:
206        // (0,0), (0,1), (0,2), (0,3), (1,1), (1,2), (1,3), (2,2), (2,3), (3,3)
207        let distances = &[0, 10, 20, 30, 0, 40, 50, 60, 0];
208        let graph = CompleteGraph::from_vec(3, distances.to_vec());
209
210        assert_eq!(graph.num_nodes(), 3);
211        assert_eq!(graph[(0, 0)], 0);
212        assert_eq!(graph[(0, 1)], 10);
213        assert_eq!(graph[(0, 2)], 20);
214        assert_eq!(graph[(1, 0)], 30);
215        assert_eq!(graph[(1, 1)], 0);
216        assert_eq!(graph[(1, 2)], 40);
217        assert_eq!(graph[(2, 0)], 50);
218        assert_eq!(graph[(2, 1)], 60);
219        assert_eq!(graph[(2, 2)], 0);
220    }
221
222    #[test]
223    #[should_panic(expected = "Cannot modify diagonal")]
224    fn test_modify_diagonal() {
225        let mut graph: CompleteGraph<i32> = CompleteGraph::new(4);
226        graph[(0, 0)] = 5; // Should panic
227    }
228
229    #[test]
230    #[should_panic(expected = "Vector length")]
231    fn test_from_vec_wrong_size() {
232        // For 4 nodes with diagonal, we need 10 distances, not 9
233        let distances = vec![0, 10, 20, 30, 0, 40, 50, 0, 60];
234        let _graph = CompleteGraph::<i32>::from_vec(4, distances);
235    }
236
237    #[test]
238    #[should_panic]
239    fn test_get_index_out_of_bounds() {
240        let graph: CompleteGraph<i32> = CompleteGraph::new(3);
241
242        // This should trigger the out-of-bounds check when we try to access an element
243        // The debug_assert! will only panic in debug mode, but in release mode,
244        // we will get a panic from the vector access itself
245        let _ = graph[(3, 0)]; // Index 3 is out of bounds for a 3-node graph
246    }
247
248    #[test]
249    #[should_panic]
250    fn test_get_mut_index_out_of_bounds() {
251        let mut graph: CompleteGraph<i32> = CompleteGraph::new(3);
252
253        // This should trigger the out-of-bounds check when we try to access an element mutably
254        // The debug_assert! will only panic in debug mode, but in release mode,
255        // we will get a panic from the vector access itself
256        *graph.get_mut(0, 4) = 10; // Index 4 is out of bounds for a 3-node graph
257    }
258}