1use std::ops::Range;
7
8#[derive(Default)]
12struct Csr {
13 offset: Vec<u32>,
16 len: Vec<u32>,
18 capacity: Vec<u32>,
20 neighbors: Vec<u32>,
22 edges: Vec<u32>,
24 stale_count: usize,
26}
27
28impl Csr {
29 const MIN_ROW: usize = 4;
31
32 const REBUILD_SLACK: usize = 4;
34
35 fn with_rows(n: usize) -> Self {
37 Self {
38 offset: vec![0; n],
39 len: vec![0; n],
40 capacity: vec![0; n],
41 ..Self::default()
42 }
43 }
44
45 #[inline]
47 fn span(&self, i: u32) -> Range<usize> {
48 let start = self.offset[i as usize] as usize;
49 start..start + self.len[i as usize] as usize
50 }
51
52 #[inline]
54 fn row(&self, i: u32) -> &[u32] {
55 &self.neighbors[self.span(i)]
56 }
57
58 #[inline]
60 fn row_edges(&self, i: u32) -> &[u32] {
61 &self.edges[self.span(i)]
62 }
63
64 fn push_row(&mut self) {
69 self.offset.push(0);
70 self.len.push(0);
71 self.capacity.push(0);
72 }
73
74 fn relocate(&mut self, i: u32) {
76 let span = self.span(i);
77 let i = i as usize;
78 let new_capacity = (self.capacity[i] as usize * 2).max(Self::MIN_ROW);
79 let new_offset = self.neighbors.len();
80
81 self.neighbors.extend_from_within(span.clone());
82 self.edges.extend_from_within(span);
83 self.neighbors.resize(new_offset + new_capacity, 0);
84 self.edges.resize(new_offset + new_capacity, 0);
85
86 self.stale_count += self.capacity[i] as usize;
87 self.offset[i] = new_offset as u32;
88 self.capacity[i] = new_capacity as u32;
89 }
90
91 fn push(&mut self, i: u32, neighbor: u32, edge: u32) {
93 if self.len[i as usize] == self.capacity[i as usize] {
94 self.relocate(i);
95 }
96 let offset = self.offset[i as usize] as usize + self.len[i as usize] as usize;
97 self.neighbors[offset] = neighbor;
98 self.edges[offset] = edge;
99 self.len[i as usize] += 1;
100 }
101
102 fn remove(&mut self, i: u32, edge: u32) -> bool {
106 let Range { start, end } = self.span(i);
107 let Some(offset) = self.edges[start..end].iter().position(|&e| e == edge) else {
108 return false;
109 };
110 let offset = start + offset;
111 self.neighbors[offset] = self.neighbors[end - 1];
112 self.edges[offset] = self.edges[end - 1];
113 self.len[i as usize] -= 1;
114 true
115 }
116
117 fn renumber_edge(&mut self, i: u32, old: u32, new: u32) {
119 let span = self.span(i);
120 if let Some(slot) = self.edges[span].iter_mut().find(|e| **e == old) {
121 *slot = new;
122 }
123 }
124
125 fn clear_row(&mut self, i: u32) {
127 self.stale_count += self.capacity[i as usize] as usize;
128 self.offset[i as usize] = 0;
129 self.len[i as usize] = 0;
130 self.capacity[i as usize] = 0;
131 }
132
133 fn build(&mut self, n: usize, entries: impl Iterator<Item = (u32, u32, u32)> + Clone) {
137 self.offset.clear();
138 self.offset.resize(n, 0);
139 self.len.clear();
140 self.len.resize(n, 0);
141 self.capacity.clear();
142 self.capacity.resize(n, 0);
143
144 for (row, _, _) in entries.clone() {
145 self.len[row as usize] += 1;
146 }
147
148 let mut total = 0;
149 for i in 0..n {
150 let new_offset = total;
151 let new_capacity = Self::packed_capacity(self.len[i] as usize);
152 self.capacity[i] = new_capacity as u32;
153 self.offset[i] = new_offset as u32;
154 total += new_capacity;
155 }
156
157 self.len.fill(0);
158 self.neighbors.clear();
159 self.neighbors.resize(total, 0);
160 self.edges.clear();
161 self.edges.resize(total, 0);
162 self.stale_count = 0;
163
164 for (row, neighbor, edge) in entries {
165 let i = row as usize;
166 let offset = self.offset[i] as usize + self.len[i] as usize;
167 self.neighbors[offset] = neighbor;
168 self.edges[offset] = edge;
169 self.len[i] += 1;
170 }
171 }
172
173 fn repack(&mut self) {
178 let n = self.len.len();
179 let total: usize = self.len.iter().map(|&len| Self::packed_capacity(len as usize)).sum();
180 let mut neighbors = Vec::with_capacity(total);
181 let mut edges = Vec::with_capacity(total);
182 for i in 0..n {
183 let span = self.span(i as u32);
184 let new_offset = neighbors.len();
185 let new_capacity = Self::packed_capacity(span.len());
186 neighbors.extend_from_slice(&self.neighbors[span.clone()]);
187 edges.extend_from_slice(&self.edges[span]);
188 neighbors.resize(new_offset + new_capacity, 0);
189 edges.resize(new_offset + new_capacity, 0);
190 self.offset[i] = new_offset as u32;
191 self.capacity[i] = new_capacity as u32;
192 }
193 self.neighbors = neighbors;
194 self.edges = edges;
195 self.stale_count = 0;
196 }
197
198 fn packed_capacity(len: usize) -> usize {
202 (len + len / Self::REBUILD_SLACK).max(Self::MIN_ROW)
203 }
204
205 fn clear(&mut self) {
207 self.offset.clear();
208 self.len.clear();
209 self.capacity.clear();
210 self.neighbors.clear();
211 self.edges.clear();
212 self.stale_count = 0;
213 }
214
215 fn heap_bytes(&self) -> usize {
216 (self.offset.capacity()
217 + self.len.capacity()
218 + self.capacity.capacity()
219 + self.neighbors.capacity()
220 + self.edges.capacity())
221 * size_of::<u32>()
222 }
223}
224
225pub struct Network {
229 occupied: Vec<bool>,
230 free: Vec<u32>,
232 node_count: usize,
233
234 src: Vec<u32>,
235 dst: Vec<u32>,
236 color: Vec<u8>,
237
238 in_csr: Csr,
240 out_csr: Csr,
242
243 directed: bool,
244
245 version: u64,
247}
248
249impl std::fmt::Debug for Network {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 f.debug_struct("Network")
253 .field("node_count", &self.node_count)
254 .field("slot_count", &self.slot_count())
255 .field("edge_count", &self.edge_count())
256 .field("directed", &self.directed)
257 .field("version", &self.version)
258 .finish_non_exhaustive()
259 }
260}
261
262impl Network {
263 pub fn new(nodes: usize, directed: bool) -> Self {
265 Self {
266 occupied: vec![true; nodes],
267 free: Vec::new(),
268 node_count: nodes,
269 src: Vec::new(),
270 dst: Vec::new(),
271 color: Vec::new(),
272 in_csr: Csr::with_rows(nodes),
273 out_csr: Csr::with_rows(if directed { nodes } else { 0 }),
274 directed,
275 version: 0,
276 }
277 }
278
279 pub fn slot_count(&self) -> usize {
281 self.occupied.len()
282 }
283
284 pub fn node_count(&self) -> usize {
286 self.node_count
287 }
288
289 pub fn contains_node(&self, i: u32) -> bool {
291 self.occupied.get(i as usize).copied().unwrap_or(false)
292 }
293
294 pub fn directed(&self) -> bool {
296 self.directed
297 }
298
299 pub fn version(&self) -> u64 {
301 self.version
302 }
303
304 #[doc(hidden)]
306 pub fn spawn(&mut self) -> u32 {
307 self.node_count += 1;
308 if let Some(i) = self.free.pop() {
309 self.occupied[i as usize] = true;
310 return i;
311 }
312 let i = self.occupied.len() as u32;
313 self.occupied.push(true);
314 self.in_csr.push_row();
315 if self.directed {
316 self.out_csr.push_row();
317 }
318 i
319 }
320
321 #[doc(hidden)]
323 pub fn retire(&mut self, i: u32) {
324 if !self.contains_node(i) {
325 return;
326 }
327
328 let mut doomed: Vec<u32> = self.in_csr.row_edges(i).to_vec();
331 if self.directed {
332 doomed.extend_from_slice(self.out_csr.row_edges(i));
333 }
334 doomed.sort_unstable();
335 doomed.dedup();
336 for &e in doomed.iter().rev() {
337 self.remove_edge(e);
338 }
339
340 self.in_csr.clear_row(i);
341 if self.directed {
342 self.out_csr.clear_row(i);
343 }
344 self.occupied[i as usize] = false;
345 self.node_count -= 1;
346 self.free.push(i);
347 self.version += 1;
348 }
349
350 pub fn edge_count(&self) -> usize {
352 self.src.len()
353 }
354
355 pub fn edges(&self) -> (&[u32], &[u32], &[u8]) {
357 (&self.src, &self.dst, &self.color)
358 }
359
360 pub fn update_colors(&mut self, f: impl FnOnce(&[u32], &[u32], &mut [u8]) -> bool) {
365 if f(&self.src, &self.dst, &mut self.color) {
366 self.version += 1;
367 }
368 }
369
370 pub fn set_edge_color(&mut self, edge: u32, color: u8) {
372 self.color[edge as usize] = color;
373 self.version += 1;
374 }
375
376 pub fn add_edge(&mut self, a: u32, b: u32, color: u8) -> u32 {
384 debug_assert!(a != b, "a self loop has no meaning for either row set");
385 let e = self.src.len() as u32;
386 self.src.push(a);
387 self.dst.push(b);
388 self.color.push(color);
389 if self.directed {
390 self.in_csr.push(b, a, e);
391 self.out_csr.push(a, b, e);
392 } else {
393 self.in_csr.push(b, a, e);
394 self.in_csr.push(a, b, e);
395 }
396 self.version += 1;
397 e
398 }
399
400 pub fn remove_edge(&mut self, edge: u32) {
402 let e = edge as usize;
403 let (a, b) = (self.src[e], self.dst[e]);
404 if self.directed {
405 self.in_csr.remove(b, edge);
406 self.out_csr.remove(a, edge);
407 } else {
408 self.in_csr.remove(b, edge);
409 self.in_csr.remove(a, edge);
410 }
411
412 self.src.swap_remove(e);
413 self.dst.swap_remove(e);
414 self.color.swap_remove(e);
415
416 let moved = self.src.len() as u32;
418 if e < self.src.len() {
419 let (ma, mb) = (self.src[e], self.dst[e]);
420 if self.directed {
421 self.in_csr.renumber_edge(mb, moved, edge);
422 self.out_csr.renumber_edge(ma, moved, edge);
423 } else {
424 self.in_csr.renumber_edge(mb, moved, edge);
425 self.in_csr.renumber_edge(ma, moved, edge);
426 }
427 }
428 self.version += 1;
429 }
430
431 #[inline]
433 pub fn in_neighbors(&self, i: u32) -> &[u32] {
434 self.in_csr.row(i)
435 }
436
437 #[inline]
439 pub fn out_neighbors(&self, i: u32) -> &[u32] {
440 if self.directed {
441 self.out_csr.row(i)
442 } else {
443 self.in_csr.row(i)
444 }
445 }
446
447 #[inline]
449 pub fn degree(&self, i: u32) -> usize {
450 if self.directed {
451 self.in_csr.row(i).len() + self.out_csr.row(i).len()
452 } else {
453 self.in_csr.row(i).len()
454 }
455 }
456
457 #[inline]
462 pub fn edge_between(&self, a: u32, b: u32) -> Option<u32> {
463 let (csr, a, b) = if self.directed {
464 (&self.out_csr, a, b)
465 } else if self.degree(a) <= self.degree(b) {
466 (&self.in_csr, a, b)
467 } else {
468 (&self.in_csr, b, a)
469 };
470 let offset = csr.row(a).iter().position(|&n| n == b)?;
471 Some(csr.row_edges(a)[offset])
472 }
473
474 #[inline]
476 pub fn has_edge(&self, a: u32, b: u32) -> bool {
477 self.edge_between(a, b).is_some()
478 }
479
480 #[doc(hidden)]
481 pub fn set_directed(&mut self, directed: bool) {
482 if directed == self.directed {
483 return;
484 }
485 self.directed = directed;
486 self.rebuild();
487 self.version += 1;
488 }
489
490 #[doc(hidden)]
494 pub fn should_repack(&self) -> bool {
495 let stale = self.in_csr.stale_count + self.out_csr.stale_count;
496 let slots = self.in_csr.neighbors.len() + self.out_csr.neighbors.len();
497 stale > slots / 2 && stale > Csr::MIN_ROW
498 }
499
500 #[doc(hidden)]
504 pub fn repack(&mut self) {
505 self.in_csr.repack();
506 self.out_csr.repack();
507 }
508
509 #[doc(hidden)]
513 pub fn rebuild(&mut self) {
514 let n = self.occupied.len();
515 let (src, dst) = (&self.src, &self.dst);
516 let edges = || src.iter().zip(dst).enumerate().map(|(e, (&a, &b))| (a, b, e as u32));
518 if self.directed {
519 self.in_csr.build(n, edges().map(|(a, b, e)| (b, a, e)));
520 self.out_csr.build(n, edges());
521 } else {
522 self.in_csr
523 .build(n, edges().flat_map(|(a, b, e)| [(b, a, e), (a, b, e)]));
524 self.out_csr.clear();
525 }
526 }
527
528 pub fn heap_bytes(&self) -> usize {
530 self.occupied.capacity()
531 + self.free.capacity() * size_of::<u32>()
532 + (self.src.capacity() + self.dst.capacity()) * size_of::<u32>()
533 + self.color.capacity()
534 + self.in_csr.heap_bytes()
535 + self.out_csr.heap_bytes()
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::Network;
542 use crate::authoring::primitives::rng::{next_bits, xorshift64};
543
544 fn brute_force(net: &Network) -> (Vec<Vec<u32>>, Vec<Vec<u32>>) {
546 let n = net.slot_count();
547 let (src, dst, _) = net.edges();
548 let mut ins = vec![Vec::new(); n];
549 let mut outs = vec![Vec::new(); n];
550 for e in 0..src.len() {
551 let (a, b) = (src[e], dst[e]);
552 ins[b as usize].push(a);
553 if net.directed() {
554 outs[a as usize].push(b);
555 } else {
556 ins[a as usize].push(b);
557 }
558 }
559 if !net.directed() {
560 outs = ins.clone();
561 }
562 (ins, outs)
563 }
564
565 fn sorted(mut v: Vec<u32>) -> Vec<u32> {
566 v.sort_unstable();
567 v
568 }
569
570 fn assert_rows_match(net: &Network, what: &str) {
572 let (ins, outs) = brute_force(net);
573 for i in 0..net.slot_count() as u32 {
574 assert_eq!(
575 sorted(net.in_neighbors(i).to_vec()),
576 sorted(ins[i as usize].clone()),
577 "{what}: node {i} in-row disagrees with the edge list"
578 );
579 assert_eq!(
580 sorted(net.out_neighbors(i).to_vec()),
581 sorted(outs[i as usize].clone()),
582 "{what}: node {i} out-row disagrees with the edge list"
583 );
584 }
585 }
586
587 #[test]
588 fn an_undirected_edge_lands_in_both_rows() {
589 let mut net = Network::new(4, false);
590 net.add_edge(0, 3, 0);
591 assert_eq!(net.in_neighbors(0), &[3]);
592 assert_eq!(net.in_neighbors(3), &[0]);
593 assert_eq!(net.out_neighbors(0), net.in_neighbors(0), "one row answers both ways");
594 assert!(net.has_edge(0, 3) && net.has_edge(3, 0));
595 assert_eq!(net.degree(0), 1);
596 }
597
598 #[test]
599 fn a_directed_edge_lands_in_one_row_each_way() {
600 let mut net = Network::new(4, true);
601 net.add_edge(0, 3, 0);
602 assert_eq!(net.out_neighbors(0), &[3]);
603 assert!(net.in_neighbors(0).is_empty(), "0 has no edge into it");
604 assert_eq!(net.in_neighbors(3), &[0]);
605 assert!(net.out_neighbors(3).is_empty());
606 assert!(net.has_edge(0, 3), "the edge runs 0 to 3");
607 assert!(!net.has_edge(3, 0), "and not the other way");
608 assert_eq!(net.degree(0), 1);
609 }
610
611 #[test]
612 fn a_row_survives_the_relocation_its_growth_forces() {
613 let mut net = Network::new(40, false);
614 for b in 1..40u32 {
616 net.add_edge(0, b, 0);
617 }
618 assert_eq!(sorted(net.in_neighbors(0).to_vec()), (1..40).collect::<Vec<_>>());
619 assert_rows_match(&net, "after growth");
620 }
621
622 #[test]
623 fn removing_an_edge_renumbers_the_one_that_takes_its_place() {
624 let mut net = Network::new(6, false);
625 for (a, b) in [(0, 1), (2, 3), (4, 5)] {
626 net.add_edge(a, b, 0);
627 }
628 net.remove_edge(0);
630 assert_eq!(net.edge_count(), 2);
631 assert_rows_match(&net, "after a middle removal");
632 net.remove_edge(0);
633 assert_rows_match(&net, "after a second removal");
634 assert_eq!(net.edge_count(), 1);
635 }
636
637 #[test]
638 fn retiring_a_node_takes_every_edge_touching_it() {
639 let mut net = Network::new(6, false);
640 for b in [1, 2, 3, 4] {
641 net.add_edge(0, b, 0);
642 }
643 net.add_edge(1, 2, 0);
644 net.retire(0);
645
646 assert!(!net.contains_node(0));
647 assert_eq!(net.node_count(), 5);
648 assert_eq!(net.edge_count(), 1, "only the edge clear of node 0 is left");
649 assert_rows_match(&net, "after a retirement");
650 for i in 1..6u32 {
651 assert!(
652 !net.in_neighbors(i).contains(&0),
653 "node {i} still lists the retired node"
654 );
655 }
656 }
657
658 #[test]
659 fn retiring_a_node_takes_its_edges_when_directed_too() {
660 let mut net = Network::new(6, true);
661 net.add_edge(0, 1, 0);
662 net.add_edge(2, 0, 0);
663 net.add_edge(3, 4, 0);
664 net.retire(0);
665 assert_eq!(net.edge_count(), 1);
666 assert_rows_match(&net, "after a directed retirement");
667 }
668
669 #[test]
670 fn a_retired_slot_is_the_next_one_handed_out() {
671 let mut net = Network::new(3, false);
672 net.retire(1);
673 net.retire(2);
674 assert_eq!(net.spawn(), 2, "newest free slot first");
675 assert_eq!(net.spawn(), 1);
676 assert_eq!(net.spawn(), 3, "then a fresh one");
677 assert_eq!(net.slot_count(), 4);
678 assert_eq!(net.node_count(), 4);
679 }
680
681 #[test]
682 fn a_reused_slot_starts_with_no_edges() {
683 let mut net = Network::new(4, false);
684 net.add_edge(1, 2, 0);
685 net.add_edge(1, 3, 0);
686 net.retire(1);
687 let reused = net.spawn();
688 assert_eq!(reused, 1);
689 assert!(
690 net.in_neighbors(reused).is_empty(),
691 "the old row came back with the slot"
692 );
693 net.add_edge(reused, 2, 0);
694 assert_rows_match(&net, "after reuse");
695 }
696
697 #[test]
698 fn a_rebuild_leaves_the_same_graph() {
699 let mut net = Network::new(20, false);
700 let mut rng = 0x51A7_u64;
701 for _ in 0..60 {
702 let a = next_bits(&mut rng) % 20;
703 let b = next_bits(&mut rng) % 20;
704 if a != b && !net.has_edge(a, b) {
705 net.add_edge(a, b, 0);
706 }
707 }
708 let before: Vec<Vec<u32>> = (0..20).map(|i| sorted(net.in_neighbors(i).to_vec())).collect();
709 net.rebuild();
710 let after: Vec<Vec<u32>> = (0..20).map(|i| sorted(net.in_neighbors(i).to_vec())).collect();
711 assert_eq!(before, after, "a repack changed the neighbours");
712 assert_rows_match(&net, "after a rebuild");
713 }
714
715 #[test]
716 fn flipping_the_direction_rebuilds_both_row_sets() {
717 let mut net = Network::new(5, false);
718 net.add_edge(0, 1, 0);
719 net.add_edge(1, 2, 0);
720 net.set_directed(true);
721
722 assert!(net.directed());
723 assert_eq!(net.out_neighbors(0), &[1]);
724 assert!(net.in_neighbors(0).is_empty(), "0 has no edge into it once directed");
725 assert_rows_match(&net, "after a flip to directed");
726
727 net.set_directed(false);
728 assert_rows_match(&net, "and back");
729 assert_eq!(sorted(net.in_neighbors(1).to_vec()), vec![0, 2]);
730 }
731
732 #[test]
734 fn rows_track_the_edge_list_through_random_churn() {
735 for &directed in &[false, true] {
736 let mut net = Network::new(30, directed);
737 let mut rng = xorshift64(0xC0FF_EE01 ^ u64::from(directed));
738 for round in 0..400 {
739 match next_bits(&mut rng) % 10 {
740 0..=5 => {
741 let a = next_bits(&mut rng) % 30;
742 let b = next_bits(&mut rng) % 30;
743 if a != b && net.contains_node(a) && net.contains_node(b) && !net.has_edge(a, b) {
744 net.add_edge(a, b, 0);
745 }
746 }
747 6..=7 if net.edge_count() > 0 => {
748 let e = next_bits(&mut rng) % net.edge_count() as u32;
749 net.remove_edge(e);
750 }
751 8 => {
752 let i = next_bits(&mut rng) % net.slot_count() as u32;
753 net.retire(i);
754 }
755 _ => {
756 net.spawn();
757 }
758 }
759 assert_rows_match(&net, &format!("directed={directed} round={round}"));
760 }
761 assert!(
762 net.edge_count() > 0,
763 "the churn removed everything, so it proved little"
764 );
765 }
766 }
767
768 #[test]
769 fn edge_between_returns_the_index_in_the_edge_list() {
770 for directed in [false, true] {
771 let mut net = Network::new(6, directed);
772 let mut rng = 0xED6E_u64;
773 for _ in 0..20 {
774 let a = next_bits(&mut rng) % 6;
775 let b = next_bits(&mut rng) % 6;
776 if a != b && !net.has_edge(a, b) {
777 net.add_edge(a, b, 0);
778 }
779 }
780 net.remove_edge(0);
781 let (src, dst, _) = net.edges();
782 for a in 0..6u32 {
783 for b in 0..6u32 {
784 let listed = (0..src.len())
785 .find(|&e| (src[e], dst[e]) == (a, b) || (!directed && (src[e], dst[e]) == (b, a)));
786 assert_eq!(
787 net.edge_between(a, b),
788 listed.map(|e| e as u32),
789 "directed={directed} edge_between({a}, {b})"
790 );
791 }
792 }
793 }
794 }
795
796 #[test]
797 fn has_edge_agrees_with_the_edge_list() {
798 let mut net = Network::new(12, false);
799 let mut rng = 0xBEEF_u64;
800 for _ in 0..30 {
801 let a = next_bits(&mut rng) % 12;
802 let b = next_bits(&mut rng) % 12;
803 if a != b && !net.has_edge(a, b) {
804 net.add_edge(a, b, 0);
805 }
806 }
807 let (src, dst, _) = net.edges();
808 let joined: Vec<(u32, u32)> = src.iter().zip(dst).map(|(&a, &b)| (a, b)).collect();
809 for a in 0..12u32 {
810 for b in 0..12u32 {
811 let listed = joined.contains(&(a, b)) || joined.contains(&(b, a));
812 assert_eq!(net.has_edge(a, b), a != b && listed, "has_edge({a}, {b})");
813 }
814 }
815 }
816
817 #[test]
818 fn the_version_moves_for_every_change_the_view_can_see() {
819 let mut net = Network::new(4, false);
820 let start = net.version();
821 let e = net.add_edge(0, 1, 0);
822 assert!(net.version() > start, "an edge appeared");
823
824 let after_add = net.version();
825 net.set_edge_color(e, 3);
826 assert!(net.version() > after_add, "a colour changed");
827
828 let after_color = net.version();
829 net.remove_edge(e);
830 assert!(net.version() > after_color, "an edge went");
831
832 let after_remove = net.version();
833 net.set_directed(true);
834 assert!(net.version() > after_remove, "the direction changed");
835 }
836
837 #[test]
840 fn a_recolour_moves_the_version_only_when_it_changed_something() {
841 let mut net = Network::new(3, false);
842 net.add_edge(0, 1, 0);
843 net.add_edge(1, 2, 0);
844
845 let before = net.version();
846 net.update_colors(|_, _, color| {
847 color.fill(0);
848 false
849 });
850 assert_eq!(net.version(), before, "an unchanged recolour moved the version");
851
852 net.update_colors(|src, _, color| {
853 for (c, &a) in color.iter_mut().zip(src) {
854 *c = a as u8;
855 }
856 true
857 });
858 assert!(net.version() > before, "a real recolour left the version behind");
859 assert_eq!(net.edges().2, &[0, 1]);
860 }
861
862 #[test]
864 fn retiring_most_of_the_graph_asks_for_a_repack() {
865 let mut net = Network::new(40, false);
866 let mut rng = 0x9A5B_u64;
867 for _ in 0..250 {
868 let a = next_bits(&mut rng) % 40;
869 let b = next_bits(&mut rng) % 40;
870 if a != b && !net.has_edge(a, b) {
871 net.add_edge(a, b, 0);
872 }
873 }
874 assert!(!net.should_repack(), "growth alone should not ask for one");
875
876 for i in 0..38u32 {
877 net.retire(i);
878 }
879 assert!(net.should_repack(), "38 cleared rows left nothing to reclaim");
880
881 net.repack();
882 assert!(!net.should_repack(), "the repack did not reclaim it");
883 assert_rows_match(&net, "after the repack");
884 }
885
886 #[test]
888 fn a_repack_keeps_every_row_as_it_was() {
889 let mut net = Network::new(30, true);
890 let mut rng = 0x2E9A_u64;
891 for _ in 0..200 {
892 let a = next_bits(&mut rng) % 30;
893 let b = next_bits(&mut rng) % 30;
894 if a != b && !net.has_edge(a, b) {
895 net.add_edge(a, b, 0);
896 }
897 }
898 for e in 0..40 {
899 net.remove_edge(e);
900 }
901 net.retire(3);
902 let rows = |net: &Network| -> Vec<(Vec<u32>, Vec<u32>)> {
903 (0..30)
904 .map(|i| (net.in_neighbors(i).to_vec(), net.out_neighbors(i).to_vec()))
905 .collect()
906 };
907 let before = rows(&net);
908 net.repack();
909 assert_eq!(rows(&net), before, "a repack reordered or lost a row");
910 assert_rows_match(&net, "after a repack");
911
912 net.add_edge(3, 4, 0);
913 assert_rows_match(&net, "after a repack and an insert");
914 }
915}