99//! at most one method per relationship can be eligible at any given point (the inputs of
1010//! each method are the outputs of the other methods), so selection is deterministic.
1111//!
12+ //! **Pre-claiming**: when a determined cell eliminates all but one feasible method for a
13+ //! relationship (a method is infeasible if it would overwrite a determined or pre-claimed
14+ //! cell), that sole method's outputs are *pre-claimed*: they can never become sources, even
15+ //! before all of the method's inputs are determined. Excluding pre-claimed outputs from
16+ //! feasibility prevents a method whose output is pre-claimed by the current flood-fill pass
17+ //! from being counted as a second viable option. This allows the planner to correctly handle
18+ //! constraints where the highest-strength cell is one of several inputs to the selected
19+ //! method.
20+ //!
1221//! Because a method is only selected once all its inputs are determined, any method that
1322//! writes an input to a later method necessarily appears earlier in the selection order.
1423//! The selection order is therefore already a valid topological execution order.
@@ -43,14 +52,15 @@ pub(crate) fn plan(
4352 relationships : & SlotMap < RelationshipId , RelationshipData > ,
4453) -> Result < Plan , Error > {
4554 let mut determined: HashSet < CellId > = HashSet :: new ( ) ;
55+ let mut pre_claimed: HashSet < CellId > = HashSet :: new ( ) ;
4656 let mut selected: Vec < ( RelationshipId , usize ) > = Vec :: new ( ) ;
4757 let mut selected_set: HashSet < RelationshipId > = HashSet :: new ( ) ;
4858
4959 let mut cells_sorted: Vec < CellId > = cells. keys ( ) . collect ( ) ;
5060 cells_sorted. sort_by_key ( |& id| Reverse ( cells[ id] . strength ) ) ;
5161
5262 for & source in & cells_sorted {
53- if determined. contains ( & source) {
63+ if determined. contains ( & source) || pre_claimed . contains ( & source ) {
5464 continue ;
5565 }
5666 determined. insert ( source) ;
@@ -77,10 +87,37 @@ pub(crate) fn plan(
7787 ) ;
7888 for & output in & method. outputs {
7989 determined. insert ( output) ;
90+ pre_claimed. remove ( & output) ;
8091 queue. push_back ( output) ;
8192 }
8293 selected_set. insert ( rel_id) ;
8394 selected. push ( ( rel_id, method_idx) ) ;
95+ } else {
96+ // No method is immediately selectable. If exactly one method remains
97+ // feasible — meaning no other method can run without overwriting a cell
98+ // that is already determined or pre-claimed — and the current cell is
99+ // one of its inputs, the flow continues along that method: pre-claim its
100+ // outputs and enqueue them so the flood-fill propagates further.
101+ //
102+ // Both `determined` and `pre_claimed` are excluded from feasibility so
103+ // that a method whose output is pre-claimed by this same flood-fill pass
104+ // is not counted as a second viable option.
105+ let mut feasible = rel. methods . iter ( ) . filter ( |m| {
106+ m. outputs
107+ . iter ( )
108+ . all ( |o| !determined. contains ( o) && !pre_claimed. contains ( o) )
109+ } ) ;
110+ let first = feasible. next ( ) ;
111+ let second = feasible. next ( ) ;
112+ if let ( Some ( sole) , None ) = ( first, second)
113+ && sole. inputs . contains ( & cell)
114+ {
115+ for & output in & sole. outputs {
116+ if pre_claimed. insert ( output) {
117+ queue. push_back ( output) ;
118+ }
119+ }
120+ }
84121 }
85122 }
86123 }
0 commit comments