• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaidokert / heapless-graphs-rs / 15791628916

21 Jun 2025 03:28AM UTC coverage: 94.579% (-0.1%) from 94.725%
15791628916

Pull #12

github

web-flow
Merge af3ccbf83 into ce0622484
Pull Request #12: Rework node add/remove

194 of 263 new or added lines in 5 files covered. (73.76%)

6 existing lines in 1 file now uncovered.

6543 of 6918 relevant lines covered (94.58%)

38.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.55
/src/matrix/map_matrix.rs
1
use crate::{
2
    containers::maps::MapTrait,
3
    graph::{Graph, GraphError, GraphWithMutableNodes, NodeIndex},
4
};
5

6
/// A matrix-based graph representation that maps arbitrary node indices to matrix positions
7
///
8
/// This struct wraps a [`Matrix`](super::simple_matrix::Matrix) and provides a mapping
9
/// from arbitrary node indices to matrix row/column positions, allowing graphs with
10
/// non-contiguous or non-zero-based node indices.
11
pub struct MapMatrix<const N: usize, NI, EDGEVALUE, M, COLUMNS, ROW>
12
where
13
    NI: NodeIndex,
14
    ROW: AsRef<[Option<EDGEVALUE>]>,
15
    COLUMNS: AsRef<[ROW]>,
16
    M: MapTrait<NI, usize>,
17
{
18
    inner: super::simple_matrix::Matrix<N, EDGEVALUE, COLUMNS, ROW>,
19
    index_map: M,
20
    _phantom: core::marker::PhantomData<NI>,
21
}
22

23
impl<const N: usize, NI, EDGEVALUE, M, COLUMNS, ROW> MapMatrix<N, NI, EDGEVALUE, M, COLUMNS, ROW>
24
where
25
    NI: NodeIndex,
26
    ROW: AsRef<[Option<EDGEVALUE>]>,
27
    COLUMNS: AsRef<[ROW]>,
28
    M: MapTrait<NI, usize>,
29
{
30
    /// Creates a new MapMatrix with the given matrix data and index mapping
31
    ///
32
    /// The `matrix` parameter provides the adjacency matrix data, and `index_map`
33
    /// maps from the actual node indices to matrix indices (0..N).
34
    pub fn new(matrix: COLUMNS, index_map: M) -> Result<Self, GraphError<NI>> {
16✔
35
        for (node, idx) in index_map.iter() {
34✔
36
            if *idx >= N {
34✔
37
                return Err(GraphError::IndexOutOfBounds(*idx, *node));
×
38
            }
34✔
39
        }
40
        Ok(Self::new_unchecked(matrix, index_map))
16✔
41
    }
16✔
42

43
    /// Creates a new MapMatrix with the given matrix data and index mapping
44
    ///
45
    /// The `matrix` parameter provides the adjacency matrix data, and `index_map`
46
    /// maps from the actual node indices to matrix indices (0..N).
47
    pub fn new_unchecked(matrix: COLUMNS, index_map: M) -> Self {
16✔
48
        Self {
16✔
49
            inner: super::simple_matrix::Matrix::new(matrix),
16✔
50
            index_map,
16✔
51
            _phantom: Default::default(),
16✔
52
        }
16✔
53
    }
16✔
54
}
55

56
impl<const N: usize, NI, EDGEVALUE, M, COLUMNS, ROW> Graph<NI>
57
    for MapMatrix<N, NI, EDGEVALUE, M, COLUMNS, ROW>
58
where
59
    NI: NodeIndex,
60
    ROW: AsRef<[Option<EDGEVALUE>]>,
61
    COLUMNS: AsRef<[ROW]>,
62
    M: MapTrait<NI, usize>,
