1#![no_std]
13#![forbid(non_ascii_idents)]
14#![warn(missing_docs)]
15#![warn(let_underscore)]
16#![warn(unsafe_code)]
17#![warn(clippy::pedantic)]
18#![warn(clippy::cargo)]
19#![allow(clippy::multiple_crate_versions, reason = "Unresolvable")]
20#![warn(clippy::nursery)]
21#![warn(clippy::restriction)]
22#![allow(clippy::blanket_clippy_restriction_lints, reason = "Conflicting lint")]
23#![allow(clippy::allow_attributes, reason = "Conflicting lint")]
24#![allow(clippy::pattern_type_mismatch, reason = "Conflicting lint")]
25#![allow(clippy::separated_literal_suffix, reason = "Conflicting lint")]
26#![allow(clippy::semicolon_outside_block, reason = "Conflicting lint")]
27#![allow(
28 clippy::field_scoped_visibility_modifiers,
29 reason = "Used by IndependentWeave::from()"
30)]
31#![allow(
32 clippy::missing_inline_in_public_items,
33 reason = "Reasonable candidates have already been inlined"
34)]
35#![allow(clippy::inline_always, reason = "Performance")]
36#![allow(clippy::exhaustive_enums, reason = "API")]
37#![allow(clippy::exhaustive_structs, reason = "API")]
38#![allow(clippy::little_endian_bytes, reason = "API")]
39#![allow(clippy::partial_pub_fields, reason = "API")]
40#![allow(clippy::pub_use, reason = "API")]
41#![allow(clippy::arbitrary_source_item_ordering, reason = "Readability")]
42#![allow(clippy::question_mark_used, reason = "Readability")]
43#![allow(clippy::single_call_fn, reason = "Readability")]
44#![allow(clippy::single_char_lifetime_names, reason = "Readability")]
45#![allow(clippy::else_if_without_else, reason = "Style")]
46#![allow(clippy::if_then_some_else_none, reason = "Style")]
47#![allow(clippy::implicit_return, reason = "Style")]
48#![allow(clippy::min_ident_chars, reason = "Style")]
49#![allow(clippy::mod_module_files, reason = "Style")]
50#![allow(clippy::module_name_repetitions, reason = "Style")]
51#![allow(clippy::multiple_inherent_impl, reason = "Style")]
52#![allow(clippy::try_err, reason = "Style")]
53#![allow(clippy::allow_attributes_without_reason)] #![allow(clippy::indexing_slicing)] #![allow(clippy::unwrap_in_result)] #![allow(clippy::unwrap_used)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::shadow_unrelated)] #![allow(clippy::shadow_reuse)] #![allow(clippy::shadow_same)] mod contract;
80pub mod dependent;
81pub mod independent;
82pub mod wrappers;
83
84#[cfg(all(
85 feature = "layout",
86 any(target_pointer_width = "32", target_pointer_width = "64")
87))]
88pub mod layout;
89
90#[cfg(feature = "rkyv")]
91pub mod versioning;
92
93pub use contracts;
94pub use hashbrown;
95pub use indexmap;
96
97#[cfg(feature = "layout")]
98pub use glam;
99
100#[cfg(feature = "layout")]
101pub use tinyvec;
102
103#[cfg(feature = "rkyv")]
104pub use rkyv;
105
106#[cfg(feature = "serde")]
107pub use serde;
108
109#[cfg(feature = "loro")]
110pub use loro;
111
112extern crate alloc;
113
114use alloc::vec::Vec;
115use core::{
116 cmp::{Ordering, Reverse},
117 hash::{BuildHasher, Hash},
118};
119
120use hashbrown::{HashMap, hash_map::Entry};
121use scratchpads::{ScratchpadMap, ScratchpadSet, ScratchpadVec};
122
123#[cfg(feature = "rkyv")]
124use rkyv::collections::swiss_table::{ArchivedHashMap, ArchivedIndexSet};
125
126#[must_use]
128pub trait Node<K, T>
129where
130 K: Hash + Copy + Eq + Ord,
131{
132 type From;
134 type To;
136
137 #[must_use]
139 fn id(&self) -> K;
140 #[must_use]
142 fn from(&self) -> &Self::From;
143 #[must_use]
145 fn to(&self) -> &Self::To;
146 #[must_use]
150 fn is_active(&self) -> bool;
151 #[must_use]
153 fn contents(&self) -> &T;
154}
155
156pub trait DiscreteContents: Sized {
158 fn split(self, at: usize) -> DiscreteContentResult<Self>;
162 fn merge(self, value: Self) -> DiscreteContentResult<Self>;
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
170#[allow(missing_docs, reason = "Enum items are self-explanatory")]
171#[must_use]
172pub enum DiscreteContentResult<T> {
173 One(T),
174 Two(T, T),
175}
176
177impl DiscreteContents for () {
178 fn split(self, _at: usize) -> DiscreteContentResult<Self> {
179 DiscreteContentResult::Two((), ())
180 }
181 fn merge(self, _value: Self) -> DiscreteContentResult<Self> {
182 DiscreteContentResult::One(())
183 }
184}
185
186pub trait IndependentContents {}
188
189impl IndependentContents for () {}
190
191pub trait DeduplicatableContents {
195 #[must_use]
197 fn is_duplicate_of(&self, other: &Self) -> bool;
198}
199
200#[must_use]
210pub trait Weave<K, N, T>
211where
212 K: Hash + Copy + Eq + Ord,
213 N: Node<K, T>,
214{
215 type Nodes;
217 type Roots;
219
220 #[must_use]
222 fn len(&self) -> usize;
223 #[must_use]
225 fn is_empty(&self) -> bool;
226 #[must_use]
228 fn nodes(&self) -> &Self::Nodes;
229 #[must_use]
231 fn roots(&self) -> &Self::Roots;
232 #[must_use]
234 fn contains(&self, id: &K) -> bool;
235 #[must_use]
239 fn contains_active(&self, id: &K) -> bool;
240 #[must_use]
242 fn get(&self, id: &K) -> Option<&N>;
243 #[must_use]
245 fn get_parents(&self, id: &K) -> Option<&N::From>;
246 #[must_use]
248 fn get_children(&self, id: &K) -> Option<&N::To>;
249 #[must_use]
251 fn get_contents(&self, id: &K) -> Option<&T>;
252 fn get_ordered_identifiers(&mut self, output: &mut Vec<K>);
254 fn get_ordered_identifiers_from(&mut self, id: &K, output: &mut Vec<K>);
258 fn get_active_path(&mut self, output: &mut Vec<K>);
262 fn get_path_from(&mut self, id: &K, output: &mut Vec<K>);
266 fn insert(&mut self, node: N) -> bool;
270 fn set_active(&mut self, id: &K, value: bool) -> bool;
274 fn remove(&mut self, id: &K) -> Option<N>;
280 fn remove_tracked(&mut self, id: &K, on_removal: impl FnMut(N)) -> bool;
288 fn clear(&mut self);
292}
293
294pub trait MetadataWeave<K, N, T, M>: Weave<K, N, T>
300where
301 K: Hash + Copy + Eq + Ord,
302 N: Node<K, T>,
303{
304 #[must_use]
306 fn metadata(&self) -> &M;
307 fn metadata_mut<O>(&mut self, callback: impl FnOnce(&mut M) -> O) -> O;
313}
314
315pub trait BookmarkableWeave<K, N, T>: Weave<K, N, T>
317where
318 K: Hash + Copy + Eq + Ord,
319 N: Node<K, T>,
320{
321 type Bookmarks;
323
324 #[must_use]
326 fn bookmarks(&self) -> &Self::Bookmarks;
327 #[must_use]
329 fn contains_bookmark(&self, id: &K) -> bool;
330 fn set_bookmarked(&mut self, id: &K, value: bool) -> bool;
332}
333
334pub trait SortableWeave<K, N, T>: Weave<K, N, T>
340where
341 K: Hash + Copy + Eq + Ord,
342 N: Node<K, T>,
343{
344 fn sort_children_by(&mut self, id: &K, cmp: impl FnMut(&N, &N) -> Ordering) -> bool;
350 fn sort_children_by_id(&mut self, id: &K, cmp: impl FnMut(&K, &K) -> Ordering) -> bool;
356 fn sort_roots_by(&mut self, cmp: impl FnMut(&N, &N) -> Ordering);
362 fn sort_roots_by_id(&mut self, cmp: impl FnMut(&K, &K) -> Ordering);
368}
369
370pub trait SortableBookmarkableWeave<K, N, T>:
376 BookmarkableWeave<K, N, T> + SortableWeave<K, N, T>
377where
378 K: Hash + Copy + Eq + Ord,
379 N: Node<K, T>,
380{
381 fn sort_bookmarks_by(&mut self, cmp: impl FnMut(&N, &N) -> Ordering);
387 fn sort_bookmarks_by_id(&mut self, cmp: impl FnMut(&K, &K) -> Ordering);
393}
394
395pub trait ActiveSingularWeave<K, N, T>: Weave<K, N, T>
397where
398 K: Hash + Copy + Eq + Ord,
399 N: Node<K, T>,
400{
401 #[must_use]
403 fn active(&self) -> Option<K>;
404}
405
406pub trait ActivePathWeave<K, N, T>: Weave<K, N, T>
408where
409 K: Hash + Copy + Eq + Ord,
410 N: Node<K, T>,
411{
412 type Active;
414
415 #[must_use]
417 fn active(&self) -> &Self::Active;
418 fn set_active_path(&mut self, active: impl Iterator<Item = K>);
422}
423
424pub trait IndependentWeave<K, N, T>: Weave<K, N, T> + SemiIndependentWeave<K, N, T>
426where
427 K: Hash + Copy + Eq + Ord,
428 N: Node<K, T>,
429 T: IndependentContents,
430{
431 fn move_to(&mut self, id: &K, new_parents: &[K]) -> bool;
435}
436
437pub trait SemiIndependentWeave<K, N, T>: Weave<K, N, T>
443where
444 K: Hash + Copy + Eq + Ord,
445 N: Node<K, T>,
446 T: IndependentContents,
447{
448 #[must_use]
456 fn get_contents_mut<O>(&mut self, id: &K, callback: impl FnOnce(&mut T) -> O) -> Option<O>;
457}
458
459pub trait DiscreteWeave<K, N, T>: Weave<K, N, T>
465where
466 K: Hash + Copy + Eq + Ord,
467 N: Node<K, T>,
468 T: DiscreteContents,
469{
470 fn split(&mut self, id: &K, at: usize, new_id: K) -> bool;
480 fn merge_with_parent(&mut self, id: &K) -> Option<K>;
488}
489
490#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
492pub enum LayoutItem<K, V, P> {
493 Node {
495 id: K,
497 center: V,
499 size: V,
501 },
502 Polyline {
504 from: K,
506 to: K,
508 points: P,
510 },
511}
512
513pub trait Layouter<W, K, N, T, V, P>
519where
520 W: Weave<K, N, T>,
521 K: Hash + Copy + Eq + Ord,
522 N: Node<K, T>,
523{
524 fn layout(&mut self, weave: &mut W, sizes: impl FnMut(&K) -> V);
532 fn size(&self) -> V;
534 fn view(&mut self, min: V, max: V, callback: impl FnMut(LayoutItem<K, V, P>));
536}
537
538#[must_use]
540pub trait ImmutableWeave<K, N, T>
541where
542 K: Hash + Copy + Eq + Ord,
543 N: Node<K, T>,
544{
545 type Nodes;
547 type Roots;
549
550 #[must_use]
552 fn len(&self) -> usize;
553 #[must_use]
555 fn is_empty(&self) -> bool;
556 #[must_use]
558 fn nodes(&self) -> &Self::Nodes;
559 #[must_use]
561 fn roots(&self) -> &Self::Roots;
562 #[must_use]
564 fn contains(&self, id: &K) -> bool;
565 #[must_use]
569 fn contains_active(&self, id: &K) -> bool;
570 #[must_use]
572 fn get(&self, id: &K) -> Option<&N>;
573 #[must_use]
575 fn get_parents(&self, id: &K) -> Option<&N::From>;
576 #[must_use]
578 fn get_children(&self, id: &K) -> Option<&N::To>;
579 #[must_use]
581 fn get_contents(&self, id: &K) -> Option<&T>;
582 fn get_ordered_identifiers(&self, output: &mut Vec<K>);
584 fn get_ordered_identifiers_from(&self, id: &K, output: &mut Vec<K>);
588 fn get_active_path(&self, output: &mut Vec<K>);
592 fn get_path_from(&self, id: &K, output: &mut Vec<K>);
596}
597
598pub trait ImmutableMetadataWeave<K, N, T, M>: ImmutableWeave<K, N, T>
600where
601 K: Hash + Copy + Eq + Ord,
602 N: Node<K, T>,
603{
604 #[must_use]
606 fn metadata(&self) -> &M;
607}
608
609pub trait ImmutableBookmarkableWeave<K, N, T>: ImmutableWeave<K, N, T>
611where
612 K: Hash + Copy + Eq + Ord,
613 N: Node<K, T>,
614{
615 type Bookmarks;
617
618 #[must_use]
620 fn bookmarks(&self) -> &Self::Bookmarks;
621 #[must_use]
623 fn contains_bookmark(&self, id: &K) -> bool;
624}
625
626pub trait ImmutableActiveSingularWeave<K, N, T>: ImmutableWeave<K, N, T>
628where
629 K: Hash + Copy + Eq + Ord,
630 N: Node<K, T>,
631{
632 #[must_use]
634 fn active(&self) -> Option<K>;
635}
636
637pub trait ImmutableActivePathWeave<K, N, T>: ImmutableWeave<K, N, T>
639where
640 K: Hash + Copy + Eq + Ord,
641 N: Node<K, T>,
642{
643 type Active;
645
646 #[must_use]
648 fn active(&self) -> &Self::Active;
649}
650
651pub trait ImmutableLayouter<W, K, N, T, V, P>
657where
658 W: ImmutableWeave<K, N, T>,
659 K: Hash + Copy + Eq + Ord,
660 N: Node<K, T>,
661{
662 fn layout(&mut self, weave: &W, sizes: impl FnMut(&K) -> V);
670 fn size(&self) -> V;
672 fn view(&mut self, min: V, max: V, callback: impl FnMut(LayoutItem<K, V, P>));
674}
675
676#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
677enum Step<A, B> {
678 Enter(A),
679 Exit(B),
680}
681
682fn topological_sort<'a, K, N, T, S>(
683 nodes: &'a HashMap<K, N, S>,
684 roots: impl DoubleEndedIterator<Item = K>,
685 stack: &mut ScratchpadVec<'_, K>,
686 mut identifier_callback: impl FnMut(K),
687 identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
688) where
689 K: Hash + Copy + Eq + Ord + 'a,
690 N: Node<K, T> + 'a,
691 <N as Node<K, T>>::From: 'a,
692 <N as Node<K, T>>::To: 'a,
693 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
694 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
695 S: BuildHasher + Default + Clone,
696{
697 identifier_map.extend(nodes.iter().map(|(&k, n)| (k, n.from().into_iter().len())));
698
699 stack.extend(roots.rev());
700
701 while let Some(id) = stack.pop() {
702 identifier_callback(id);
703
704 for child in nodes[&id].to().into_iter().rev().copied() {
705 let remaining = identifier_map.get_mut(&child).unwrap();
706 #[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
707 {
708 *remaining -= 1;
709 }
710
711 if *remaining == 0 {
712 stack.push(child);
713 }
714 }
715 }
716}
717
718#[cfg(feature = "rkyv")]
719fn archived_topological_sort<'a, K, N, T, S>(
720 nodes: &'a ArchivedHashMap<K, N>,
721 roots: &'a ArchivedIndexSet<K>,
722 stack: &mut ScratchpadVec<'_, K>,
723 mut identifier_callback: impl FnMut(K),
724 identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
725) where
726 K: Hash + Copy + Eq + Ord + 'a,
727 N: Node<K, T, From = ArchivedIndexSet<K>, To = ArchivedIndexSet<K>> + 'a,
728 S: BuildHasher + Default + Clone,
729{
730 identifier_map.extend(nodes.iter().map(|(&k, n)| (k, n.from().len())));
731
732 stack.extend(archived_set_reverse_order(roots));
733
734 while let Some(id) = stack.pop() {
735 identifier_callback(id);
736
737 for child in archived_set_reverse_order(nodes[&id].to()).copied() {
738 let remaining = identifier_map.get_mut(&child).unwrap();
739 #[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
740 {
741 *remaining -= 1;
742 }
743
744 if *remaining == 0 {
745 stack.push(child);
746 }
747 }
748 }
749}
750
751fn topological_sort_subgraph<'a, K, N, T, S>(
752 nodes: &'a HashMap<K, N, S>,
753 filter: impl Fn(&K) -> bool,
754 subgraph_root: K,
755 stack: &mut ScratchpadVec<'_, K>,
756 mut identifier_callback: impl FnMut(K),
757 identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
758) where
759 K: Hash + Copy + Eq + Ord + 'a,
760 N: Node<K, T> + 'a,
761 <N as Node<K, T>>::From: 'a,
762 <N as Node<K, T>>::To: 'a,
763 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
764 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
765 S: BuildHasher + Default + Clone,
766{
767 stack.push(subgraph_root);
780
781 while let Some(id) = stack.pop() {
782 identifier_callback(id);
783
784 for child in nodes[&id].to().into_iter().rev().copied() {
785 if !filter(&child) {
786 continue;
787 }
788
789 let remaining = identifier_map.entry(child).or_insert_with(|| {
790 nodes[&child]
791 .from()
792 .into_iter()
793 .filter(|&parent| filter(parent))
794 .count()
795 });
796 #[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
797 {
798 *remaining -= 1;
799 }
800
801 if *remaining == 0 {
802 stack.push(child);
803 }
804 }
805 }
806}
807
808#[cfg(feature = "rkyv")]
809fn archived_topological_sort_subgraph<'a, K, N, T, S>(
810 nodes: &'a ArchivedHashMap<K, N>,
811 filter: impl Fn(&K) -> bool,
812 subgraph_root: K,
813 stack: &mut ScratchpadVec<'_, K>,
814 mut identifier_callback: impl FnMut(K),
815 identifier_map: &mut ScratchpadMap<'_, K, usize, S>,
816) where
817 K: Hash + Copy + Eq + Ord + 'a,
818 N: Node<K, T, From = ArchivedIndexSet<K>, To = ArchivedIndexSet<K>> + 'a,
819 S: BuildHasher + Default + Clone,
820{
821 stack.push(subgraph_root);
834
835 while let Some(id) = stack.pop() {
836 identifier_callback(id);
837
838 for child in archived_set_reverse_order(nodes[&id].to()).copied() {
839 if !filter(&child) {
840 continue;
841 }
842
843 let remaining = identifier_map.entry(child).or_insert_with(|| {
844 nodes[&child]
845 .from()
846 .iter()
847 .filter(|&parent| filter(parent))
848 .count()
849 });
850 #[allow(clippy::arithmetic_side_effects, reason = "Can never underflow")]
851 {
852 *remaining -= 1;
853 }
854
855 if *remaining == 0 {
856 stack.push(child);
857 }
858 }
859 }
860}
861
862fn shortest_path_to_ancestor<'a, K, N, T, S>(
863 nodes: &'a HashMap<K, N, S>,
864 id: &'a K,
865 target: impl Fn(&'a N) -> bool,
866 scratchpad: &mut ScratchpadVec<'_, K>,
867 scratchpad_map: &mut ScratchpadMap<'_, K, K, S>,
868 path: &mut Vec<K>,
869) where
870 K: Hash + Copy + Eq + Ord + 'a,
871 N: Node<K, T> + 'a,
872 <N as Node<K, T>>::From: 'a,
873 <N as Node<K, T>>::To: 'a,
874 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
875 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
876 S: BuildHasher + Default + Clone,
877{
878 scratchpad.push(*id);
879 scratchpad_map.insert(*id, *id);
880
881 let mut head = 0;
882
883 while head < scratchpad.len() {
884 let id = scratchpad[head];
885 #[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
886 {
887 head += 1;
888 }
889
890 let node = &nodes[&id];
891
892 if target(node) {
893 path.push(id);
894 break;
895 }
896
897 for parent in node.from().into_iter().copied() {
898 if let Entry::Vacant(entry) = scratchpad_map.entry(parent) {
899 entry.insert(id);
900 scratchpad.push(parent);
901 }
902 }
903 }
904
905 while let Some(last) = path.last()
906 && last != id
907 {
908 path.push(scratchpad_map[last]);
909 }
910}
911
912#[cfg(feature = "rkyv")]
913fn archived_shortest_path_to_ancestor<'a, K, N, T, S>(
914 nodes: &'a ArchivedHashMap<K, N>,
915 id: &'a K,
916 target: impl Fn(&'a N) -> bool,
917 scratchpad: &mut ScratchpadVec<'_, K>,
918 scratchpad_map: &mut ScratchpadMap<'_, K, K, S>,
919 path: &mut Vec<K>,
920) where
921 K: Hash + Copy + Eq + Ord + 'a,
922 N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
923 S: BuildHasher + Default + Clone,
924{
925 scratchpad.push(*id);
926 scratchpad_map.insert(*id, *id);
927
928 let mut head = 0;
929
930 while head < scratchpad.len() {
931 let id = scratchpad[head];
932 #[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
933 {
934 head += 1;
935 }
936
937 let node = &nodes[&id];
938
939 if target(node) {
940 path.push(id);
941 break;
942 }
943
944 for parent in node.from().iter().copied() {
945 if let Entry::Vacant(entry) = scratchpad_map.entry(parent) {
946 entry.insert(id);
947 scratchpad.push(parent);
948 }
949 }
950 }
951
952 while let Some(last) = path.last()
953 && last != id
954 {
955 path.push(scratchpad_map[last]);
956 }
957}
958
959fn longest_candidate_path_to_root<'a, K, N, T, S>(
960 nodes: &'a HashMap<K, N, S>,
961 topological_order: &[K],
962 is_candidate: impl Fn(&K) -> bool,
963 scratchpad_map: &mut ScratchpadMap<'_, K, (usize, K), S>,
964 mut reversed_path_callback: impl FnMut(K),
965) where
966 K: Hash + Copy + Eq + Ord + 'a,
967 N: Node<K, T> + 'a,
968 <N as Node<K, T>>::From: 'a,
969 <N as Node<K, T>>::To: 'a,
970 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
971 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
972 S: BuildHasher + Default + Clone,
973{
974 let mut longest_distance = None;
975
976 for id in topological_order {
977 if !is_candidate(id) {
978 continue;
979 }
980
981 let from = nodes[id].from().into_iter();
982
983 let has_parents = from.len() != 0;
984 let best_parent = from
985 .filter_map(|id| scratchpad_map.get(id).map(|v| (v.0, id)))
986 .min_by_key(|&(v, _)| Reverse(v));
987
988 #[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
989 let distance = match best_parent {
990 Some((parent_distance, parent)) => Some((parent_distance + 1, *parent)),
991 None => {
992 if has_parents {
993 None
994 } else {
995 Some((0, *id))
996 }
997 }
998 };
999
1000 if let Some((distance, parent)) = distance {
1001 scratchpad_map.insert(*id, (distance, parent));
1002
1003 if longest_distance.is_none_or(|(value, _)| distance > value) {
1004 longest_distance = Some((distance, *id));
1005 }
1006 }
1007 }
1008
1009 if let Some(mut id) = longest_distance.map(|(_, id)| id) {
1010 loop {
1011 reversed_path_callback(id);
1012
1013 let parent = scratchpad_map[&id].1;
1014 if parent == id {
1015 break;
1016 }
1017 id = parent;
1018 }
1019 }
1020}
1021
1022#[cfg(feature = "rkyv")]
1023fn archived_longest_candidate_path_to_root<'a, K, N, T, S>(
1024 nodes: &'a ArchivedHashMap<K, N>,
1025 topological_order: &'a [K],
1026 is_candidate: impl Fn(&K) -> bool,
1027 scratchpad_map: &mut ScratchpadMap<'_, K, (usize, K), S>,
1028 mut reversed_path_callback: impl FnMut(K),
1029) where
1030 K: Hash + Copy + Eq + Ord + 'a,
1031 N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
1032 S: BuildHasher + Default + Clone,
1033{
1034 let mut longest_distance = None;
1035
1036 for id in topological_order {
1037 if !is_candidate(id) {
1038 continue;
1039 }
1040
1041 let from = nodes[id].from();
1042
1043 let has_parents = !from.is_empty();
1044 let best_parent = from
1045 .iter()
1046 .filter_map(|id| scratchpad_map.get(id).map(|v| (v.0, id)))
1047 .min_by_key(|&(v, _)| Reverse(v));
1048
1049 #[allow(clippy::arithmetic_side_effects, reason = "Can never overflow")]
1050 let distance = match best_parent {
1051 Some((parent_distance, parent)) => Some((parent_distance + 1, *parent)),
1052 None => {
1053 if has_parents {
1054 None
1055 } else {
1056 Some((0, *id))
1057 }
1058 }
1059 };
1060
1061 if let Some((distance, parent)) = distance {
1062 scratchpad_map.insert(*id, (distance, parent));
1063
1064 if longest_distance.is_none_or(|(value, _)| distance > value) {
1065 longest_distance = Some((distance, *id));
1066 }
1067 }
1068 }
1069
1070 if let Some(mut id) = longest_distance.map(|(_, id)| id) {
1071 loop {
1072 reversed_path_callback(id);
1073
1074 let parent = scratchpad_map[&id].1;
1075 if parent == id {
1076 break;
1077 }
1078 id = parent;
1079 }
1080 }
1081}
1082
1083fn ancestor_subgraph<'a, K, N, T, S>(
1084 nodes: &'a HashMap<K, N, S>,
1085 id: K,
1086 stack: &mut ScratchpadVec<'_, K>,
1087 identifiers: &mut ScratchpadSet<'_, K, S>,
1088 mut root_callback: impl FnMut(K),
1089) where
1090 K: Hash + Copy + Eq + Ord + 'a,
1091 N: Node<K, T>,
1092 <N as Node<K, T>>::From: 'a,
1093 <N as Node<K, T>>::To: 'a,
1094 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator + ExactSizeIterator>,
1095 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1096 S: BuildHasher + Default + Clone,
1097{
1098 if identifiers.insert(id) {
1099 stack.push(id);
1100 }
1101
1102 while let Some(id) = stack.pop() {
1103 let from = nodes[&id].from().into_iter();
1104
1105 if from.len() == 0 {
1106 root_callback(id);
1107 } else {
1108 for parent in from.rev().copied() {
1109 if identifiers.insert(parent) {
1110 stack.push(parent);
1111 }
1112 }
1113 }
1114 }
1115}
1116
1117fn ancestor_subgraph_reaches<'a, K, N, T, S>(
1118 nodes: &'a HashMap<K, N, S>,
1119 ids: impl DoubleEndedIterator<Item = K>,
1120 target: impl Fn(&K) -> bool,
1121 stack: &mut ScratchpadVec<'_, K>,
1122 identifiers: &mut ScratchpadSet<'_, K, S>,
1123) -> bool
1124where
1125 K: Hash + Copy + Eq + Ord + 'a,
1126 N: Node<K, T>,
1127 <N as Node<K, T>>::From: 'a,
1128 <N as Node<K, T>>::To: 'a,
1129 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1130 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1131 S: BuildHasher + Default + Clone,
1132{
1133 for id in ids.rev() {
1134 if identifiers.insert(id) {
1135 if target(&id) {
1136 return true;
1137 }
1138
1139 stack.push(id);
1140 }
1141 }
1142
1143 while let Some(id) = stack.pop() {
1144 for parent in nodes[&id].from().into_iter().rev().copied() {
1145 if identifiers.insert(parent) {
1146 if target(&parent) {
1147 return true;
1148 }
1149
1150 stack.push(parent);
1151 }
1152 }
1153 }
1154
1155 false
1156}
1157
1158#[cfg(feature = "rkyv")]
1159fn archived_ancestor_subgraph<'a, K, N, T, S>(
1160 nodes: &'a ArchivedHashMap<K, N>,
1161 id: K,
1162 stack: &mut ScratchpadVec<'_, K>,
1163 identifiers: &mut ScratchpadSet<'_, K, S>,
1164 mut root_callback: impl FnMut(K),
1165) where
1166 K: Hash + Copy + Eq + Ord + 'a,
1167 N: Node<K, T, From = ArchivedIndexSet<K>> + 'a,
1168 S: BuildHasher + Default + Clone,
1169{
1170 if identifiers.insert(id) {
1171 stack.push(id);
1172 }
1173
1174 while let Some(id) = stack.pop() {
1175 let from = nodes[&id].from();
1176
1177 if from.is_empty() {
1178 root_callback(id);
1179 } else {
1180 for parent in archived_set_reverse_order(from).copied() {
1181 if identifiers.insert(parent) {
1182 stack.push(parent);
1183 }
1184 }
1185 }
1186 }
1187}
1188
1189fn descendant_subgraph<'a, K, N, T, S>(
1190 nodes: &'a HashMap<K, N, S>,
1191 id: K,
1192 stack: &mut ScratchpadVec<'_, K>,
1193 identifiers: &mut ScratchpadSet<'_, K, S>,
1194) where
1195 K: Hash + Copy + Eq + Ord + 'a,
1196 N: Node<K, T>,
1197 <N as Node<K, T>>::From: 'a,
1198 <N as Node<K, T>>::To: 'a,
1199 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1200 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1201 S: BuildHasher + Default + Clone,
1202{
1203 if identifiers.insert(id) {
1204 stack.push(id);
1205 }
1206
1207 while let Some(id) = stack.pop() {
1208 for child in nodes[&id].to().into_iter().rev().copied() {
1209 if identifiers.insert(child) {
1210 stack.push(child);
1211 }
1212 }
1213 }
1214}
1215
1216fn descendant_subgraph_reaches<'a, K, N, T, S>(
1217 nodes: &'a HashMap<K, N, S>,
1218 ids: impl DoubleEndedIterator<Item = K>,
1219 target: impl Fn(&K) -> bool,
1220 stack: &mut ScratchpadVec<'_, K>,
1221 identifiers: &mut ScratchpadSet<'_, K, S>,
1222) -> bool
1223where
1224 K: Hash + Copy + Eq + Ord + 'a,
1225 N: Node<K, T>,
1226 <N as Node<K, T>>::From: 'a,
1227 <N as Node<K, T>>::To: 'a,
1228 &'a N::From: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1229 &'a N::To: IntoIterator<Item = &'a K, IntoIter: DoubleEndedIterator>,
1230 S: BuildHasher + Default + Clone,
1231{
1232 for id in ids.rev() {
1233 if identifiers.insert(id) {
1234 if target(&id) {
1235 return true;
1236 }
1237
1238 stack.push(id);
1239 }
1240 }
1241
1242 while let Some(id) = stack.pop() {
1243 for child in nodes[&id].to().into_iter().rev().copied() {
1244 if identifiers.insert(child) {
1245 if target(&child) {
1246 return true;
1247 }
1248
1249 stack.push(child);
1250 }
1251 }
1252 }
1253
1254 false
1255}
1256
1257#[cfg(feature = "rkyv")]
1258fn archived_descendant_subgraph<'a, K, N, T, S>(
1259 nodes: &'a ArchivedHashMap<K, N>,
1260 id: K,
1261 stack: &mut ScratchpadVec<'_, K>,
1262 identifiers: &mut ScratchpadSet<'_, K, S>,
1263) where
1264 K: Hash + Copy + Eq + Ord + 'a,
1265 N: Node<K, T, To = ArchivedIndexSet<K>> + 'a,
1266 S: BuildHasher + Default + Clone,
1267{
1268 if identifiers.insert(id) {
1269 stack.push(id);
1270 }
1271
1272 while let Some(id) = stack.pop() {
1273 for child in archived_set_reverse_order(nodes[&id].to()).copied() {
1274 if identifiers.insert(child) {
1275 stack.push(child);
1276 }
1277 }
1278 }
1279}
1280
1281#[cfg(feature = "rkyv")]
1282fn archived_set_reverse_order<T>(set: &ArchivedIndexSet<T>) -> impl Iterator<Item = &T> {
1283 (0..set.len())
1284 .rev()
1285 .filter_map(|index| set.get_index(index))
1286}