1use super::csr_edges::CsrEdges;
2use super::vertex::VertexId;
3use formualizer_common::Coord as AbsCoord;
4use rustc_hash::{FxHashMap, FxHashSet};
5
6#[cfg(test)]
7mod tests {
8 use super::*;
9 use formualizer_common::Coord as AbsCoord;
10
11 #[test]
12 fn test_delta_slab_add_edge() {
13 let csr = CsrEdges::from_adjacency(
14 vec![(0u32, vec![1u32])],
15 &[
16 AbsCoord::new(0, 0),
17 AbsCoord::new(0, 1),
18 AbsCoord::new(0, 2),
19 ],
20 );
21 let mut delta = DeltaEdgeSlab::new();
22
23 delta.add_edge(VertexId(0), VertexId(2));
24
25 let merged = delta.merged_view(&csr, VertexId(0));
26 assert_eq!(merged, vec![VertexId(1), VertexId(2)]);
27 }
28
29 #[test]
30 fn test_delta_slab_remove_edge() {
31 let csr = CsrEdges::from_adjacency(
32 vec![(0u32, vec![1u32, 2u32, 3u32])],
33 &[
34 AbsCoord::new(0, 0),
35 AbsCoord::new(0, 1),
36 AbsCoord::new(0, 2),
37 AbsCoord::new(0, 3),
38 ],
39 );
40 let mut delta = DeltaEdgeSlab::new();
41
42 delta.remove_edge(VertexId(0), VertexId(2));
43
44 let merged = delta.merged_view(&csr, VertexId(0));
45 assert_eq!(merged, vec![VertexId(1), VertexId(3)]);
46 }
47
48 #[test]
49 fn test_delta_slab_rebuild_threshold() {
50 let mut edges = CsrMutableEdges::new();
51
52 for i in 0..1000 {
54 edges.add_edge(VertexId(i), VertexId(i + 1));
55 }
56
57 assert!(edges.delta_size() < 100); }
60
61 #[test]
62 fn test_delta_slab_multiple_operations() {
63 let csr = CsrEdges::from_adjacency(
64 vec![(0u32, vec![1u32, 2u32]), (1u32, vec![3u32])],
65 &[
66 AbsCoord::new(0, 0),
67 AbsCoord::new(0, 1),
68 AbsCoord::new(0, 2),
69 AbsCoord::new(1, 0),
70 ],
71 );
72 let mut delta = DeltaEdgeSlab::new();
73
74 delta.add_edge(VertexId(0), VertexId(3));
76 delta.remove_edge(VertexId(0), VertexId(1));
77 delta.add_edge(VertexId(0), VertexId(4));
78
79 let merged = delta.merged_view(&csr, VertexId(0));
80 assert_eq!(merged, vec![VertexId(2), VertexId(3), VertexId(4)]);
81 }
82
83 #[test]
84 fn test_mutable_edges_exact_edge_count_includes_delta() {
85 let mut edges = CsrMutableEdges::with_coords(vec![
86 AbsCoord::new(0, 0),
87 AbsCoord::new(0, 1),
88 AbsCoord::new(0, 2),
89 ]);
90 edges.add_edge(VertexId(0), VertexId(1));
91 edges.add_edge(VertexId(0), VertexId(2));
92 assert_eq!(edges.num_edges_exact(), 2);
93
94 edges.remove_edge(VertexId(0), VertexId(1));
95 assert_eq!(edges.num_edges_exact(), 1);
96 }
97
98 #[test]
99 fn test_delta_slab_empty_base() {
100 let csr = CsrEdges::empty();
101 let mut delta = DeltaEdgeSlab::new();
102
103 delta.add_edge(VertexId(0), VertexId(1));
104 delta.add_edge(VertexId(0), VertexId(2));
105
106 let merged = delta.merged_view(&csr, VertexId(0));
107 assert_eq!(merged, vec![VertexId(1), VertexId(2)]);
108 }
109
110 #[test]
111 fn test_delta_slab_remove_nonexistent() {
112 let csr = CsrEdges::from_adjacency(
113 vec![(0u32, vec![1u32])],
114 &[AbsCoord::new(0, 0), AbsCoord::new(0, 1)],
115 );
116 let mut delta = DeltaEdgeSlab::new();
117
118 delta.remove_edge(VertexId(0), VertexId(2));
120
121 let merged = delta.merged_view(&csr, VertexId(0));
122 assert_eq!(merged, vec![VertexId(1)]); }
124
125 #[test]
126 fn test_delta_slab_apply_to_csr() {
127 let csr = CsrEdges::from_adjacency(
128 vec![(0u32, vec![1u32]), (1u32, vec![2u32]), (2u32, vec![])],
129 &[
130 AbsCoord::new(0, 0),
131 AbsCoord::new(0, 1),
132 AbsCoord::new(1, 0),
133 ],
134 );
135
136 let mut delta = DeltaEdgeSlab::new();
137 delta.add_edge(VertexId(0), VertexId(2));
138 delta.remove_edge(VertexId(1), VertexId(2));
139 delta.add_edge(VertexId(2), VertexId(0));
140
141 let coords = vec![
143 AbsCoord::new(0, 0),
144 AbsCoord::new(0, 1),
145 AbsCoord::new(1, 0),
146 ];
147 let vertex_ids = vec![0u32, 1u32, 2u32];
148 let new_csr = delta.apply_to_csr(&csr, &coords, &vertex_ids);
149
150 assert_eq!(new_csr.out_edges(VertexId(0)), &[VertexId(1), VertexId(2)]);
151 assert_eq!(new_csr.out_edges(VertexId(1)), &[]);
152 assert_eq!(new_csr.out_edges(VertexId(2)), &[VertexId(0)]);
153 }
154
155 #[test]
156 fn test_mutable_edges_auto_rebuild() {
157 let mut edges = CsrMutableEdges::with_coords(vec![
158 AbsCoord::new(0, 0),
159 AbsCoord::new(0, 1),
160 AbsCoord::new(1, 0),
161 ]);
162
163 edges.add_edge(VertexId(0), VertexId(1));
165 edges.add_edge(VertexId(1), VertexId(2));
166
167 for _ in 0..500 {
169 edges.add_edge(VertexId(2), VertexId(0));
170 edges.remove_edge(VertexId(2), VertexId(0));
171 }
172
173 assert!(edges.delta_size() < 50);
175
176 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1)]);
178 assert_eq!(edges.out_edges(VertexId(1)), vec![VertexId(2)]);
179 }
180
181 #[test]
182 fn test_mutable_edges_with_offset_vertex_ids() {
183 use crate::engine::vertex_store::FIRST_NORMAL_VERTEX;
184
185 let mut edges = CsrMutableEdges::new();
186
187 let base_id = FIRST_NORMAL_VERTEX;
189 edges.add_vertex(AbsCoord::new(0, 0), base_id);
190 edges.add_vertex(AbsCoord::new(0, 1), base_id + 1);
191 edges.add_vertex(AbsCoord::new(1, 0), base_id + 2);
192
193 edges.add_edge(VertexId(base_id), VertexId(base_id + 1));
195 edges.add_edge(VertexId(base_id + 1), VertexId(base_id + 2));
196 edges.add_edge(VertexId(base_id + 2), VertexId(base_id));
197
198 assert_eq!(
200 edges.out_edges(VertexId(base_id)),
201 vec![VertexId(base_id + 1)]
202 );
203 assert_eq!(
204 edges.out_edges(VertexId(base_id + 1)),
205 vec![VertexId(base_id + 2)]
206 );
207 assert_eq!(
208 edges.out_edges(VertexId(base_id + 2)),
209 vec![VertexId(base_id)]
210 );
211
212 edges.rebuild();
214 assert_eq!(
215 edges.out_edges(VertexId(base_id)),
216 vec![VertexId(base_id + 1)]
217 );
218 assert_eq!(
219 edges.out_edges(VertexId(base_id + 1)),
220 vec![VertexId(base_id + 2)]
221 );
222 assert_eq!(
223 edges.out_edges(VertexId(base_id + 2)),
224 vec![VertexId(base_id)]
225 );
226 }
227
228 #[test]
229 fn test_csr_coord_update() {
230 let mut edges = CsrMutableEdges::new();
231
232 edges.add_vertex(AbsCoord::new(1, 1), 1024);
233 edges.add_vertex(AbsCoord::new(2, 2), 1025);
234 edges.add_edge(VertexId(1024), VertexId(1025));
235
236 edges.update_coord(VertexId(1024), AbsCoord::new(5, 5));
238
239 edges.rebuild();
241 let out = edges.out_edges(VertexId(1024));
242 assert_eq!(out, vec![VertexId(1025)]);
243 }
244
245 #[test]
246 fn update_coord_uses_vertex_position_index() {
247 let mut edges = CsrMutableEdges::new();
248 let items: Vec<_> = (0..20_000u32)
249 .map(|id| (AbsCoord::new(id, id % 17), id))
250 .collect();
251 edges.add_vertices_batch(&items);
252
253 let started = std::time::Instant::now();
254 for id in 15_000..20_000u32 {
255 edges.update_coord(VertexId(id), AbsCoord::new(id + 1, (id + 2) % 100));
256 }
257 let elapsed = started.elapsed();
258
259 if !cfg!(debug_assertions) {
260 assert!(
261 elapsed < std::time::Duration::from_millis(50),
262 "update_coord took {elapsed:?}"
263 );
264 }
265
266 for id in 15_000..20_000u32 {
267 let pos = edges.vertex_pos[&id];
268 assert_eq!(edges.vertex_ids[pos], id);
269 assert_eq!(edges.coords[pos], AbsCoord::new(id + 1, (id + 2) % 100));
270 }
271 }
272
273 #[test]
274 fn test_last_op_wins_add_then_remove() {
275 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![])], &[AbsCoord::new(0, 0)]);
276 let mut delta = DeltaEdgeSlab::new();
277 delta.add_edge(VertexId(0), VertexId(1));
278 delta.remove_edge(VertexId(0), VertexId(1));
279 let merged = delta.merged_view(&csr, VertexId(0));
280 assert_eq!(merged, vec![]);
281 }
282
283 #[test]
284 fn test_last_op_wins_remove_then_add() {
285 let csr = CsrEdges::from_adjacency(vec![(0u32, vec![])], &[AbsCoord::new(0, 0)]);
286 let mut delta = DeltaEdgeSlab::new();
287 delta.remove_edge(VertexId(0), VertexId(1));
288 delta.add_edge(VertexId(0), VertexId(1));
289 let merged = delta.merged_view(&csr, VertexId(0));
290 assert_eq!(merged, vec![VertexId(1)]);
291 }
292
293 #[test]
294 fn test_dedup_additions_and_sorted() {
295 let csr = CsrEdges::from_adjacency(
296 vec![(0u32, vec![2u32])],
297 &[
298 AbsCoord::new(0, 0),
299 AbsCoord::new(0, 1),
300 AbsCoord::new(0, 2),
301 ],
302 );
303 let mut delta = DeltaEdgeSlab::new();
304 delta.add_edge(VertexId(0), VertexId(1));
306 delta.add_edge(VertexId(0), VertexId(3));
307 delta.add_edge(VertexId(0), VertexId(1)); let merged = delta.merged_view(&csr, VertexId(0));
309 assert_eq!(merged, vec![VertexId(1), VertexId(2), VertexId(3)]);
311 }
312
313 #[test]
314 fn test_merged_in_view_add_and_remove() {
315 let csr = CsrEdges::from_adjacency(
316 vec![(0u32, vec![2u32]), (1u32, vec![2u32])],
317 &[
318 AbsCoord::new(0, 0),
319 AbsCoord::new(0, 1),
320 AbsCoord::new(0, 2),
321 AbsCoord::new(0, 3),
322 ],
323 );
324 let mut delta = DeltaEdgeSlab::new();
325
326 delta.add_edge(VertexId(3), VertexId(2));
328 delta.remove_edge(VertexId(0), VertexId(2));
329
330 let merged = delta.merged_in_view(&csr, VertexId(2));
331 assert_eq!(merged, vec![VertexId(1), VertexId(3)]);
332 }
333
334 #[test]
335 fn test_merged_in_view_last_op_wins() {
336 let csr = CsrEdges::from_adjacency(
337 vec![(0u32, vec![1u32])],
338 &[AbsCoord::new(0, 0), AbsCoord::new(0, 1)],
339 );
340 let mut delta = DeltaEdgeSlab::new();
341
342 delta.remove_edge(VertexId(0), VertexId(1));
343 delta.add_edge(VertexId(0), VertexId(1));
344 assert_eq!(delta.merged_in_view(&csr, VertexId(1)), vec![VertexId(0)]);
345
346 delta.add_edge(VertexId(2), VertexId(1));
347 delta.remove_edge(VertexId(2), VertexId(1));
348 assert_eq!(delta.merged_in_view(&csr, VertexId(1)), vec![VertexId(0)]);
349 }
350
351 #[test]
352 fn test_end_batch_below_threshold_defers_rebuild() {
353 let mut edges = CsrMutableEdges::with_coords(vec![
354 AbsCoord::new(0, 0),
355 AbsCoord::new(0, 1),
356 AbsCoord::new(0, 2),
357 ]);
358 let before = edges.rebuild_count();
359
360 edges.begin_batch();
361 edges.add_edge(VertexId(0), VertexId(1));
362 edges.add_edge(VertexId(0), VertexId(2));
363 edges.end_batch();
364
365 assert_eq!(edges.rebuild_count(), before);
367 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1), VertexId(2)]);
369 assert_eq!(edges.in_edges_merged(VertexId(1)), vec![VertexId(0)]);
370 assert_eq!(edges.in_edges_merged(VertexId(2)), vec![VertexId(0)]);
371 }
372
373 #[test]
374 fn test_end_batch_rebuilds_at_threshold() {
375 let coords: Vec<AbsCoord> = (0..1200u32).map(|i| AbsCoord::new(i, 0)).collect();
376 let mut edges = CsrMutableEdges::with_coords(coords);
377 let before = edges.rebuild_count();
378
379 edges.begin_batch();
380 for i in 0..1100u32 {
381 edges.add_edge(VertexId(i), VertexId(i + 1));
382 }
383 edges.end_batch();
384
385 assert_eq!(edges.rebuild_count(), before + 1);
386 assert_eq!(edges.delta_size(), 0);
387 assert_eq!(edges.out_edges(VertexId(0)), vec![VertexId(1)]);
388 assert_eq!(edges.in_edges(VertexId(1)), &[VertexId(0)]);
389 }
390
391 #[test]
392 fn test_add_vertex_defers_rebuild() {
393 let mut edges = CsrMutableEdges::new();
394 edges.add_vertex(AbsCoord::new(0, 0), 1024);
395 edges.add_vertex(AbsCoord::new(0, 1), 1025);
396 assert_eq!(edges.rebuild_count(), 0);
397
398 edges.add_edge(VertexId(1024), VertexId(1025));
399 assert_eq!(edges.out_edges(VertexId(1024)), vec![VertexId(1025)]);
401 assert_eq!(edges.in_edges_merged(VertexId(1025)), vec![VertexId(1024)]);
402
403 edges.rebuild();
405 assert_eq!(edges.out_edges(VertexId(1024)), vec![VertexId(1025)]);
406 assert_eq!(edges.in_edges(VertexId(1025)), &[VertexId(1024)]);
407 }
408
409 #[test]
410 fn test_end_batch_rebuilds_on_coord_change_only() {
411 let mut edges =
412 CsrMutableEdges::with_coords(vec![AbsCoord::new(0, 0), AbsCoord::new(0, 1)]);
413 edges.begin_batch();
414 edges.update_coord(VertexId(0), AbsCoord::new(0, 2));
415 edges.end_batch();
417 assert_eq!(edges.out_edges(VertexId(0)), Vec::<VertexId>::new());
419 }
420}
421
422#[derive(Debug)]
427pub struct DeltaEdgeSlab {
428 additions: FxHashMap<VertexId, FxHashSet<VertexId>>,
430
431 removals: FxHashMap<VertexId, FxHashSet<VertexId>>,
433
434 additions_in: FxHashMap<VertexId, FxHashSet<VertexId>>,
437
438 removals_in: FxHashMap<VertexId, FxHashSet<VertexId>>,
440
441 op_count: usize,
443
444 coord_changed: bool,
446}
447
448impl DeltaEdgeSlab {
449 pub fn new() -> Self {
451 Self {
452 additions: FxHashMap::default(),
453 removals: FxHashMap::default(),
454 additions_in: FxHashMap::default(),
455 removals_in: FxHashMap::default(),
456 op_count: 0,
457 coord_changed: false,
458 }
459 }
460
461 fn reserve_additions(&mut self, additional: usize) {
462 self.additions.reserve(additional);
463 self.additions_in.reserve(additional);
464 }
465
466 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
468 if let Some(rem) = self.removals.get_mut(&from) {
470 rem.remove(&to);
471 }
472 if let Some(rem) = self.removals_in.get_mut(&to) {
473 rem.remove(&from);
474 }
475 self.additions.entry(from).or_default().insert(to);
477 self.additions_in.entry(to).or_default().insert(from);
478 self.op_count += 1;
479 }
480
481 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
483 if let Some(adds) = self.additions.get_mut(&from) {
485 adds.remove(&to);
486 }
487 if let Some(adds) = self.additions_in.get_mut(&to) {
488 adds.remove(&from);
489 }
490 self.removals.entry(from).or_default().insert(to);
492 self.removals_in.entry(to).or_default().insert(from);
493 self.op_count += 1;
494 }
495
496 pub fn merged_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
498 let mut result: Vec<_> = csr.out_edges(v).to_vec();
500 if let Some(removes) = self.removals.get(&v) {
502 result.retain(|e| !removes.contains(e));
503 }
504 if let Some(adds) = self.additions.get(&v) {
506 result.extend(adds.iter().copied());
507 }
508 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
510 result.retain(|e| seen.insert(*e));
511 result.sort_by_key(|e| e.0);
512 result
513 }
514
515 pub fn merged_in_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
518 let mut result: Vec<_> = csr.in_edges(v).to_vec();
519 if let Some(removes) = self.removals_in.get(&v) {
520 result.retain(|e| !removes.contains(e));
521 }
522 if let Some(adds) = self.additions_in.get(&v) {
523 result.extend(adds.iter().copied());
524 }
525 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
526 result.retain(|e| seen.insert(*e));
527 result.sort_by_key(|e| e.0);
528 result
529 }
530
531 pub fn needs_rebuild(&self) -> bool {
533 self.op_count >= 1000 || self.coord_changed
534 }
535
536 pub fn mark_dirty(&mut self) {
538 self.coord_changed = true;
539 }
540
541 pub fn op_count(&self) -> usize {
543 self.op_count
544 }
545
546 pub fn clear(&mut self) {
548 self.additions.clear();
549 self.removals.clear();
550 self.additions_in.clear();
551 self.removals_in.clear();
552 self.op_count = 0;
553 self.coord_changed = false;
554 }
555
556 pub fn additions_iter(&self) -> impl Iterator<Item = (&VertexId, &FxHashSet<VertexId>)> {
560 self.additions.iter()
561 }
562
563 pub fn removals_for(&self, from: VertexId) -> impl Iterator<Item = VertexId> + '_ {
565 self.removals
566 .get(&from)
567 .into_iter()
568 .flat_map(|set| set.iter().copied())
569 }
570
571 pub fn apply_to_csr(
573 &self,
574 base: &CsrEdges,
575 coords: &[AbsCoord],
576 vertex_ids: &[u32],
577 ) -> CsrEdges {
578 let mut adjacency = Vec::with_capacity(vertex_ids.len());
579
580 for &vid in vertex_ids {
582 let v = VertexId(vid);
583 let merged = self.merged_view(base, v);
584
585 let targets: Vec<u32> = merged.into_iter().map(|id| id.0).collect();
587
588 adjacency.push((vid, targets));
589 }
590
591 CsrEdges::from_adjacency(adjacency, coords)
592 }
593}
594
595impl Default for DeltaEdgeSlab {
596 fn default() -> Self {
597 Self::new()
598 }
599}
600
601#[derive(Debug)]
606pub struct CsrMutableEdges {
607 base: CsrEdges,
609
610 delta: DeltaEdgeSlab,
612
613 coords: Vec<AbsCoord>,
615
616 vertex_ids: Vec<u32>,
618
619 vertex_pos: FxHashMap<u32, usize>,
621
622 batch_depth: usize,
624
625 rebuild_count: u64,
627}
628
629impl CsrMutableEdges {
630 pub fn new() -> Self {
632 Self {
633 base: CsrEdges::empty(),
634 delta: DeltaEdgeSlab::new(),
635 coords: Vec::new(),
636 vertex_ids: Vec::new(),
637 vertex_pos: FxHashMap::default(),
638 batch_depth: 0,
639 rebuild_count: 0,
640 }
641 }
642
643 pub fn with_coords(coords: Vec<AbsCoord>) -> Self {
645 let num_vertices = coords.len();
646 let vertex_ids: Vec<u32> = (0..num_vertices as u32).collect();
647 let adjacency: Vec<_> = vertex_ids.iter().map(|&id| (id, Vec::new())).collect();
648 let vertex_pos = vertex_ids
649 .iter()
650 .enumerate()
651 .map(|(idx, &id)| (id, idx))
652 .collect();
653
654 Self {
655 base: CsrEdges::from_adjacency(adjacency, &coords),
656 delta: DeltaEdgeSlab::new(),
657 coords,
658 vertex_ids,
659 vertex_pos,
660 batch_depth: 0,
661 rebuild_count: 0,
662 }
663 }
664
665 pub(crate) fn reserve_prepared_additions(&mut self, vertices: usize, edges: usize) {
666 self.coords.reserve(vertices);
667 self.vertex_ids.reserve(vertices);
668 self.vertex_pos.reserve(vertices);
669 self.delta.reserve_additions(edges);
670 }
671
672 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
674 self.delta.add_edge(from, to);
675 self.maybe_rebuild();
676 }
677
678 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
680 self.delta.remove_edge(from, to);
681 self.maybe_rebuild();
682 }
683
684 pub fn out_edges(&self, v: VertexId) -> Vec<VertexId> {
686 if self.delta.op_count() == 0 {
687 self.base.out_edges(v).to_vec()
688 } else {
689 self.delta.merged_view(&self.base, v)
690 }
691 }
692
693 #[inline]
697 pub fn out_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
698 if self.delta.op_count() == 0 {
699 Some(self.base.out_edges(v))
700 } else {
701 None
702 }
703 }
704
705 pub fn in_edges(&self, v: VertexId) -> &[VertexId] {
708 self.base.in_edges(v)
709 }
710
711 pub fn in_edges_merged(&self, v: VertexId) -> Vec<VertexId> {
715 if self.delta.op_count() == 0 {
716 self.base.in_edges(v).to_vec()
717 } else {
718 self.delta.merged_in_view(&self.base, v)
719 }
720 }
721
722 #[inline]
726 pub fn in_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
727 if self.delta.op_count() == 0 {
728 Some(self.base.in_edges(v))
729 } else {
730 None
731 }
732 }
733
734 pub(crate) fn visit_in_edges_bounded(
744 &self,
745 v: VertexId,
746 remaining_work: &mut u64,
747 visitor: &mut dyn FnMut(VertexId) -> bool,
748 ) -> bool {
749 let removals = self.delta.removals_in.get(&v);
750 for &source in self.base.in_edges(v) {
751 if *remaining_work == 0 {
752 return false;
753 }
754 *remaining_work -= 1;
755 if removals.is_some_and(|set| set.contains(&source)) {
756 continue;
757 }
758 if !visitor(source) {
759 return false;
760 }
761 }
762 if let Some(additions) = self.delta.additions_in.get(&v) {
763 for &source in additions {
764 if *remaining_work == 0 {
765 return false;
766 }
767 *remaining_work -= 1;
768 if !visitor(source) {
769 return false;
770 }
771 }
772 }
773 true
774 }
775
776 pub fn delta_size(&self) -> usize {
778 self.delta.op_count()
779 }
780
781 pub fn num_edges_exact(&self) -> usize {
788 if self.delta.op_count() == 0 {
789 return self.base.num_edges();
790 }
791
792 self.vertex_ids
793 .iter()
794 .map(|&id| self.out_edges(VertexId(id)).len())
795 .sum()
796 }
797
798 pub fn rebuild(&mut self) {
800 if self.delta.op_count() > 0 || self.delta.needs_rebuild() {
801 self.base = self
802 .delta
803 .apply_to_csr(&self.base, &self.coords, &self.vertex_ids);
804 self.delta.clear();
805 self.rebuild_count += 1;
806 }
807 }
808
809 pub fn rebuild_count(&self) -> u64 {
814 self.rebuild_count
815 }
816
817 fn maybe_rebuild(&mut self) {
819 if self.batch_depth == 0 && self.delta.needs_rebuild() {
820 self.rebuild();
821 }
822 }
823
824 pub fn begin_batch(&mut self) {
826 self.batch_depth = self.batch_depth.saturating_add(1);
827 }
828
829 pub fn end_batch(&mut self) {
837 self.batch_depth = self.batch_depth.saturating_sub(1);
838 if self.batch_depth == 0 && self.delta.needs_rebuild() {
839 self.rebuild();
840 }
841 }
842
843 pub(crate) fn end_batch_deferred(&mut self) {
846 self.batch_depth = self.batch_depth.saturating_sub(1);
847 }
848
849 pub fn add_vertex(&mut self, coord: AbsCoord, vertex_id: u32) -> usize {
856 let idx = self.coords.len();
857 self.coords.push(coord);
858 self.vertex_ids.push(vertex_id);
859 self.vertex_pos.insert(vertex_id, idx);
860 idx
861 }
862
863 pub fn add_vertices_batch(&mut self, items: &[(AbsCoord, u32)]) {
865 if items.is_empty() {
866 return;
867 }
868 let start_len = self.coords.len();
869 self.coords.reserve(items.len());
870 self.vertex_ids.reserve(items.len());
871 for (coord, vid) in items {
872 let idx = self.coords.len();
873 self.coords.push(*coord);
874 self.vertex_ids.push(*vid);
875 self.vertex_pos.insert(*vid, idx);
876 }
877 self.rebuild();
879 debug_assert_eq!(self.coords.len(), start_len + items.len());
880 }
881
882 pub fn update_coord(&mut self, vertex_id: VertexId, new_coord: AbsCoord) {
885 if let Some(&pos) = self.vertex_pos.get(&vertex_id.0) {
886 debug_assert_eq!(
887 self.vertex_ids[pos], vertex_id.0,
888 "vertex_pos out of sync with vertex_ids at position {pos}"
889 );
890 self.coords[pos] = new_coord;
891 self.delta.mark_dirty();
893 }
894 }
895
896 pub fn adjacency_with_carried_forward_edges(
911 &self,
912 mut adjacency: Vec<(u32, Vec<u32>)>,
913 ) -> Vec<(u32, Vec<u32>)> {
914 let covered: FxHashSet<u32> = adjacency.iter().map(|(vid, _)| *vid).collect();
915 for &vid in &self.vertex_ids {
919 if covered.contains(&vid) {
920 continue;
921 }
922 let v = VertexId(vid);
923 let merged = if self.delta.op_count() == 0 {
925 self.base.out_edges(v).to_vec()
926 } else {
927 self.delta.merged_view(&self.base, v)
928 };
929 if !merged.is_empty() {
930 adjacency.push((vid, merged.into_iter().map(|v| v.0).collect()));
931 }
932 }
933 for (&from, adds) in self.delta.additions_iter() {
938 if covered.contains(&from.0) {
939 continue;
940 }
941 if adjacency.iter().any(|(v, _)| *v == from.0) {
942 continue;
943 }
944 let removals: FxHashSet<u32> = self.delta.removals_for(from).map(|v| v.0).collect();
945 let mut base_set: FxHashSet<u32> =
946 self.base.out_edges(from).iter().map(|v| v.0).collect();
947 for r in &removals {
948 base_set.remove(r);
949 }
950 for &add in adds {
951 base_set.insert(add.0);
952 }
953 if !base_set.is_empty() {
954 let mut targets: Vec<u32> = base_set.into_iter().collect();
955 targets.sort_unstable();
956 adjacency.push((from.0, targets));
957 }
958 }
959 adjacency
960 }
961
962 pub fn build_from_adjacency(
970 &mut self,
971 adjacency: Vec<(u32, Vec<u32>)>,
972 coords: Vec<AbsCoord>,
973 vertex_ids: Vec<u32>,
974 ) {
975 self.base = CsrEdges::from_adjacency(adjacency, &coords);
976 self.coords = coords;
977 self.vertex_ids = vertex_ids;
978 self.vertex_pos = self
979 .vertex_ids
980 .iter()
981 .enumerate()
982 .map(|(idx, &id)| (id, idx))
983 .collect();
984 self.delta.clear();
985 }
986}
987
988impl Default for CsrMutableEdges {
989 fn default() -> Self {
990 Self::new()
991 }
992}