63
{
64
    type Error = GraphError<NI>;
65

66
    fn iter_nodes(&self) -> Result<impl Iterator<Item = NI>, Self::Error> {
9✔
67
        Ok(self.index_map.iter().map(|(&k, _)| k))
20✔
68
    }
9✔
69

70
    fn contains_node(&self, node: NI) -> Result<bool, Self::Error> {
3✔
71
        Ok(self.index_map.contains_key(&node))
3✔
72
    }
3✔
73

74
    /// Note: Uses linear search for backindexing matrix indices to NI, this is slow.
75
    ///
76
    /// TODO: Store back-index ?
77
    fn iter_edges(&self) -> Result<impl Iterator<Item = (NI, NI)>, Self::Error> {
4✔
78
        Ok(self
4✔
79
            .index_map
4✔
80
            .iter()
4✔
81
            .flat_map(move |(&from_node, &from_idx)| {
11✔
82
                self.index_map
11✔
83
                    .iter()
11✔
84
                    .filter_map(move |(&to_node, &to_idx)| {
31✔
85
                        if self.inner.get_edge_value(from_idx, to_idx).is_some() {
31✔
86
                            Some((from_node, to_node))
9✔
87
                        } else {
88
                            None
22✔
89
                        }
90
                    })
31✔
91
            }))
11✔
92
    }
4✔
93

94
    /// Note: Uses linear search for backindexing matrix indices to NI, this is slow.
95
    ///
96
    /// TODO: Store back-index ?
97
    fn outgoing_edges(&self, node: NI) -> Result<impl Iterator<Item = NI>, Self::Error> {
9✔
98
        // Fast direct lookup of matrix index for this node
9✔
99
        let matrix_idx = self.index_map.get(&node).copied();
9✔
100

9✔
101
        Ok(self
9✔
102
            .inner
9✔
103
            .outgoing_edges(matrix_idx.unwrap_or(usize::MAX))
9✔
104
            .map_err(|_| GraphError::Unexpected)?
9✔
105
            .filter(move |_| matrix_idx.is_some()) // Filter out everything if node doesn't exist
9✔
106
            .filter_map(move |target_idx| {
9✔
107
                self.index_map
8✔
108
                    .iter()
8✔
109
                    .find(|(_, &idx)| idx == target_idx)
12✔
110
                    .map(|(&node, _)| node)
8✔
111
            }))
9✔
112
    }
9✔
113

114
    /// Note: Uses linear search for backindexing matrix indices to NI, this is slow.
115
    ///
116
    /// TODO: Store back-index ?
117
    fn incoming_edges(&self, node: NI) -> Result<impl Iterator<Item = NI>, Self::Error> {
4✔
118
        // Fast direct lookup of matrix index for this node
4✔
119
        let matrix_idx = self.index_map.get(&node).copied();
4✔
120

4✔
121
        Ok(self
4✔
122
            .inner
4✔
123
            .incoming_edges(matrix_idx.unwrap_or(usize::MAX))
4✔
124
            .map_err(|_| GraphError::Unexpected)?
4✔
125
            .filter(move |_| matrix_idx.is_some()) // Filter out everything if node doesn't exist
5✔
126
            .filter_map(move |source_idx| {
5✔
127
                self.index_map
5✔
128
                    .iter()
5✔
129
                    .find(|(_, &idx)| idx == source_idx)
10✔
130
                    .map(|(&node, _)| node)
5✔
131
            }))
5✔
132
    }
4✔
133
}
134

135
impl<const N: usize, NI, EDGEVALUE, M, COLUMNS, ROW>
136
    crate::graph::GraphWithEdgeValues<NI, EDGEVALUE>
137
    for MapMatrix<N, NI, EDGEVALUE, M, COLUMNS, ROW>
138
where
139
    NI: NodeIndex,
140
    ROW: AsRef<[Option<EDGEVALUE>]>,
141
    COLUMNS: AsRef<[ROW]>,
142
    M: MapTrait<NI, usize>,
143
{
144
    fn iter_edge_values<'a>(
2✔
145
        &'a self,
2✔
146
    ) -> Result<impl Iterator<Item = (NI, NI, Option<&'a EDGEVALUE>)>, Self::Error>
2✔
147
    where
2✔
148
        EDGEVALUE: 'a,
2✔
149
    {
2✔
150
        Ok(self
2✔
151
            .index_map
2✔
152
            .iter()
2✔
153
            .flat_map(move |(&from_node, &from_idx)| {
6✔
154
                self.index_map
6✔
155
                    .iter()
6✔
156
                    .filter_map(move |(&to_node, &to_idx)| {
18✔
157
                        let value = self.inner.get_edge_value(from_idx, to_idx);
18✔
158
                        if value.is_some() {
18✔
159
                            Some((from_node, to_node, value))
12✔
160
                        } else {
161
                            None
6✔
162
                        }
163
                    })
18✔
164
            }))
6✔
165
    }
2✔
166
}
167

