1use yo_common::prefetch;
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum Dir {
76 Out,
78 In,
80}
81
82const CLASSES: usize = 96;
88
89const LADDER: [u32; CLASSES] = ladder();
91
92const LIVE: u8 = 1;
93const INCOMING: u8 = 2;
94
95const fn ladder() -> [u32; CLASSES] {
107 let mut out = [0u32; CLASSES];
108 let mut cap: u64 = 1;
109 let mut i = 0;
110 while i < CLASSES {
111 out[i] = if cap > u32::MAX as u64 {
112 u32::MAX
113 } else {
114 cap as u32
115 };
116 cap = if cap < 16 {
117 cap * 2
118 } else {
119 (cap + cap / 4 + 3) & !3
120 };
121 i += 1;
122 }
123 out
124}
125
126#[derive(Debug, Clone, Copy, Default)]
131struct Slot {
132 node: u64,
133 at: u32,
134 len: u32,
135 cap: u32,
136 label: u32,
137 flags: u8,
138}
139
140#[derive(Debug)]
156pub struct Adjacency {
157 slots: Vec<Slot>,
158 live: usize,
159 filled: usize,
160 edges: usize,
161 entries: usize,
162 neighbour: Vec<u64>,
163 edge: Vec<u32>,
164 free: Vec<Vec<u32>>,
165 both: bool,
166}
167
168impl Default for Adjacency {
169 fn default() -> Adjacency {
170 Adjacency::new()
171 }
172}
173
174impl Adjacency {
175 #[must_use]
178 pub fn new() -> Adjacency {
179 Adjacency::build(true)
180 }
181
182 #[must_use]
191 pub fn out_only() -> Adjacency {
192 Adjacency::build(false)
193 }
194
195 fn build(both: bool) -> Adjacency {
196 Adjacency {
197 slots: Vec::new(),
198 live: 0,
199 filled: 0,
200 edges: 0,
201 entries: 0,
202 neighbour: Vec::new(),
203 edge: Vec::new(),
204 free: Vec::new(),
205 both,
206 }
207 }
208
209 #[must_use]
211 pub fn indexes_incoming(&self) -> bool {
212 self.both
213 }
214
215 #[must_use]
217 pub fn edges(&self) -> usize {
218 self.edges
219 }
220
221 #[must_use]
223 pub fn is_empty(&self) -> bool {
224 self.edges == 0
225 }
226
227 #[must_use]
229 pub fn runs(&self) -> usize {
230 self.filled
231 }
232
233 pub fn link(&mut self, src: u64, dst: u64, label: u32, edge: u32) {
240 let s = self.run_for(src, label, 0);
241 self.push(s, dst, edge);
242 if self.both {
243 let d = self.run_for(dst, label, INCOMING);
244 self.push(d, src, edge);
245 }
246 self.edges += 1;
247 }
248
249 pub fn unlink(&mut self, src: u64, dst: u64, label: u32) -> Option<u32> {
257 let s = self.find(src, label, 0)?;
258 let i = self.position(s, dst)?;
259 let edge = self.take(s, i).1;
260 if self.both
261 && let Some(d) = self.find(dst, label, INCOMING)
262 && let Some(j) = self.position(d, src)
263 {
264 self.take(d, j);
265 }
266 self.edges -= 1;
267 Some(edge)
268 }
269
270 pub fn unlink_at(&mut self, node: u64, label: u32, dir: Dir, i: usize) -> Option<(u64, u32)> {
278 let s = self.find(node, label, incoming(dir))?;
279 if i >= self.slots[s].len as usize {
280 return None;
281 }
282 Some(self.take(s, i))
283 }
284
285 #[must_use]
291 pub fn neighbours(&self, node: u64, label: u32, dir: Dir) -> &[u64] {
292 match self.find(node, label, incoming(dir)) {
293 Some(s) => {
294 let (at, len) = (self.slots[s].at as usize, self.slots[s].len as usize);
295 &self.neighbour[at..at + len]
296 }
297 None => &[],
298 }
299 }
300
301 #[must_use]
304 pub fn edge_slots(&self, node: u64, label: u32, dir: Dir) -> &[u32] {
305 match self.find(node, label, incoming(dir)) {
306 Some(s) => {
307 let (at, len) = (self.slots[s].at as usize, self.slots[s].len as usize);
308 &self.edge[at..at + len]
309 }
310 None => &[],
311 }
312 }
313
314 #[must_use]
316 pub fn degree(&self, node: u64, label: u32, dir: Dir) -> usize {
317 match self.find(node, label, incoming(dir)) {
318 Some(s) => self.slots[s].len as usize,
319 None => 0,
320 }
321 }
322
323 pub fn for_each_run(&self, label: u32, dir: Dir, mut f: impl FnMut(u64, &[u64], &[u32])) {
334 let want = incoming(dir);
335 for s in &self.slots {
336 if s.flags & LIVE == 0 || s.len == 0 || s.label != label || s.flags & INCOMING != want {
337 continue;
338 }
339 let (at, len) = (s.at as usize, s.len as usize);
340 f(
341 s.node,
342 &self.neighbour[at..at + len],
343 &self.edge[at..at + len],
344 );
345 }
346 }
347
348 pub fn prefetch(&self, node: u64, label: u32, dir: Dir) {
357 if self.slots.is_empty() {
358 return;
359 }
360 let i = bucket(hash(node, label, incoming(dir)), self.slots.len());
361 prefetch(&self.slots[i]);
362 }
363
364 #[must_use]
367 pub fn bytes(&self) -> usize {
368 self.slots.capacity() * size_of::<Slot>()
369 + self.neighbour.capacity() * size_of::<u64>()
370 + self.edge.capacity() * size_of::<u32>()
371 + self.free.capacity() * size_of::<Vec<u32>>()
372 + self
373 .free
374 .iter()
375 .map(|f| f.capacity() * size_of::<u32>())
376 .sum::<usize>()
377 }
378
379 pub fn compact(&mut self) {
389 let mut keep: Vec<Slot> = self
390 .slots
391 .iter()
392 .copied()
393 .filter(|s| s.flags & LIVE != 0 && s.len > 0)
394 .collect();
395 let mut neighbour = Vec::with_capacity(self.entries);
396 let mut edge = Vec::with_capacity(self.entries);
397 for slot in &mut keep {
398 let (at, len) = (slot.at as usize, slot.len as usize);
399 let to = neighbour.len() as u32;
400 neighbour.extend_from_slice(&self.neighbour[at..at + len]);
401 edge.extend_from_slice(&self.edge[at..at + len]);
402 slot.at = to;
403 slot.cap = slot.len;
404 }
405 self.neighbour = neighbour;
406 self.edge = edge;
407 self.free = Vec::new();
408 self.live = keep.len();
409 self.filled = keep.len();
410 self.slots = vec![Slot::default(); (keep.len() * 4 / 3).max(16)];
415 for slot in keep {
416 let i = self.vacancy(slot.node, slot.label, slot.flags & INCOMING);
417 self.slots[i] = slot;
418 }
419 }
420
421 fn find(&self, node: u64, label: u32, incoming: u8) -> Option<usize> {
422 if self.slots.is_empty() {
423 return None;
424 }
425 let n = self.slots.len();
426 let mut i = bucket(hash(node, label, incoming), n);
427 loop {
428 let s = &self.slots[i];
429 if s.flags & LIVE == 0 {
430 return None;
431 }
432 if s.node == node && s.label == label && s.flags & INCOMING == incoming {
433 return Some(i);
434 }
435 i += 1;
436 if i == n {
437 i = 0;
438 }
439 }
440 }
441
442 fn position(&self, s: usize, node: u64) -> Option<usize> {
443 let (at, len) = (self.slots[s].at as usize, self.slots[s].len as usize);
444 self.neighbour[at..at + len].iter().position(|n| *n == node)
445 }
446
447 fn run_for(&mut self, node: u64, label: u32, incoming: u8) -> usize {
449 if (self.live + 1) * 4 > self.slots.len() * 3 {
450 self.regrow();
451 }
452 let n = self.slots.len();
453 let mut i = bucket(hash(node, label, incoming), n);
454 loop {
455 let s = &self.slots[i];
456 if s.flags & LIVE == 0 {
457 self.slots[i] = Slot {
458 node,
459 at: 0,
460 len: 0,
461 cap: 0,
462 label,
463 flags: LIVE | incoming,
464 };
465 self.live += 1;
466 return i;
467 }
468 if s.node == node && s.label == label && s.flags & INCOMING == incoming {
469 return i;
470 }
471 i += 1;
472 if i == n {
473 i = 0;
474 }
475 }
476 }
477
478 fn vacancy(&self, node: u64, label: u32, incoming: u8) -> usize {
480 let n = self.slots.len();
481 let mut i = bucket(hash(node, label, incoming), n);
482 while self.slots[i].flags & LIVE != 0 {
483 i += 1;
484 if i == n {
485 i = 0;
486 }
487 }
488 i
489 }
490
491 fn regrow(&mut self) {
492 let want = (self.slots.len() + self.slots.len() / 4).max(16);
499 let old = core::mem::replace(&mut self.slots, vec![Slot::default(); want]);
500 for slot in old {
501 if slot.flags & LIVE != 0 {
502 let i = self.vacancy(slot.node, slot.label, slot.flags & INCOMING);
503 self.slots[i] = slot;
504 }
505 }
506 }
507
508 fn push(&mut self, s: usize, node: u64, edge: u32) {
509 let Slot {
510 mut at,
511 len,
512 mut cap,
513 ..
514 } = self.slots[s];
515 if len == cap {
516 let want = LADDER[ceil_class(cap + 1)];
517 let to = self.alloc(want);
518 if len > 0 {
519 self.copy_run(at, to, len as usize);
520 self.release(at, cap);
521 }
522 at = to;
523 cap = want;
524 }
525 let i = at as usize + len as usize;
526 self.neighbour[i] = node;
527 self.edge[i] = edge;
528 self.slots[s].at = at;
529 self.slots[s].cap = cap;
530 self.slots[s].len = len + 1;
531 self.entries += 1;
532 if len == 0 {
533 self.filled += 1;
534 }
535 }
536
537 fn take(&mut self, s: usize, i: usize) -> (u64, u32) {
539 let Slot { at, len, cap, .. } = self.slots[s];
540 let (at, last) = (at as usize, at as usize + len as usize - 1);
541 let gone = (self.neighbour[at + i], self.edge[at + i]);
542 self.neighbour[at + i] = self.neighbour[last];
543 self.edge[at + i] = self.edge[last];
544 self.slots[s].len = len - 1;
545 self.entries -= 1;
546 if len == 1 {
547 self.filled -= 1;
548 }
549 self.shrink(s, cap);
550 gone
551 }
552
553 fn shrink(&mut self, s: usize, cap: u32) {
554 let len = self.slots[s].len;
555 if len == 0 {
556 self.release(self.slots[s].at, cap);
557 self.slots[s].at = 0;
558 self.slots[s].cap = 0;
559 return;
560 }
561 if len * 2 > cap {
565 return;
566 }
567 let want = LADDER[ceil_class(len)];
568 if want >= cap {
569 return;
570 }
571 let at = self.slots[s].at;
572 let to = self.alloc(want);
573 self.copy_run(at, to, len as usize);
574 self.release(at, cap);
575 self.slots[s].at = to;
576 self.slots[s].cap = want;
577 }
578
579 fn copy_run(&mut self, from: u32, to: u32, len: usize) {
580 let (from, to) = (from as usize, to as usize);
581 self.neighbour.copy_within(from..from + len, to);
582 self.edge.copy_within(from..from + len, to);
583 }
584
585 fn alloc(&mut self, cap: u32) -> u32 {
586 let class = ceil_class(cap);
587 if let Some(list) = self.free.get_mut(class)
588 && let Some(at) = list.pop()
589 {
590 return at;
591 }
592 let cap = cap as usize;
593 let at = self.neighbour.len();
594 assert!(at + cap <= u32::MAX as usize, "the adjacency arena is full");
595 if at + cap > self.neighbour.capacity() {
601 let cur = self.neighbour.capacity();
602 let want = (cur + cur / 8).max(at + cap).max(64);
603 self.neighbour.reserve_exact(want - at);
604 self.edge.reserve_exact(want - at);
605 }
606 self.neighbour.resize(at + cap, 0);
607 self.edge.resize(at + cap, 0);
608 at as u32
609 }
610
611 fn release(&mut self, at: u32, cap: u32) {
615 let class = floor_class(cap);
616 while self.free.len() <= class {
617 self.free.push(Vec::new());
618 }
619 self.free[class].push(at);
620 }
621}
622
623fn ceil_class(n: u32) -> usize {
629 LADDER.iter().position(|c| *c >= n).unwrap_or(CLASSES - 1)
630}
631
632fn floor_class(n: u32) -> usize {
634 let at = ceil_class(n);
635 if LADDER[at] > n {
636 at.saturating_sub(1)
637 } else {
638 at
639 }
640}
641
642fn incoming(dir: Dir) -> u8 {
643 match dir {
644 Dir::Out => 0,
645 Dir::In => INCOMING,
646 }
647}
648
649#[inline]
654fn bucket(h: u64, n: usize) -> usize {
655 ((u128::from(h) * n as u128) >> 64) as usize
656}
657
658#[inline]
664fn hash(node: u64, label: u32, incoming: u8) -> u64 {
665 let tag = (u64::from(label) << 1) | u64::from(incoming >> 1);
666 let mut x = node ^ tag.wrapping_mul(0x9e37_79b9_7f4a_7c15);
667 x ^= x >> 33;
668 x = x.wrapping_mul(0xff51_afd7_ed55_8ccd);
669 x ^= x >> 29;
670 x = x.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
671 x ^ (x >> 32)
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use yo_common::Rng;
678
679 const FOLLOWS: u32 = 1;
680 const WORKS_AT: u32 = 2;
681
682 fn sorted(v: &[u64]) -> Vec<u64> {
683 let mut v = v.to_vec();
684 v.sort_unstable();
685 v
686 }
687
688 #[test]
689 fn a_slot_is_one_half_of_a_cache_line() {
690 assert_eq!(size_of::<Slot>(), 32);
691 }
692
693 #[test]
694 fn the_ladder_only_ever_goes_up() {
695 for w in LADDER.windows(2) {
696 assert!(w[1] > w[0] || w[0] == u32::MAX, "{w:?} does not go up");
697 }
698 assert_eq!(LADDER[4], 16, "doubling should run out at 16");
699 assert_eq!(
700 LADDER[5], 20,
701 "and a quarter more should be the step after it"
702 );
703 assert!(
704 LADDER.contains(&u32::MAX),
705 "the ladder should reach the end of a u32"
706 );
707 assert_eq!(LADDER[ceil_class(17)], 20);
709 assert_eq!(LADDER[floor_class(19)], 16);
710 assert_eq!(LADDER[floor_class(20)], 20);
711 assert_eq!(LADDER[ceil_class(1)], 1);
712 }
713
714 #[test]
715 fn a_run_is_the_neighbours_that_were_linked_to_it() {
716 let mut g = Adjacency::new();
717 for (i, dst) in [7u64, 9, 11].iter().enumerate() {
718 g.link(1, *dst, FOLLOWS, i as u32);
719 }
720 assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[7, 9, 11]);
721 assert_eq!(g.edge_slots(1, FOLLOWS, Dir::Out), &[0, 1, 2]);
722 assert_eq!(g.degree(1, FOLLOWS, Dir::Out), 3);
723 assert_eq!(g.edges(), 3);
724 assert_eq!(g.runs(), 4);
725 }
726
727 #[test]
728 fn a_node_nobody_linked_has_no_neighbours_rather_than_no_answer() {
729 let g = Adjacency::new();
730 assert!(g.neighbours(1, FOLLOWS, Dir::Out).is_empty());
731 assert!(g.edge_slots(1, FOLLOWS, Dir::Out).is_empty());
732 assert_eq!(g.degree(1, FOLLOWS, Dir::Out), 0);
733 assert!(g.is_empty());
734 g.prefetch(1, FOLLOWS, Dir::Out);
735 }
736
737 #[test]
738 fn an_edge_is_readable_from_both_ends() {
739 let mut g = Adjacency::new();
740 g.link(1, 2, FOLLOWS, 10);
741 assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[2]);
742 assert_eq!(g.neighbours(2, FOLLOWS, Dir::In), &[1]);
743 assert_eq!(g.edge_slots(2, FOLLOWS, Dir::In), &[10]);
744 assert!(g.neighbours(2, FOLLOWS, Dir::Out).is_empty());
745 }
746
747 #[test]
748 fn one_label_is_not_another() {
749 let mut g = Adjacency::new();
750 g.link(1, 2, FOLLOWS, 10);
751 g.link(1, 3, WORKS_AT, 11);
752 assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[2]);
753 assert_eq!(g.neighbours(1, WORKS_AT, Dir::Out), &[3]);
754 }
755
756 #[test]
757 fn a_self_loop_is_at_both_of_its_ends_and_they_are_different_runs() {
758 let mut g = Adjacency::new();
759 g.link(1, 1, FOLLOWS, 5);
760 assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[1]);
761 assert_eq!(g.neighbours(1, FOLLOWS, Dir::In), &[1]);
762 assert_eq!(g.unlink(1, 1, FOLLOWS), Some(5));
763 assert!(g.neighbours(1, FOLLOWS, Dir::Out).is_empty());
764 assert!(g.neighbours(1, FOLLOWS, Dir::In).is_empty());
765 }
766
767 #[test]
768 fn out_only_stores_nothing_incoming() {
769 let mut both = Adjacency::new();
770 let mut out = Adjacency::out_only();
771 for i in 0..1000u64 {
772 both.link(i, i + 1, FOLLOWS, i as u32);
773 out.link(i, i + 1, FOLLOWS, i as u32);
774 }
775 assert_eq!(out.neighbours(500, FOLLOWS, Dir::Out), &[501]);
776 assert!(out.neighbours(500, FOLLOWS, Dir::In).is_empty());
777 assert_eq!(both.neighbours(500, FOLLOWS, Dir::In), &[499]);
778 assert!(!out.indexes_incoming());
779 assert!(
780 out.bytes() * 3 < both.bytes() * 2,
781 "{} against {}",
782 out.bytes(),
783 both.bytes()
784 );
785 }
786
787 #[test]
788 fn unlinking_takes_the_edge_off_both_ends() {
789 let mut g = Adjacency::new();
790 g.link(1, 2, FOLLOWS, 10);
791 g.link(1, 3, FOLLOWS, 11);
792 assert_eq!(g.unlink(1, 2, FOLLOWS), Some(10));
793 assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[3]);
794 assert!(g.neighbours(2, FOLLOWS, Dir::In).is_empty());
795 assert_eq!(g.edges(), 1);
796 assert_eq!(g.unlink(1, 2, FOLLOWS), None);
797 assert_eq!(g.unlink(9, 9, FOLLOWS), None);
798 }
799
800 #[test]
801 fn a_delete_moves_the_last_edge_into_the_hole() {
802 let mut g = Adjacency::new();
803 for dst in 1..=5u64 {
804 g.link(0, dst, FOLLOWS, dst as u32);
805 }
806 g.unlink(0, 2, FOLLOWS);
807 let n = g.neighbours(0, FOLLOWS, Dir::Out);
810 let e = g.edge_slots(0, FOLLOWS, Dir::Out);
811 assert_eq!(sorted(n), vec![1, 3, 4, 5]);
812 for (i, node) in n.iter().enumerate() {
813 assert_eq!(u64::from(e[i]), *node, "the pairing survived the swap");
814 }
815 }
816
817 #[test]
818 fn unlink_at_moves_the_last_entry_into_the_position_it_took() {
819 let mut g = Adjacency::new();
820 for dst in 1..=4u64 {
821 g.link(0, dst, FOLLOWS, dst as u32);
822 }
823 assert_eq!(g.unlink_at(0, FOLLOWS, Dir::Out, 0), Some((1, 1)));
824 assert_eq!(g.neighbours(0, FOLLOWS, Dir::Out), &[4, 2, 3]);
825 assert_eq!(g.unlink_at(0, FOLLOWS, Dir::Out, 9), None);
826 assert_eq!(g.unlink_at(7, FOLLOWS, Dir::Out, 0), None);
827 }
828
829 #[test]
830 fn a_run_survives_growing_through_every_size_it_passes() {
831 let mut g = Adjacency::out_only();
832 let n = 5000u64;
833 for dst in 0..n {
834 g.link(0, dst, FOLLOWS, dst as u32);
835 }
836 assert_eq!(g.degree(0, FOLLOWS, Dir::Out), n as usize);
837 assert_eq!(
838 g.neighbours(0, FOLLOWS, Dir::Out),
839 (0..n).collect::<Vec<_>>()
840 );
841 assert_eq!(
842 g.edge_slots(0, FOLLOWS, Dir::Out),
843 (0..n as u32).collect::<Vec<_>>()
844 );
845 }
846
847 #[test]
848 fn a_hub_that_empties_gives_its_block_back() {
849 let mut g = Adjacency::out_only();
850 for dst in 0..4000u64 {
851 g.link(0, dst, FOLLOWS, 0);
852 }
853 let full = g.bytes();
854 for dst in 0..4000u64 {
855 assert!(g.unlink(0, dst, FOLLOWS).is_some());
856 }
857 assert_eq!(g.degree(0, FOLLOWS, Dir::Out), 0);
858 assert_eq!(g.runs(), 0);
859 for dst in 0..4000u64 {
862 g.link(1, dst, FOLLOWS, 0);
863 }
864 assert!(g.bytes() <= full + full / 4, "{} against {full}", g.bytes());
865 }
866
867 #[test]
868 fn a_run_that_grows_and_shrinks_does_not_copy_itself_on_a_boundary() {
869 let mut g = Adjacency::out_only();
874 for dst in 0..16u64 {
875 g.link(0, dst, FOLLOWS, 0);
876 }
877 g.link(0, 999, FOLLOWS, 0);
880 g.unlink(0, 999, FOLLOWS);
881 let settled = g.bytes();
882 for _ in 0..100 {
883 g.link(0, 999, FOLLOWS, 0);
884 g.unlink(0, 999, FOLLOWS);
885 }
886 assert_eq!(g.bytes(), settled);
887 assert_eq!(g.degree(0, FOLLOWS, Dir::Out), 16);
888 }
889
890 #[test]
891 fn a_run_that_loses_most_of_itself_gives_the_room_back() {
892 let mut g = Adjacency::out_only();
893 for dst in 0..4000u64 {
894 g.link(0, dst, FOLLOWS, 0);
895 }
896 for dst in 0..3990u64 {
897 g.unlink(0, dst, FOLLOWS);
898 }
899 assert_eq!(g.degree(0, FOLLOWS, Dir::Out), 10);
900 assert!(g.slots.iter().any(|s| s.len == 10 && s.cap <= 16));
902 g.compact();
903 assert_eq!(g.degree(0, FOLLOWS, Dir::Out), 10);
904 assert!(g.bytes() < 4000, "{} bytes for ten edges", g.bytes());
905 }
906
907 #[test]
908 fn compact_drops_the_runs_that_emptied() {
909 let mut g = Adjacency::new();
910 for i in 0..2000u64 {
911 g.link(i, i + 1, FOLLOWS, i as u32);
912 }
913 for i in 0..1990u64 {
914 g.unlink(i, i + 1, FOLLOWS);
915 }
916 assert_eq!(g.runs(), 20);
917 let before = g.bytes();
918 g.compact();
919 assert_eq!(g.runs(), 20);
920 assert_eq!(g.edges(), 10);
921 assert_eq!(g.neighbours(1995, FOLLOWS, Dir::Out), &[1996]);
922 assert_eq!(g.neighbours(1996, FOLLOWS, Dir::In), &[1995]);
923 assert!(g.bytes() * 4 < before, "{} against {before}", g.bytes());
924 g.link(1995, 3000, FOLLOWS, 7);
928 assert_eq!(
929 sorted(g.neighbours(1995, FOLLOWS, Dir::Out)),
930 vec![1996, 3000]
931 );
932 assert_eq!(sorted(g.neighbours(3000, FOLLOWS, Dir::In)), vec![1995]);
933 }
934
935 #[test]
936 fn a_hot_run_costs_about_twelve_bytes_an_edge() {
937 let mut g = Adjacency::out_only();
942 let mut rng = Rng::new(0x9e3f);
943 let nodes = 200_000u64;
944 let mut edges = 0usize;
945 for src in 0..nodes {
946 let deg = match rng.next_u64() % 1000 {
947 0..=799 => 1 + rng.next_u64() % 4,
948 800..=979 => 5 + rng.next_u64() % 40,
949 _ => 45 + rng.next_u64() % 600,
950 };
951 for _ in 0..deg {
952 g.link(src, rng.next_u64() % nodes, FOLLOWS, 0);
953 edges += 1;
954 }
955 }
956 let per = g.bytes() as f64 / edges as f64;
957 g.compact();
958 let settled = g.bytes() as f64 / edges as f64;
959 assert!(per < 19.0, "{per:.2} bytes an edge over {edges} edges");
964 assert!(settled < 16.0, "{settled:.2} bytes an edge once swept");
965 }
966
967 #[test]
968 fn a_two_hop_reaches_what_a_pair_of_one_hops_reaches() {
969 let mut g = Adjacency::new();
970 let mut rng = Rng::new(7);
971 let nodes = 5000u64;
972 for src in 0..nodes {
973 for _ in 0..8 {
974 g.link(src, rng.next_u64() % nodes, FOLLOWS, 0);
975 }
976 }
977 let first = g.neighbours(0, FOLLOWS, Dir::Out).to_vec();
978 for hop in &first {
979 g.prefetch(*hop, FOLLOWS, Dir::Out);
980 }
981 let mut seen = Vec::new();
982 for hop in &first {
983 seen.extend_from_slice(g.neighbours(*hop, FOLLOWS, Dir::Out));
984 }
985 assert_eq!(seen.len(), 64);
986 for (i, hop) in first.iter().enumerate() {
989 for dst in &seen[i * 8..(i + 1) * 8] {
990 assert!(g.neighbours(*dst, FOLLOWS, Dir::In).contains(hop));
991 }
992 }
993 }
994
995 #[test]
996 fn the_plane_agrees_with_a_list_of_what_was_done_to_it() {
997 let mut g = Adjacency::new();
1002 let mut want: Vec<Vec<u64>> = vec![Vec::new(); 64];
1003 let mut rng = Rng::new(0xbeef);
1004 for _ in 0..200_000 {
1005 let src = rng.next_u64() % 64;
1006 let dst = rng.next_u64() % 64;
1007 if rng.next_u64().is_multiple_of(3) {
1008 if let Some(i) = want[src as usize].iter().position(|n| *n == dst) {
1009 want[src as usize].swap_remove(i);
1010 assert!(g.unlink(src, dst, FOLLOWS).is_some());
1011 } else {
1012 assert_eq!(g.unlink(src, dst, FOLLOWS), None);
1013 }
1014 } else {
1015 want[src as usize].push(dst);
1016 g.link(src, dst, FOLLOWS, 0);
1017 }
1018 }
1019 let mut total = 0;
1020 for (src, list) in want.iter().enumerate() {
1021 assert_eq!(
1022 sorted(g.neighbours(src as u64, FOLLOWS, Dir::Out)),
1023 sorted(list),
1024 "node {src}"
1025 );
1026 total += list.len();
1027 }
1028 assert_eq!(g.edges(), total);
1029 let mut incoming: Vec<Vec<u64>> = vec![Vec::new(); 64];
1031 for (src, list) in want.iter().enumerate() {
1032 for dst in list {
1033 incoming[*dst as usize].push(src as u64);
1034 }
1035 }
1036 for (dst, list) in incoming.iter().enumerate() {
1037 assert_eq!(
1038 sorted(g.neighbours(dst as u64, FOLLOWS, Dir::In)),
1039 sorted(list),
1040 "into node {dst}"
1041 );
1042 }
1043 }
1044}