@@ -34,12 +34,20 @@ use std::collections::HashMap;
3434use smallvec:: SmallVec ;
3535
3636use crate :: arena:: Handle ;
37+ use crate :: arena:: List ;
38+ use crate :: arena:: ListArena ;
39+ use crate :: graph:: Edge ;
3740use crate :: graph:: Node ;
41+ use crate :: graph:: StackGraph ;
3842use crate :: partial:: PartialPath ;
43+ use crate :: partial:: PartialPaths ;
3944use crate :: paths:: Path ;
45+ use crate :: paths:: PathResolutionError ;
46+ use crate :: stitching:: Database ;
47+ use crate :: stitching:: OwnedOrDatabasePath ;
4048
41- /// Helps detect cycles in the path-finding algorithm.
42- pub struct CycleDetector < P > {
49+ /// Helps detect similar paths in the path-finding algorithm.
50+ pub struct SimilarPathDetector < P > {
4351 paths : HashMap < PathKey , SmallVec < [ P ; 8 ] > > ,
4452}
4553
@@ -86,13 +94,13 @@ impl HasPathKey for PartialPath {
8694
8795const MAX_SIMILAR_PATH_COUNT : usize = 7 ;
8896
89- impl < P > CycleDetector < P >
97+ impl < P > SimilarPathDetector < P >
9098where
9199 P : HasPathKey ,
92100{
93101 /// Creates a new, empty cycle detector.
94- pub fn new ( ) -> CycleDetector < P > {
95- CycleDetector {
102+ pub fn new ( ) -> SimilarPathDetector < P > {
103+ SimilarPathDetector {
96104 paths : HashMap :: new ( ) ,
97105 }
98106 }
@@ -127,3 +135,144 @@ where
127135 true
128136 }
129137}
138+
139+ // ----------------------------------------------------------------------------
140+ // Cycle detector
141+
142+ pub trait Appendable {
143+ type Ctx ;
144+
145+ fn append_to (
146+ & self ,
147+ graph : & StackGraph ,
148+ partials : & mut PartialPaths ,
149+ ctx : & mut Self :: Ctx ,
150+ path : & mut PartialPath ,
151+ ) -> Result < ( ) , PathResolutionError > ;
152+ fn start_node ( & self , ctx : & mut Self :: Ctx ) -> Handle < Node > ;
153+ fn end_node ( & self , ctx : & mut Self :: Ctx ) -> Handle < Node > ;
154+ }
155+
156+ #[ derive( Clone ) ]
157+ pub struct AppendingCycleDetector < A > {
158+ appendages : List < A > ,
159+ }
160+
161+ pub type Appendables < A > = ListArena < A > ;
162+
163+ impl < A : Appendable + Clone > AppendingCycleDetector < A > {
164+ pub fn new ( ) -> Self {
165+ Self {
166+ appendages : List :: empty ( ) ,
167+ }
168+ }
169+
170+ pub fn from ( appendables : & mut Appendables < A > , appendage : A ) -> Self {
171+ let mut result = Self :: new ( ) ;
172+ result. appendages . push_front ( appendables, appendage) ;
173+ result
174+ }
175+
176+ pub fn append (
177+ & mut self ,
178+ graph : & StackGraph ,
179+ partials : & mut PartialPaths ,
180+ ctx : & mut A :: Ctx ,
181+ appendables : & mut Appendables < A > ,
182+ appendage : A ,
183+ ) -> Result < ( ) , PathResolutionError > {
184+ let end_node = appendage. end_node ( ctx) ;
185+ self . appendages . push_front ( appendables, appendage) ;
186+
187+ let mut maybe_cyclic_path = None ;
188+ let mut appendages = self . appendages ;
189+ loop {
190+ // get prefix elements
191+ let mut prefix_appendages = List :: empty ( ) ;
192+ loop {
193+ let appendable = appendages. pop_front ( appendables) . cloned ( ) ;
194+ match appendable {
195+ Some ( appendage) => {
196+ let is_cycle = appendage. start_node ( ctx) == end_node;
197+ prefix_appendages. push_front ( appendables, appendage) ;
198+ if is_cycle {
199+ break ;
200+ }
201+ }
202+ None => return Ok ( ( ) ) ,
203+ }
204+ }
205+
206+ // build prefix path -- prefix starts at end_node, because this is a cycle
207+ let mut prefix_path = PartialPath :: from_node ( graph, partials, end_node) ;
208+ while let Some ( appendage) = prefix_appendages. pop_front ( appendables) {
209+ prefix_path
210+ . resolve_to ( graph, partials, appendage. start_node ( ctx) )
211+ . expect ( "resolving cycle prefix path failed" ) ;
212+ appendage
213+ . append_to ( graph, partials, ctx, & mut prefix_path)
214+ . expect ( "appending cycle prefix path failed" ) ;
215+ }
216+
217+ // build cyclic path
218+ let cyclic_path = maybe_cyclic_path
219+ . unwrap_or_else ( || PartialPath :: from_node ( graph, partials, end_node) ) ;
220+ prefix_path
221+ . resolve_to ( graph, partials, cyclic_path. start_node )
222+ . expect ( "resolving cyclic path failed" ) ;
223+ prefix_path. ensure_no_overlapping_variables ( partials, & cyclic_path) ;
224+ prefix_path
225+ . concatenate ( graph, partials, & cyclic_path)
226+ . expect ( "concatenating cyclic path failed " ) ;
227+ if !prefix_path. is_productive ( graph, partials) {
228+ return Err ( PathResolutionError :: DisallowedCycle ) ;
229+ }
230+ maybe_cyclic_path = Some ( prefix_path) ;
231+ }
232+ }
233+ }
234+
235+ impl Appendable for Edge {
236+ type Ctx = ( ) ;
237+
238+ fn append_to (
239+ & self ,
240+ graph : & StackGraph ,
241+ partials : & mut PartialPaths ,
242+ _: & mut ( ) ,
243+ path : & mut PartialPath ,
244+ ) -> Result < ( ) , PathResolutionError > {
245+ path. append ( graph, partials, * self )
246+ }
247+
248+ fn start_node ( & self , _: & mut ( ) ) -> Handle < Node > {
249+ self . source
250+ }
251+
252+ fn end_node ( & self , _: & mut ( ) ) -> Handle < Node > {
253+ self . sink
254+ }
255+ }
256+
257+ impl Appendable for OwnedOrDatabasePath {
258+ type Ctx = Database ;
259+
260+ fn append_to (
261+ & self ,
262+ graph : & StackGraph ,
263+ partials : & mut PartialPaths ,
264+ db : & mut Database ,
265+ path : & mut PartialPath ,
266+ ) -> Result < ( ) , PathResolutionError > {
267+ path. ensure_no_overlapping_variables ( partials, self . get ( db) ) ;
268+ path. concatenate ( graph, partials, self . get ( db) )
269+ }
270+
271+ fn start_node ( & self , db : & mut Database ) -> Handle < Node > {
272+ self . get ( db) . start_node
273+ }
274+
275+ fn end_node ( & self , db : & mut Database ) -> Handle < Node > {
276+ self . get ( db) . end_node
277+ }
278+ }
0 commit comments