168
impl<const N: usize, NI, EDGEVALUE, M, COLUMNS, ROW> GraphWithMutableNodes<NI>
169
    for MapMatrix<N, NI, EDGEVALUE, M, COLUMNS, ROW>
170
where
171
    NI: NodeIndex,
172
    ROW: AsRef<[Option<EDGEVALUE>]>,
173
    COLUMNS: AsRef<[ROW]>,
174
    M: MapTrait<NI, usize>,
175
{
176
    fn add_node(&mut self, node: NI) -> Result<(), Self::Error> {
8✔
177
        // Check if node already exists
8✔
178
        if self.index_map.contains_key(&node) {
8✔
179
            return Err(GraphError::DuplicateNode(node));
1✔
180
        }
7✔
181

182
        // Find an unused matrix index using iterator approach
183
        let unused_index = (0..N)
7✔
184
            .find(|&idx| !self.index_map.iter().any(|(_, &used_idx)| used_idx == idx))
15✔
185
            .ok_or(GraphError::OutOfCapacity)?;
7✔
186

187
        // Insert the new node mapping
188
        self.index_map
6✔
189
            .insert(node, unused_index)
6✔
190
            .map_err(|_| GraphError::OutOfCapacity)?;
6✔
191

192
        Ok(())
6✔
193
    }
8✔
194

NEW
195
    fn remove_node(&mut self, node: NI) -> Result<(), Self::Error> {
×
NEW
196
        // Check if node exists
×
NEW
197
        if !self.index_map.contains_key(&node) {
×
NEW
198
            return Err(GraphError::NodeNotFound(node));
×
NEW
199
        }
×
NEW
200

×
NEW
201
        // Check if node has incoming edges
×
NEW
202
        if self.incoming_edges(node)?.next().is_some() {
×
NEW
203
            return Err(GraphError::NodeHasIncomingEdges(node));
×
NEW
204
        }
×
NEW
205

×
NEW
206
        // Remove the node mapping (matrix position becomes available for reuse)
×
NEW
207
        self.index_map.remove(&node);
×
NEW
208
        Ok(())
×
NEW
209
    }
×
210
}
211

