1use crate::core::graph::EdgeId;
10use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
11use std::collections::VecDeque;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum RtMode {
16 Out,
18 In,
20 All,
22}
23
24pub fn layout_reingold_tilford(
60 graph: &Graph,
61 root: Option<VertexId>,
62 mode: RtMode,
63) -> IgraphResult<Vec<[f64; 2]>> {
64 let n = graph.vcount() as usize;
65 if n == 0 {
66 return Ok(Vec::new());
67 }
68
69 let root_v = root.unwrap_or_else(|| select_root(graph, mode));
70 if root_v >= graph.vcount() {
71 return Err(IgraphError::InvalidArgument(
72 "root vertex out of range".into(),
73 ));
74 }
75
76 let root_idx = root_v as usize;
77 let mut vdata = vec![RtVertex::new(); n];
78
79 build_spanning_tree(graph, root_idx, mode, &mut vdata)?;
81
82 postorder(&mut vdata, root_idx, n);
84
85 let mut pos = vec![[0.0_f64; 2]; n];
87 calc_coords(&vdata, &mut pos, root_idx, n, vdata[root_idx].offset);
88
89 Ok(pos)
90}
91
92pub fn layout_reingold_tilford_circular(
111 graph: &Graph,
112 root: Option<VertexId>,
113 mode: RtMode,
114) -> IgraphResult<Vec<[f64; 2]>> {
115 let mut pos = layout_reingold_tilford(graph, root, mode)?;
116 let n = pos.len();
117 if n == 0 {
118 return Ok(pos);
119 }
120
121 let ratio_base = 2.0 * std::f64::consts::PI * (n as f64 - 1.0) / n as f64;
122
123 let minx = pos.iter().map(|p| p[0]).fold(f64::INFINITY, f64::min);
124 let maxx = pos.iter().map(|p| p[0]).fold(f64::NEG_INFINITY, f64::max);
125
126 let ratio = if maxx > minx {
127 ratio_base / (maxx - minx)
128 } else {
129 ratio_base
130 };
131
132 for p in &mut pos {
133 let phi = (p[0] - minx) * ratio;
134 let r = p[1];
135 p[0] = r * phi.cos();
136 p[1] = r * phi.sin();
137 }
138
139 Ok(pos)
140}
141
142#[derive(Clone)]
147struct RtVertex {
148 parent: i64,
149 level: i64,
150 offset: f64,
151 left_contour: i64,
152 right_contour: i64,
153 offset_to_left_contour: f64,
154 offset_to_right_contour: f64,
155 left_extreme: usize,
156 right_extreme: usize,
157 offset_to_left_extreme: f64,
158 offset_to_right_extreme: f64,
159}
160
161impl RtVertex {
162 fn new() -> Self {
163 Self {
164 parent: -1,
165 level: -1,
166 offset: 0.0,
167 left_contour: -1,
168 right_contour: -1,
169 offset_to_left_contour: 0.0,
170 offset_to_right_contour: 0.0,
171 left_extreme: 0, right_extreme: 0,
173 offset_to_left_extreme: 0.0,
174 offset_to_right_extreme: 0.0,
175 }
176 }
177}
178
179fn select_root(graph: &Graph, _mode: RtMode) -> VertexId {
184 let n = graph.vcount() as usize;
185 if n == 0 {
186 return 0;
187 }
188 let mut best = 0u32;
189 let mut best_deg = 0usize;
190 for v in 0..n {
191 if let Ok(deg) = graph.degree(v as VertexId) {
192 if deg > best_deg {
193 best_deg = deg;
194 best = v as u32;
195 }
196 }
197 }
198 best
199}
200
201fn build_spanning_tree(
202 graph: &Graph,
203 root: usize,
204 mode: RtMode,
205 vdata: &mut [RtVertex],
206) -> IgraphResult<()> {
207 let n = vdata.len();
208 for i in 0..n {
209 vdata[i].left_extreme = i;
210 vdata[i].right_extreme = i;
211 }
212
213 vdata[root].parent = root as i64;
214 vdata[root].level = 0;
215
216 let mut queue = VecDeque::new();
217 queue.push_back((root, 0i64));
218
219 while let Some((node, dist)) = queue.pop_front() {
220 let neighbors = get_neighbors(graph, node as VertexId, mode);
221 for &nei in &neighbors {
222 let nei_idx = nei as usize;
223 if vdata[nei_idx].parent >= 0 {
224 continue;
225 }
226 vdata[nei_idx].parent = node as i64;
227 vdata[nei_idx].level = dist + 1;
228 queue.push_back((nei_idx, dist + 1));
229 }
230 }
231
232 for i in 0..n {
234 if vdata[i].parent < 0 {
235 vdata[i].parent = root as i64;
236 vdata[i].level = 1;
237 }
238 }
239
240 Ok(())
241}
242
243fn get_neighbors(graph: &Graph, v: VertexId, mode: RtMode) -> Vec<VertexId> {
244 let mut result = Vec::new();
245 let ecount = graph.ecount();
246 for eid in 0..ecount as u32 {
247 if let Ok((src, tgt)) = graph.edge(eid) {
248 match mode {
249 RtMode::Out => {
250 if src == v {
251 result.push(tgt);
252 }
253 }
254 RtMode::In => {
255 if tgt == v {
256 result.push(src);
257 }
258 }
259 RtMode::All => {
260 if src == v {
261 result.push(tgt);
262 } else if tgt == v {
263 result.push(src);
264 }
265 }
266 }
267 }
268 }
269 result
270}
271
272fn children_of(vdata: &[RtVertex], node: usize, n: usize) -> Vec<usize> {
273 let mut kids = Vec::new();
274 for i in 0..n {
275 if i == node {
276 continue;
277 }
278 if vdata[i].parent == node as i64 {
279 kids.push(i);
280 }
281 }
282 kids
283}
284
285fn postorder(vdata: &mut [RtVertex], node: usize, n: usize) {
286 let children = children_of(vdata, node, n);
287
288 if children.is_empty() {
289 return;
290 }
291
292 for &child in &children {
294 postorder(vdata, child, n);
295 }
296
297 let minsep = 1.0_f64;
299 let mut leftroot: i64 = -1;
300 let mut avg = 0.0_f64;
301
302 for (j, &child) in children.iter().enumerate() {
303 if leftroot >= 0 {
304 let lr = leftroot as usize;
305 let mut lnode: i64 = lr as i64;
307 let mut rnode: i64 = child as i64;
308 let mut rootsep = vdata[lr].offset + minsep;
309 let mut loffset = vdata[lr].offset;
310 let mut roffset = loffset + minsep;
311
312 vdata[node].right_contour = child as i64;
314 vdata[node].offset_to_right_contour = rootsep;
315
316 while lnode >= 0 && rnode >= 0 {
317 let ln = lnode as usize;
318 let rn = rnode as usize;
319
320 if vdata[ln].right_contour >= 0 {
322 loffset += vdata[ln].offset_to_right_contour;
323 lnode = vdata[ln].right_contour;
324 } else {
325 if vdata[rn].left_contour >= 0 {
327 let auxnode = vdata[node].left_extreme;
328 let newoffset = (vdata[node].offset_to_right_extreme
329 - vdata[node].offset_to_left_extreme)
330 + minsep
331 + vdata[rn].offset_to_left_contour;
332 vdata[auxnode].left_contour = vdata[rn].left_contour;
333 vdata[auxnode].right_contour = vdata[rn].left_contour;
334 vdata[auxnode].offset_to_left_contour = newoffset;
335 vdata[auxnode].offset_to_right_contour = newoffset;
336
337 vdata[node].left_extreme = vdata[child].left_extreme;
338 vdata[node].right_extreme = vdata[child].right_extreme;
339 vdata[node].offset_to_left_extreme =
340 vdata[child].offset_to_left_extreme + rootsep;
341 vdata[node].offset_to_right_extreme =
342 vdata[child].offset_to_right_extreme + rootsep;
343 } else {
344 vdata[node].right_extreme = vdata[child].right_extreme;
346 vdata[node].offset_to_right_extreme =
347 vdata[child].offset_to_right_extreme + rootsep;
348 }
349 lnode = -1;
350 }
351
352 let rn = rnode as usize;
354 if rnode >= 0 && vdata[rn].left_contour >= 0 {
355 roffset += vdata[rn].offset_to_left_contour;
356 rnode = vdata[rn].left_contour;
357 } else if rnode >= 0 {
358 if lnode >= 0 {
360 let auxnode = vdata[child].right_extreme;
361 let newoffset = loffset - rootsep - vdata[child].offset_to_right_extreme;
362 vdata[auxnode].left_contour = lnode;
363 vdata[auxnode].right_contour = lnode;
364 vdata[auxnode].offset_to_left_contour = newoffset;
365 vdata[auxnode].offset_to_right_contour = newoffset;
366 }
367 rnode = -1;
368 }
369
370 if lnode >= 0 && rnode >= 0 && (roffset - loffset < minsep) {
372 rootsep += minsep - roffset + loffset;
373 roffset = loffset + minsep;
374 vdata[node].offset_to_right_contour = rootsep;
375 }
376 }
377
378 vdata[child].offset = rootsep;
379 vdata[node].offset_to_right_contour = rootsep;
380 avg = (avg * j as f64) / (j as f64 + 1.0) + rootsep / (j as f64 + 1.0);
381 leftroot = child as i64;
382 } else {
383 leftroot = child as i64;
385 vdata[node].left_contour = child as i64;
386 vdata[node].right_contour = child as i64;
387 vdata[node].offset_to_left_contour = 0.0;
388 vdata[node].offset_to_right_contour = 0.0;
389 vdata[node].left_extreme = vdata[child].left_extreme;
390 vdata[node].right_extreme = vdata[child].right_extreme;
391 vdata[node].offset_to_left_extreme = vdata[child].offset_to_left_extreme;
392 vdata[node].offset_to_right_extreme = vdata[child].offset_to_right_extreme;
393 avg = vdata[child].offset;
394 }
395 }
396
397 vdata[node].offset_to_left_contour -= avg;
399 vdata[node].offset_to_right_contour -= avg;
400 vdata[node].offset_to_left_extreme -= avg;
401 vdata[node].offset_to_right_extreme -= avg;
402 for &child in &children {
403 vdata[child].offset -= avg;
404 }
405}
406
407fn calc_coords(vdata: &[RtVertex], pos: &mut [[f64; 2]], node: usize, n: usize, xpos: f64) {
408 pos[node][0] = xpos;
409 pos[node][1] = vdata[node].level as f64;
410 for i in 0..n {
411 if i == node {
412 continue;
413 }
414 if vdata[i].parent == node as i64 {
415 calc_coords(vdata, pos, i, n, xpos + vdata[i].offset);
416 }
417 }
418}
419
420#[derive(Debug, Clone, Copy, PartialEq, Eq)]
427pub enum RootChoice {
428 Degree,
431 Eccentricity,
434}
435
436pub fn roots_for_tree_layout(
478 graph: &Graph,
479 mode: RtMode,
480 heuristic: RootChoice,
481) -> IgraphResult<Vec<VertexId>> {
482 let n = graph.vcount();
483 if n == 0 {
484 return Ok(Vec::new());
485 }
486
487 let effective_mode = if graph.is_directed() {
488 mode
489 } else {
490 RtMode::All
491 };
492
493 let order = build_vertex_order(graph, effective_mode, heuristic)?;
494
495 if effective_mode == RtMode::All {
496 roots_undirected(graph, &order)
497 } else {
498 roots_directed(graph, effective_mode, &order)
499 }
500}
501
502fn build_vertex_order(
503 graph: &Graph,
504 mode: RtMode,
505 heuristic: RootChoice,
506) -> IgraphResult<Vec<VertexId>> {
507 let n = graph.vcount() as usize;
508 match heuristic {
509 RootChoice::Eccentricity => {
510 let ecc_mode = match mode {
511 RtMode::Out => crate::algorithms::paths::radii::EccMode::Out,
512 RtMode::In => crate::algorithms::paths::radii::EccMode::In,
513 RtMode::All => crate::algorithms::paths::radii::EccMode::All,
514 };
515 let ecc = crate::algorithms::paths::radii::eccentricity_with_mode(graph, ecc_mode)?;
516 let mut indices: Vec<VertexId> = (0..n as VertexId).collect();
517 indices.sort_by(|&a, &b| ecc[a as usize].cmp(&ecc[b as usize]));
518 Ok(indices)
519 }
520 RootChoice::Degree => {
521 let deg_mode = match mode {
522 RtMode::Out => crate::algorithms::properties::degree::DegreeMode::Out,
523 RtMode::In => crate::algorithms::properties::degree::DegreeMode::In,
524 RtMode::All => crate::algorithms::properties::degree::DegreeMode::All,
525 };
526 Ok(
527 crate::algorithms::properties::sort_by_degree::sort_vertices_by_degree(
528 graph,
529 deg_mode,
530 crate::algorithms::properties::sort_by_degree::SortOrder::Descending,
531 )?,
532 )
533 }
534 }
535}
536
537fn roots_undirected(graph: &Graph, order: &[VertexId]) -> IgraphResult<Vec<VertexId>> {
538 let comps = crate::algorithms::connectivity::components::connected_components(graph)?;
539 let no_comps = comps.count as usize;
540
541 let mut roots = vec![u32::MAX; no_comps];
542 let mut seen = 0usize;
543
544 for &v in order {
545 let cl = comps.membership[v as usize] as usize;
546 if roots[cl] == u32::MAX {
547 roots[cl] = v;
548 seen += 1;
549 if seen == no_comps {
550 break;
551 }
552 }
553 }
554
555 Ok(roots)
556}
557
558fn roots_directed(graph: &Graph, mode: RtMode, order: &[VertexId]) -> IgraphResult<Vec<VertexId>> {
559 let comps = crate::algorithms::connectivity::strong::strongly_connected_components(graph)?;
560 let no_comps = comps.count as usize;
561
562 let cluster_incoming = cluster_cross_degrees(graph, &comps.membership, no_comps, mode)?;
563
564 let mut roots: Vec<Option<VertexId>> = vec![None; no_comps];
565
566 for &v in order {
567 let cl = comps.membership[v as usize] as usize;
568 if cluster_incoming[cl] == 0 && roots[cl].is_none() {
569 roots[cl] = Some(v);
570 }
571 }
572
573 Ok(roots.into_iter().flatten().collect())
574}
575
576fn cluster_cross_degrees(
580 graph: &Graph,
581 membership: &[u32],
582 no_comps: usize,
583 mode: RtMode,
584) -> IgraphResult<Vec<u32>> {
585 let mut degrees = vec![0u32; no_comps];
586 let m = graph.ecount();
587
588 for eid in 0..m as EdgeId {
589 let (from, to) = graph.edge(eid)?;
590 let from_cl = membership[from as usize];
591 let to_cl = membership[to as usize];
592
593 if from_cl != to_cl {
594 let cl = if mode == RtMode::Out { to_cl } else { from_cl };
595 degrees[cl as usize] = degrees[cl as usize].saturating_add(1);
596 }
597 }
598
599 Ok(degrees)
600}
601
602#[cfg(test)]
607mod tests {
608 use super::*;
609
610 fn binary_tree() -> Graph {
611 let mut g = Graph::with_vertices(7);
613 g.add_edge(0, 1).unwrap();
614 g.add_edge(0, 2).unwrap();
615 g.add_edge(1, 3).unwrap();
616 g.add_edge(1, 4).unwrap();
617 g.add_edge(2, 5).unwrap();
618 g.add_edge(2, 6).unwrap();
619 g
620 }
621
622 #[test]
623 fn test_rt_binary_tree_levels() {
624 let g = binary_tree();
625 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
626 assert!((pos[0][1]).abs() < 1e-10);
628 assert!((pos[1][1] - 1.0).abs() < 1e-10);
630 assert!((pos[2][1] - 1.0).abs() < 1e-10);
631 assert!((pos[3][1] - 2.0).abs() < 1e-10);
633 assert!((pos[6][1] - 2.0).abs() < 1e-10);
634 }
635
636 #[test]
637 fn test_rt_binary_tree_symmetry() {
638 let g = binary_tree();
639 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
640 let root_x = pos[0][0];
642 let left_child_x = pos[1][0];
643 let right_child_x = pos[2][0];
644 let diff = (left_child_x - root_x) + (right_child_x - root_x);
646 assert!(diff.abs() < 1e-10, "children not symmetric: diff={diff}");
647 }
648
649 #[test]
650 fn test_rt_path() {
651 let mut g = Graph::with_vertices(4);
653 g.add_edge(0, 1).unwrap();
654 g.add_edge(1, 2).unwrap();
655 g.add_edge(2, 3).unwrap();
656 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
657 for i in 0..4 {
659 assert!((pos[i][0] - pos[0][0]).abs() < 1e-10);
660 assert!((pos[i][1] - i as f64).abs() < 1e-10);
661 }
662 }
663
664 #[test]
665 fn test_rt_single_vertex() {
666 let g = Graph::with_vertices(1);
667 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
668 assert_eq!(pos.len(), 1);
669 assert!((pos[0][0]).abs() < 1e-10);
670 assert!((pos[0][1]).abs() < 1e-10);
671 }
672
673 #[test]
674 fn test_rt_empty() {
675 let g = Graph::with_vertices(0);
676 let pos = layout_reingold_tilford(&g, None, RtMode::All).unwrap();
677 assert!(pos.is_empty());
678 }
679
680 #[test]
681 fn test_rt_star() {
682 let mut g = Graph::with_vertices(5);
684 for i in 1..5 {
685 g.add_edge(0, i).unwrap();
686 }
687 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
688 for i in 1..5 {
690 assert!((pos[i][1] - 1.0).abs() < 1e-10);
691 }
692 let mut xs: Vec<f64> = (1..5).map(|i| pos[i][0]).collect();
694 xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
695 for i in 1..xs.len() {
696 assert!((xs[i] - xs[i - 1] - 1.0).abs() < 1e-10);
697 }
698 }
699
700 #[test]
701 fn test_rt_disconnected() {
702 let mut g = Graph::with_vertices(4);
704 g.add_edge(0, 1).unwrap();
705 g.add_edge(2, 3).unwrap();
706 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
707 assert_eq!(pos.len(), 4);
708 for p in &pos {
710 assert!(p[0].is_finite());
711 assert!(p[1].is_finite());
712 }
713 }
714
715 #[test]
716 fn test_rt_circular_basic() {
717 let g = binary_tree();
718 let pos = layout_reingold_tilford_circular(&g, Some(0), RtMode::All).unwrap();
719 assert_eq!(pos.len(), 7);
720 assert!(pos[0][0].abs() < 1e-10);
722 assert!(pos[0][1].abs() < 1e-10);
723 }
724
725 #[test]
726 fn test_rt_no_overlap() {
727 let g = binary_tree();
728 let pos = layout_reingold_tilford(&g, Some(0), RtMode::All).unwrap();
729 for i in 0..pos.len() {
731 for j in (i + 1)..pos.len() {
732 if (pos[i][1] - pos[j][1]).abs() < 1e-10 {
733 assert!(
734 (pos[i][0] - pos[j][0]).abs() >= 1.0 - 1e-10,
735 "nodes {i} and {j} overlap: x[{i}]={}, x[{j}]={}",
736 pos[i][0],
737 pos[j][0]
738 );
739 }
740 }
741 }
742 }
743
744 #[test]
745 fn test_rt_auto_root() {
746 let g = binary_tree();
747 let pos = layout_reingold_tilford(&g, None, RtMode::All).unwrap();
748 assert_eq!(pos.len(), 7);
749 assert!((pos[1][1]).abs() < 1e-10);
751 }
752
753 #[test]
754 fn test_rt_invalid_root() {
755 let g = Graph::with_vertices(3);
756 let result = layout_reingold_tilford(&g, Some(99), RtMode::All);
757 assert!(result.is_err());
758 }
759
760 #[test]
763 fn roots_empty_graph() {
764 let g = Graph::with_vertices(0);
765 let roots = roots_for_tree_layout(&g, RtMode::All, RootChoice::Degree).unwrap();
766 assert!(roots.is_empty());
767 }
768
769 #[test]
770 fn roots_single_vertex() {
771 let g = Graph::with_vertices(1);
772 let roots = roots_for_tree_layout(&g, RtMode::All, RootChoice::Degree).unwrap();
773 assert_eq!(roots, vec![0]);
774 }
775
776 #[test]
777 fn roots_undirected_single_component() {
778 let mut g = Graph::with_vertices(5);
780 g.add_edge(0, 1).unwrap();
781 g.add_edge(1, 2).unwrap();
782 g.add_edge(2, 3).unwrap();
783 g.add_edge(3, 4).unwrap();
784 let roots = roots_for_tree_layout(&g, RtMode::All, RootChoice::Degree).unwrap();
785 assert_eq!(roots.len(), 1);
786 assert!(
788 [1, 2, 3].contains(&roots[0]),
789 "expected interior vertex, got {}",
790 roots[0]
791 );
792 }
793
794 #[test]
795 fn roots_undirected_two_components() {
796 let mut g = Graph::with_vertices(5);
798 g.add_edge(0, 1).unwrap();
799 g.add_edge(2, 3).unwrap();
800 g.add_edge(3, 4).unwrap();
801 let roots = roots_for_tree_layout(&g, RtMode::All, RootChoice::Degree).unwrap();
802 assert_eq!(roots.len(), 2);
803 let mut sorted = roots.clone();
804 sorted.sort_unstable();
805 assert!(sorted[0] <= 1);
807 assert!(sorted[1] >= 2 && sorted[1] <= 4);
808 }
809
810 #[test]
811 fn roots_eccentricity_path() {
812 let mut g = Graph::with_vertices(5);
814 g.add_edge(0, 1).unwrap();
815 g.add_edge(1, 2).unwrap();
816 g.add_edge(2, 3).unwrap();
817 g.add_edge(3, 4).unwrap();
818 let roots = roots_for_tree_layout(&g, RtMode::All, RootChoice::Eccentricity).unwrap();
819 assert_eq!(roots.len(), 1);
820 assert_eq!(roots[0], 2, "eccentricity centre of P5 is vertex 2");
821 }
822
823 #[test]
824 fn roots_directed_dag() {
825 let mut g = Graph::new(4, true).unwrap();
827 g.add_edge(0, 1).unwrap();
828 g.add_edge(0, 2).unwrap();
829 g.add_edge(1, 3).unwrap();
830 g.add_edge(2, 3).unwrap();
831 let roots = roots_for_tree_layout(&g, RtMode::Out, RootChoice::Degree).unwrap();
832 assert_eq!(roots, vec![0]);
834 }
835
836 #[test]
837 fn roots_directed_two_sources() {
838 let mut g = Graph::new(3, true).unwrap();
840 g.add_edge(0, 2).unwrap();
841 g.add_edge(1, 2).unwrap();
842 let roots = roots_for_tree_layout(&g, RtMode::Out, RootChoice::Degree).unwrap();
843 assert_eq!(roots.len(), 2);
844 let mut sorted = roots.clone();
845 sorted.sort_unstable();
846 assert_eq!(sorted, vec![0, 1]);
847 }
848
849 #[test]
850 fn roots_directed_in_mode() {
851 let mut g = Graph::new(3, true).unwrap();
853 g.add_edge(0, 1).unwrap();
854 g.add_edge(1, 2).unwrap();
855 let roots = roots_for_tree_layout(&g, RtMode::In, RootChoice::Degree).unwrap();
856 assert_eq!(roots, vec![2]);
857 }
858
859 #[test]
860 fn roots_directed_cycle_scc() {
861 let mut g = Graph::new(4, true).unwrap();
864 g.add_edge(0, 1).unwrap();
865 g.add_edge(1, 2).unwrap();
866 g.add_edge(2, 0).unwrap();
867 g.add_edge(3, 0).unwrap();
868 let roots = roots_for_tree_layout(&g, RtMode::Out, RootChoice::Degree).unwrap();
869 assert_eq!(roots, vec![3]);
870 }
871
872 #[test]
873 fn roots_undirected_ignores_mode() {
874 let mut g = Graph::with_vertices(3);
876 g.add_edge(0, 1).unwrap();
877 g.add_edge(1, 2).unwrap();
878 let roots_out = roots_for_tree_layout(&g, RtMode::Out, RootChoice::Degree).unwrap();
879 let roots_all = roots_for_tree_layout(&g, RtMode::All, RootChoice::Degree).unwrap();
880 assert_eq!(roots_out, roots_all);
881 }
882}