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 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
463 if let Some(rem) = self.removals.get_mut(&from) {
465 rem.remove(&to);
466 }
467 if let Some(rem) = self.removals_in.get_mut(&to) {
468 rem.remove(&from);
469 }
470 self.additions.entry(from).or_default().insert(to);
472 self.additions_in.entry(to).or_default().insert(from);
473 self.op_count += 1;
474 }
475
476 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
478 if let Some(adds) = self.additions.get_mut(&from) {
480 adds.remove(&to);
481 }
482 if let Some(adds) = self.additions_in.get_mut(&to) {
483 adds.remove(&from);
484 }
485 self.removals.entry(from).or_default().insert(to);
487 self.removals_in.entry(to).or_default().insert(from);
488 self.op_count += 1;
489 }
490
491 pub fn merged_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
493 let mut result: Vec<_> = csr.out_edges(v).to_vec();
495 if let Some(removes) = self.removals.get(&v) {
497 result.retain(|e| !removes.contains(e));
498 }
499 if let Some(adds) = self.additions.get(&v) {
501 result.extend(adds.iter().copied());
502 }
503 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
505 result.retain(|e| seen.insert(*e));
506 result.sort_by_key(|e| e.0);
507 result
508 }
509
510 pub fn merged_in_view(&self, csr: &CsrEdges, v: VertexId) -> Vec<VertexId> {
513 let mut result: Vec<_> = csr.in_edges(v).to_vec();
514 if let Some(removes) = self.removals_in.get(&v) {
515 result.retain(|e| !removes.contains(e));
516 }
517 if let Some(adds) = self.additions_in.get(&v) {
518 result.extend(adds.iter().copied());
519 }
520 let mut seen: FxHashSet<VertexId> = FxHashSet::default();
521 result.retain(|e| seen.insert(*e));
522 result.sort_by_key(|e| e.0);
523 result
524 }
525
526 pub fn needs_rebuild(&self) -> bool {
528 self.op_count >= 1000 || self.coord_changed
529 }
530
531 pub fn mark_dirty(&mut self) {
533 self.coord_changed = true;
534 }
535
536 pub fn op_count(&self) -> usize {
538 self.op_count
539 }
540
541 pub fn clear(&mut self) {
543 self.additions.clear();
544 self.removals.clear();
545 self.additions_in.clear();
546 self.removals_in.clear();
547 self.op_count = 0;
548 self.coord_changed = false;
549 }
550
551 pub fn additions_iter(&self) -> impl Iterator<Item = (&VertexId, &FxHashSet<VertexId>)> {
555 self.additions.iter()
556 }
557
558 pub fn removals_for(&self, from: VertexId) -> impl Iterator<Item = VertexId> + '_ {
560 self.removals
561 .get(&from)
562 .into_iter()
563 .flat_map(|set| set.iter().copied())
564 }
565
566 pub fn apply_to_csr(
568 &self,
569 base: &CsrEdges,
570 coords: &[AbsCoord],
571 vertex_ids: &[u32],
572 ) -> CsrEdges {
573 let mut adjacency = Vec::with_capacity(vertex_ids.len());
574
575 for &vid in vertex_ids {
577 let v = VertexId(vid);
578 let merged = self.merged_view(base, v);
579
580 let targets: Vec<u32> = merged.into_iter().map(|id| id.0).collect();
582
583 adjacency.push((vid, targets));
584 }
585
586 CsrEdges::from_adjacency(adjacency, coords)
587 }
588}
589
590impl Default for DeltaEdgeSlab {
591 fn default() -> Self {
592 Self::new()
593 }
594}
595
596#[derive(Debug)]
601pub struct CsrMutableEdges {
602 base: CsrEdges,
604
605 delta: DeltaEdgeSlab,
607
608 coords: Vec<AbsCoord>,
610
611 vertex_ids: Vec<u32>,
613
614 vertex_pos: FxHashMap<u32, usize>,
616
617 batch_depth: usize,
619
620 rebuild_count: u64,
622}
623
624impl CsrMutableEdges {
625 pub fn new() -> Self {
627 Self {
628 base: CsrEdges::empty(),
629 delta: DeltaEdgeSlab::new(),
630 coords: Vec::new(),
631 vertex_ids: Vec::new(),
632 vertex_pos: FxHashMap::default(),
633 batch_depth: 0,
634 rebuild_count: 0,
635 }
636 }
637
638 pub fn with_coords(coords: Vec<AbsCoord>) -> Self {
640 let num_vertices = coords.len();
641 let vertex_ids: Vec<u32> = (0..num_vertices as u32).collect();
642 let adjacency: Vec<_> = vertex_ids.iter().map(|&id| (id, Vec::new())).collect();
643 let vertex_pos = vertex_ids
644 .iter()
645 .enumerate()
646 .map(|(idx, &id)| (id, idx))
647 .collect();
648
649 Self {
650 base: CsrEdges::from_adjacency(adjacency, &coords),
651 delta: DeltaEdgeSlab::new(),
652 coords,
653 vertex_ids,
654 vertex_pos,
655 batch_depth: 0,
656 rebuild_count: 0,
657 }
658 }
659
660 pub fn add_edge(&mut self, from: VertexId, to: VertexId) {
662 self.delta.add_edge(from, to);
663 self.maybe_rebuild();
664 }
665
666 pub fn remove_edge(&mut self, from: VertexId, to: VertexId) {
668 self.delta.remove_edge(from, to);
669 self.maybe_rebuild();
670 }
671
672 pub fn out_edges(&self, v: VertexId) -> Vec<VertexId> {
674 if self.delta.op_count() == 0 {
675 self.base.out_edges(v).to_vec()
676 } else {
677 self.delta.merged_view(&self.base, v)
678 }
679 }
680
681 #[inline]
685 pub fn out_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
686 if self.delta.op_count() == 0 {
687 Some(self.base.out_edges(v))
688 } else {
689 None
690 }
691 }
692
693 pub fn in_edges(&self, v: VertexId) -> &[VertexId] {
696 self.base.in_edges(v)
697 }
698
699 pub fn in_edges_merged(&self, v: VertexId) -> Vec<VertexId> {
703 if self.delta.op_count() == 0 {
704 self.base.in_edges(v).to_vec()
705 } else {
706 self.delta.merged_in_view(&self.base, v)
707 }
708 }
709
710 #[inline]
714 pub fn in_edges_ref(&self, v: VertexId) -> Option<&[VertexId]> {
715 if self.delta.op_count() == 0 {
716 Some(self.base.in_edges(v))
717 } else {
718 None
719 }
720 }
721
722 pub fn delta_size(&self) -> usize {
724 self.delta.op_count()
725 }
726
727 pub fn num_edges_exact(&self) -> usize {
734 if self.delta.op_count() == 0 {
735 return self.base.num_edges();
736 }
737
738 self.vertex_ids
739 .iter()
740 .map(|&id| self.out_edges(VertexId(id)).len())
741 .sum()
742 }
743
744 pub fn rebuild(&mut self) {
746 if self.delta.op_count() > 0 || self.delta.needs_rebuild() {
747 self.base = self
748 .delta
749 .apply_to_csr(&self.base, &self.coords, &self.vertex_ids);
750 self.delta.clear();
751 self.rebuild_count += 1;
752 }
753 }
754
755 pub fn rebuild_count(&self) -> u64 {
760 self.rebuild_count
761 }
762
763 fn maybe_rebuild(&mut self) {
765 if self.batch_depth == 0 && self.delta.needs_rebuild() {
766 self.rebuild();
767 }
768 }
769
770 pub fn begin_batch(&mut self) {
772 self.batch_depth = self.batch_depth.saturating_add(1);
773 }
774
775 pub fn end_batch(&mut self) {
783 self.batch_depth = self.batch_depth.saturating_sub(1);
784 if self.batch_depth == 0 && self.delta.needs_rebuild() {
785 self.rebuild();
786 }
787 }
788
789 pub fn add_vertex(&mut self, coord: AbsCoord, vertex_id: u32) -> usize {
796 let idx = self.coords.len();
797 self.coords.push(coord);
798 self.vertex_ids.push(vertex_id);
799 self.vertex_pos.insert(vertex_id, idx);
800 idx
801 }
802
803 pub fn add_vertices_batch(&mut self, items: &[(AbsCoord, u32)]) {
805 if items.is_empty() {
806 return;
807 }
808 let start_len = self.coords.len();
809 self.coords.reserve(items.len());
810 self.vertex_ids.reserve(items.len());
811 for (coord, vid) in items {
812 let idx = self.coords.len();
813 self.coords.push(*coord);
814 self.vertex_ids.push(*vid);
815 self.vertex_pos.insert(*vid, idx);
816 }
817 self.rebuild();
819 debug_assert_eq!(self.coords.len(), start_len + items.len());
820 }
821
822 pub fn update_coord(&mut self, vertex_id: VertexId, new_coord: AbsCoord) {
825 if let Some(&pos) = self.vertex_pos.get(&vertex_id.0) {
826 debug_assert_eq!(
827 self.vertex_ids[pos], vertex_id.0,
828 "vertex_pos out of sync with vertex_ids at position {pos}"
829 );
830 self.coords[pos] = new_coord;
831 self.delta.mark_dirty();
833 }
834 }
835
836 pub fn adjacency_with_carried_forward_edges(
851 &self,
852 mut adjacency: Vec<(u32, Vec<u32>)>,
853 ) -> Vec<(u32, Vec<u32>)> {
854 let covered: FxHashSet<u32> = adjacency.iter().map(|(vid, _)| *vid).collect();
855 for &vid in &self.vertex_ids {
859 if covered.contains(&vid) {
860 continue;
861 }
862 let v = VertexId(vid);
863 let merged = if self.delta.op_count() == 0 {
865 self.base.out_edges(v).to_vec()
866 } else {
867 self.delta.merged_view(&self.base, v)
868 };
869 if !merged.is_empty() {
870 adjacency.push((vid, merged.into_iter().map(|v| v.0).collect()));
871 }
872 }
873 for (&from, adds) in self.delta.additions_iter() {
878 if covered.contains(&from.0) {
879 continue;
880 }
881 if adjacency.iter().any(|(v, _)| *v == from.0) {
882 continue;
883 }
884 let removals: FxHashSet<u32> = self.delta.removals_for(from).map(|v| v.0).collect();
885 let mut base_set: FxHashSet<u32> =
886 self.base.out_edges(from).iter().map(|v| v.0).collect();
887 for r in &removals {
888 base_set.remove(r);
889 }
890 for &add in adds {
891 base_set.insert(add.0);
892 }
893 if !base_set.is_empty() {
894 let mut targets: Vec<u32> = base_set.into_iter().collect();
895 targets.sort_unstable();
896 adjacency.push((from.0, targets));
897 }
898 }
899 adjacency
900 }
901
902 pub fn build_from_adjacency(
910 &mut self,
911 adjacency: Vec<(u32, Vec<u32>)>,
912 coords: Vec<AbsCoord>,
913 vertex_ids: Vec<u32>,
914 ) {
915 self.base = CsrEdges::from_adjacency(adjacency, &coords);
916 self.coords = coords;
917 self.vertex_ids = vertex_ids;
918 self.vertex_pos = self
919 .vertex_ids
920 .iter()
921 .enumerate()
922 .map(|(idx, &id)| (id, idx))
923 .collect();
924 self.delta.clear();
925 }
926}
927
928impl Default for CsrMutableEdges {
929 fn default() -> Self {
930 Self::new()
931 }
932}