212
#[cfg(test)]
213
mod tests {
214
    use super::*;
215
    use crate::containers::maps::staticdict::Dictionary;
216
    use crate::graph::GraphWithEdgeValues;
217
    use crate::tests::{collect, collect_sorted};
218

219
    #[test]
220
    fn test_graphval_iter_nodes() {
1✔
221
        let matrix = [
1✔
222
            [Some(1), Some(2), None],
1✔
223
            [None, Some(5), Some(6)],
1✔
224
            [Some(7), None, Some(9)],
1✔
225
        ];
1✔
226

1✔
227
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
228
        index_map.insert(100, 0).unwrap();
1✔
229
        index_map.insert(200, 1).unwrap();
1✔
230
        index_map.insert(300, 2).unwrap();
1✔
231

232
        type ValMatrix = MapMatrix<
233
            3,
234
            u32,
235
            i32,
236
            Dictionary<u32, usize, 3>,
237
            [[Option<i32>; 3]; 3],
238
            [Option<i32>; 3],
239
        >;
240
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
241

1✔
242
        // Test Graph iter_nodes (returns owned values)
1✔
243
        let mut nodes = [0u32; 8];
1✔
244
        let nodes_slice = collect(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
245
        assert_eq!(nodes_slice.len(), 3);
1✔
246

247
        // Check all expected nodes are present
248
        let mut found_100 = false;
1✔
249
        let mut found_200 = false;
1✔
250
        let mut found_300 = false;
1✔
251

252
        for &node in nodes_slice.iter() {
3✔
253
            match node {
3✔
254
                100 => found_100 = true,
1✔
255
                200 => found_200 = true,
1✔
256
                300 => found_300 = true,
1✔
257
                _ => panic!("Unexpected node: {}", node),
×
258
            }
259
        }
260

261
        assert!(found_100);
1✔
262
        assert!(found_200);
1✔
263
        assert!(found_300);
1✔
264
    }
1✔
265

266
    #[test]
267
    fn test_graphval_iter_edges() {
1✔
268
        let matrix = [
1✔
269
            [Some(1), Some(2), None],
1✔
270
            [None, Some(5), Some(6)],
1✔
271
            [Some(7), None, Some(9)],
1✔
272
        ];
1✔
273

1✔
274
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
275
        index_map.insert(10, 0).unwrap();
1✔
276
        index_map.insert(20, 1).unwrap();
1✔
277
        index_map.insert(30, 2).unwrap();
1✔
278

279
        type ValMatrix = MapMatrix<
280
            3,
281
            u32,
282
            i32,
283
            Dictionary<u32, usize, 3>,
284
            [[Option<i32>; 3]; 3],
285
            [Option<i32>; 3],
286
        >;
287
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
288

1✔
289
        // Test Graph iter_edges (returns owned values)
1✔
290
        let mut edges = [(0u32, 0u32); 16];
1✔
291
        let edges_slice = collect(map_matrix.iter_edges().unwrap(), &mut edges);
1✔
292
        assert_eq!(edges_slice.len(), 6); // 6 Some values in matrix, all mapped
1✔
293

294
        // Verify expected edges exist
295
        let expected_edges = [
1✔
296
            (10, 10), // (0,0) -> Some(1)
1✔
297
            (10, 20), // (0,1) -> Some(2)
1✔
298
            (20, 20), // (1,1) -> Some(5)
1✔
299
            (20, 30), // (1,2) -> Some(6)
1✔
300
            (30, 10), // (2,0) -> Some(7)
1✔
301
            (30, 30), // (2,2) -> Some(9)
1✔
302
        ];
1✔
303

304
        for &expected_edge in &expected_edges {
7✔
305
            assert!(
6✔
306
                edges_slice.contains(&expected_edge),
6✔
307
                "Expected edge {:?} not found",
×
308
                expected_edge
309
            );
310
        }
311
    }
1✔
312

313
    #[test]
314
    fn test_graphval_outgoing_edges() {
1✔
315
        let matrix = [
1✔
316
            [Some(1), Some(2), None], // 10 -> 10, 20
1✔
317
            [None, None, Some(6)],    // 20 -> 30
1✔
318
            [Some(7), None, None],    // 30 -> 10
1✔
319
        ];
1✔
320

1✔
321
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
322
        index_map.insert(10, 0).unwrap();
1✔
323
        index_map.insert(20, 1).unwrap();
1✔
324
        index_map.insert(30, 2).unwrap();
1✔
325

326
        type ValMatrix = MapMatrix<
327
            3,
328
            u32,
329
            i32,
330
            Dictionary<u32, usize, 3>,
331
            [[Option<i32>; 3]; 3],
332
            [Option<i32>; 3],
333
        >;
334
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
335

1✔
336
        // Test outgoing edges from node 10 (Graph version)
1✔
337
        let mut targets = [0u32; 8];
1✔
338
        let targets_slice = collect(map_matrix.outgoing_edges(10).unwrap(), &mut targets);
1✔
339
        assert_eq!(targets_slice.len(), 2);
1✔
340

341
        // Check both targets found
342
        let mut found_10 = false;
1✔
343
        let mut found_20 = false;
1✔
344
        for &target in targets_slice.iter() {
2✔
345
            match target {
2✔
346
                10 => found_10 = true,
1✔
347
                20 => found_20 = true,
1✔
348
                _ => panic!("Unexpected target: {}", target),
×
349
            }
350
        }
351
        assert!(found_10);
1✔
352
        assert!(found_20);
1✔
353

354
        // Test outgoing edges from node 20
355
        let mut targets = [0u32; 8];
1✔
356
        let targets_slice = collect(map_matrix.outgoing_edges(20).unwrap(), &mut targets);
1✔
357
        assert_eq!(targets_slice, &[30]);
1✔
358

359
        // Test outgoing edges from node 30
360
        let mut targets = [0u32; 8];
1✔
361
        let targets_slice = collect(map_matrix.outgoing_edges(30).unwrap(), &mut targets);
1✔
362
        assert_eq!(targets_slice, &[10]);
1✔
363
    }
1✔
364

365
    #[test]
366
    fn test_graphval_contains_node() {
1✔
367
        let matrix = [
1✔
368
            [Some(1), None, None],
1✔
369
            [None, None, None],
1✔
370
            [None, None, None],
1✔
371
        ];
1✔
372

1✔
373
        let mut index_map = Dictionary::<i32, usize, 3>::new();
1✔
374
        index_map.insert(42, 0).unwrap();
1✔
375
        index_map.insert(84, 1).unwrap();
1✔
376

377
        type ValMatrix = MapMatrix<
378
            3,
379
            i32,
380
            i32,
381
            Dictionary<i32, usize, 3>,
382
            [[Option<i32>; 3]; 3],
383
            [Option<i32>; 3],
384
        >;
385
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
386

1✔
387
        // Test Graph contains_node
1✔
388
        assert!(map_matrix.contains_node(42).unwrap());
1✔
389
        assert!(map_matrix.contains_node(84).unwrap());
1✔
390
        assert!(!map_matrix.contains_node(999).unwrap());
1✔
391
    }
1✔
392

393
    #[test]
394
    fn test_graphval_nonexistent_node_outgoing() {
1✔
395
        let matrix = [
1✔
396
            [Some(1), Some(2), None],
1✔
397
            [None, Some(5), Some(6)],
1✔
398
            [Some(7), None, Some(9)],
1✔
399
        ];
1✔
400

1✔
401
        let mut index_map = Dictionary::<i32, usize, 3>::new();
1✔
402
        index_map.insert(100, 0).unwrap();
1✔
403
        index_map.insert(200, 1).unwrap();
1✔
404
        index_map.insert(300, 2).unwrap();
1✔
405

406
        type ValMatrix = MapMatrix<
407
            3,
408
            i32,
409
            i32,
410
            Dictionary<i32, usize, 3>,
411
            [[Option<i32>; 3]; 3],
412
            [Option<i32>; 3],
413
        >;
414
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
415

1✔
416
        // Test outgoing edges for non-existent node should return empty iterator
1✔
417
        let outgoing_count = map_matrix.outgoing_edges(999).unwrap().count();
1✔
418
        assert_eq!(outgoing_count, 0);
1✔
419
    }
1✔
420

421
    #[test]
422
    fn test_graphval_with_char_nodes() {
1✔
423
        let matrix = [[Some(true), Some(false)], [None, Some(true)]];
1✔
424

1✔
425
        let mut index_map = Dictionary::<char, usize, 2>::new();
1✔
426
        index_map.insert('X', 0).unwrap();
1✔
427
        index_map.insert('Y', 1).unwrap();
1✔
428

429
        type CharMatrix = MapMatrix<
430
            2,
431
            char,
432
            bool,
433
            Dictionary<char, usize, 2>,
434
            [[Option<bool>; 2]; 2],
435
            [Option<bool>; 2],
436
        >;
437
        let map_matrix = CharMatrix::new(matrix, index_map).unwrap();
1✔
438

1✔
439
        // Test with char node types
1✔
440
        assert_eq!(map_matrix.iter_nodes().unwrap().count(), 2);
1✔
441
        assert_eq!(map_matrix.iter_edges().unwrap().count(), 3);
1✔
442

443
        // Test specific outgoing edges
444
        let x_outgoing = map_matrix.outgoing_edges('X').unwrap().count();
1✔
445
        assert_eq!(x_outgoing, 2); // X -> X, Y
1✔
446

447
        let y_outgoing = map_matrix.outgoing_edges('Y').unwrap().count();
1✔
448
        assert_eq!(y_outgoing, 1); // Y -> Y
1✔
449
    }
1✔
450

451
    #[test]
452
    fn test_graphval_empty_matrix() {
1✔
453
        let matrix = [[None, None, None], [None, None, None], [None, None, None]];
1✔
454

1✔
455
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
456
        index_map.insert(10, 0).unwrap();
1✔
457
        index_map.insert(20, 1).unwrap();
1✔
458
        index_map.insert(30, 2).unwrap();
1✔
459

460
        type ValMatrix = MapMatrix<
461
            3,
462
            u32,
463
            i32,
464
            Dictionary<u32, usize, 3>,
465
            [[Option<i32>; 3]; 3],
466
            [Option<i32>; 3],
467
        >;
468
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
469

1✔
470
        // Should have 3 nodes but no edges
1✔
471
        assert_eq!(map_matrix.iter_nodes().unwrap().count(), 3);
1✔
472
        assert_eq!(map_matrix.iter_edges().unwrap().count(), 0);
1✔
473
    }
1✔
474

475
    #[test]
476
    fn test_graphval_with_edge_values() {
1✔
477
        let matrix = [
1✔
478
            [Some(1), Some(2), None],
1✔
479
            [None, Some(5), Some(6)],
1✔
480
            [Some(7), None, Some(9)],
1✔
481
        ];
1✔
482

1✔
483
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
484
        index_map.insert(10, 0).unwrap();
1✔
485
        index_map.insert(20, 1).unwrap();
1✔
486
        index_map.insert(30, 2).unwrap();
1✔
487

488
        type ValMatrix = MapMatrix<
489
            3,
490
            u32,
491
            i32,
492
            Dictionary<u32, usize, 3>,
493
            [[Option<i32>; 3]; 3],
494
            [Option<i32>; 3],
495
        >;
496
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
497

1✔
498
        // Test iter_edge_values
1✔
499
        let mut edges_with_values = [(0u32, 0u32, 0i32); 16];
1✔
500
        let edges_slice = collect(
1✔
501
            map_matrix
1✔
502
                .iter_edge_values()
1✔
503
                .unwrap()
1✔
504
                .filter_map(|(src, dst, value_opt)| value_opt.map(|v| (src, dst, *v))),
6✔
505
            &mut edges_with_values,
1✔
506
        );
1✔
507
        assert_eq!(edges_slice.len(), 6); // 6 Some values in matrix
1✔
508

509
        // Verify expected edges with values exist
510
        let expected_edges = [
1✔
511
            (10, 10, 1), // (0,0) -> Some(1)
1✔
512
            (10, 20, 2), // (0,1) -> Some(2)
1✔
513
            (20, 20, 5), // (1,1) -> Some(5)
1✔
514
            (20, 30, 6), // (1,2) -> Some(6)
1✔
515
            (30, 10, 7), // (2,0) -> Some(7)
1✔
516
            (30, 30, 9), // (2,2) -> Some(9)
1✔
517
        ];
1✔
518

519
        for &expected_edge in &expected_edges {
7✔
520
            assert!(
6✔
521
                edges_slice.contains(&expected_edge),
6✔
522
                "Expected edge {:?} not found",
×
523
                expected_edge
524
            );
525
        }
526

527
        // Test outgoing_edge_values from node 10
528
        let mut outgoing = [(0u32, 0i32); 8];
1✔
529
        let outgoing_slice = collect(
1✔
530
            map_matrix
1✔
531
                .outgoing_edge_values(10)
1✔
532
                .unwrap()
1✔
533
                .filter_map(|(dst, value_opt)| value_opt.map(|v| (dst, *v))),
2✔
534
            &mut outgoing,
1✔
535
        );
1✔
536
        assert_eq!(outgoing_slice.len(), 2);
1✔
537
        assert!(outgoing_slice.contains(&(10, 1)));
1✔
538
        assert!(outgoing_slice.contains(&(20, 2)));
1✔
539
    }
1✔
540

541
    #[test]
542
    fn test_graphval_incoming_edges() {
1✔
543
        let matrix = [
1✔
544
            [Some(1), Some(2), None], // 10 -> 10, 20
1✔
545
            [Some(3), None, None],    // 20 -> 10
1✔
546
            [Some(4), Some(5), None], // 30 -> 10, 20
1✔
547
        ];
1✔
548

1✔
549
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
550
        index_map.insert(10, 0).unwrap();
1✔
551
        index_map.insert(20, 1).unwrap();
1✔
552
        index_map.insert(30, 2).unwrap();
1✔
553

554
        type ValMatrix = MapMatrix<
555
            3,
556
            u32,
557
            i32,
558
            Dictionary<u32, usize, 3>,
559
            [[Option<i32>; 3]; 3],
560
            [Option<i32>; 3],
561
        >;
562
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
563

1✔
564
        // Test incoming edges to node 10 (should be from 10, 20, 30)
1✔
565
        let mut sources = [0u32; 8];
1✔
566
        let sources_slice = collect(map_matrix.incoming_edges(10).unwrap(), &mut sources);
1✔
567
        assert_eq!(sources_slice.len(), 3);
1✔
568
        assert!(sources_slice.contains(&10));
1✔
569
        assert!(sources_slice.contains(&20));
1✔
570
        assert!(sources_slice.contains(&30));
1✔
571

572
        // Test incoming edges to node 20 (should be from 10, 30)
573
        let mut sources = [0u32; 8];
1✔
574
        let sources_slice = collect(map_matrix.incoming_edges(20).unwrap(), &mut sources);
1✔
575
        assert_eq!(sources_slice.len(), 2);
1✔
576
        assert!(sources_slice.contains(&10));
1✔
577
        assert!(sources_slice.contains(&30));
1✔
578

579
        // Test incoming edges to node 30 (should be empty)
580
        let incoming_count = map_matrix.incoming_edges(30).unwrap().count();
1✔
581
        assert_eq!(incoming_count, 0);
1✔
582
    }
1✔
583

584
    #[test]
585
    fn test_graphval_incoming_edges_nonexistent_node() {
1✔
586
        let matrix = [
1✔
587
            [Some(1), Some(2), None],
1✔
588
            [None, Some(5), Some(6)],
1✔
589
            [Some(7), None, Some(9)],
1✔
590
        ];
1✔
591

1✔
592
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
593
        index_map.insert(10, 0).unwrap();
1✔
594
        index_map.insert(20, 1).unwrap();
1✔
595
        index_map.insert(30, 2).unwrap();
1✔
596

597
        type ValMatrix = MapMatrix<
598
            3,
599
            u32,
600
            i32,
601
            Dictionary<u32, usize, 3>,
602
            [[Option<i32>; 3]; 3],
603
            [Option<i32>; 3],
604
        >;
605
        let map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
606

1✔
607
        // Test incoming edges for non-existent node should return empty iterator
1✔
608
        let incoming_count = map_matrix.incoming_edges(999).unwrap().count();
1✔
609
        assert_eq!(incoming_count, 0);
1✔
610
    }
1✔
611

612
    #[test]
613
    fn test_add_node_to_empty_matrix() {
1✔
614
        let matrix = [[None, None, None], [None, None, None], [None, None, None]];
1✔
615
        let index_map = Dictionary::<u32, usize, 5>::new();
1✔
616

617
        type ValMatrix = MapMatrix<
618
            3,
619
            u32,
620
            i32,
621
            Dictionary<u32, usize, 5>,
622
            [[Option<i32>; 3]; 3],
623
            [Option<i32>; 3],
624
        >;
625
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
626

1✔
627
        map_matrix.add_node(42).unwrap();
1✔
628

1✔
629
        let mut nodes = [0u32; 2];
1✔
630
        let nodes_slice = collect(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
631
        assert_eq!(nodes_slice, &[42]);
1✔
632
        assert_eq!(map_matrix.outgoing_edges(42).unwrap().count(), 0);
1✔
633
    }
1✔
634

635
    #[test]
636
    fn test_add_node_to_existing_matrix() {
1✔
637
        let matrix = [
1✔
638
            [Some(1), None, None],
1✔
639
            [None, None, None],
1✔
640
            [None, None, None],
1✔
641
        ];
1✔
642
        let mut index_map = Dictionary::<u32, usize, 5>::new();
1✔
643
        index_map.insert(10, 0).unwrap();
1✔
644

645
        type ValMatrix = MapMatrix<
646
            3,
647
            u32,
648
            i32,
649
            Dictionary<u32, usize, 5>,
650
            [[Option<i32>; 3]; 3],
651
            [Option<i32>; 3],
652
        >;
653
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
654

1✔
655
        map_matrix.add_node(20).unwrap();
1✔
656

1✔
657
        let mut nodes = [0u32; 4];
1✔
658
        let nodes_slice = collect_sorted(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
659
        assert_eq!(nodes_slice, &[10, 20]);
1✔
660
        assert_eq!(map_matrix.outgoing_edges(20).unwrap().count(), 0);
1✔
661
        assert_eq!(map_matrix.outgoing_edges(10).unwrap().count(), 1);
1✔
662
    }
1✔
663

664
    #[test]
665
    fn test_add_duplicate_node() {
1✔
666
        let matrix = [[None, None, None], [None, None, None], [None, None, None]];
1✔
667
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
668
        index_map.insert(10, 0).unwrap();
1✔
669

670
        type ValMatrix = MapMatrix<
671
            3,
672
            u32,
673
            i32,
674
            Dictionary<u32, usize, 3>,
675
            [[Option<i32>; 3]; 3],
676
            [Option<i32>; 3],
677
        >;
678
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
679

1✔
680
        let result = map_matrix.add_node(10);
1✔
681
        assert!(matches!(result, Err(GraphError::DuplicateNode(10))));
1✔
682

683
        let mut nodes = [0u32; 2];
1✔
684
        let nodes_slice = collect(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
685
        assert_eq!(nodes_slice, &[10]);
1✔
686
    }
1✔
687

688
    #[test]
689
    fn test_add_node_capacity_exceeded() {
1✔
690
        let matrix = [[None, None], [None, None]];
1✔
691
        let mut index_map = Dictionary::<u32, usize, 3>::new();
1✔
692
        index_map.insert(10, 0).unwrap();
1✔
693
        index_map.insert(20, 1).unwrap();
1✔
694

695
        type ValMatrix = MapMatrix<
696
            2,
697
            u32,
698
            i32,
699
            Dictionary<u32, usize, 3>,
700
            [[Option<i32>; 2]; 2],
701
            [Option<i32>; 2],
702
        >;
703
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
704

1✔
705
        let result = map_matrix.add_node(30);
1✔
706
        assert!(matches!(result, Err(GraphError::OutOfCapacity)));
1✔
707

708
        let mut nodes = [0u32; 4];
1✔
709
        let nodes_slice = collect_sorted(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
710
        assert_eq!(nodes_slice, &[10, 20]);
1✔
711
    }
1✔
712

713
    #[test]
714
    fn test_add_multiple_nodes() {
1✔
715
        let matrix = [
1✔
716
            [None, None, None, None],
1✔
717
            [None, None, None, None],
1✔
718
            [None, None, None, None],
1✔
719
            [None, None, None, None],
1✔
720
        ];
1✔
721
        let index_map = Dictionary::<u32, usize, 10>::new();
1✔
722

723
        type ValMatrix = MapMatrix<
724
            4,
725
            u32,
726
            i32,
727
            Dictionary<u32, usize, 10>,
728
            [[Option<i32>; 4]; 4],
729
            [Option<i32>; 4],
730
        >;
731
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
732

1✔
733
        map_matrix.add_node(100).unwrap();
1✔
734
        map_matrix.add_node(200).unwrap();
1✔
735
        map_matrix.add_node(300).unwrap();
1✔
736

1✔
737
        let mut nodes = [0u32; 4];
1✔
738
        let nodes_slice = collect_sorted(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
739
        assert_eq!(nodes_slice, &[100, 200, 300]);
1✔
740
        assert_eq!(map_matrix.iter_edges().unwrap().count(), 0);
1✔
741
    }
1✔
742

743
    #[test]
744
    fn test_add_node_fills_gaps() {
1✔
745
        let matrix = [[None, None, None], [None, None, None], [None, None, None]];
1✔
746
        let mut index_map = Dictionary::<u32, usize, 5>::new();
1✔
747
        index_map.insert(10, 0).unwrap();
1✔
748
        index_map.insert(30, 2).unwrap();
1✔
749

750
        type ValMatrix = MapMatrix<
751
            3,
752
            u32,
753
            i32,
754
            Dictionary<u32, usize, 5>,
755
            [[Option<i32>; 3]; 3],
756
            [Option<i32>; 3],
757
        >;
758
        let mut map_matrix = ValMatrix::new(matrix, index_map).unwrap();
1✔
759

1✔
760
        map_matrix.add_node(20).unwrap();
1✔
761

1✔
762
        let mut nodes = [0u32; 4];
1✔
763
        let nodes_slice = collect_sorted(map_matrix.iter_nodes().unwrap(), &mut nodes);
1✔
764
        assert_eq!(nodes_slice, &[10, 20, 30]);
1✔
765
    }
1✔
766
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc