11package ai .timefold .solver .core .impl .bavet .common .tuple .indictment ;
22
33import java .util .ArrayList ;
4+ import java .util .Collections ;
5+ import java .util .HashSet ;
6+ import java .util .LinkedHashMap ;
7+ import java .util .LinkedHashSet ;
48import java .util .List ;
9+ import java .util .Map ;
10+ import java .util .Set ;
511import java .util .function .Consumer ;
612
713import ai .timefold .solver .core .impl .bavet .common .tuple .Tuple ;
814
915public sealed interface IndictmentSource {
1016 IndictmentSource DISABLED = new DisabledIndictmentSource ();
1117
12- void visitSources (Consumer <Object > sourceConsumer );
18+ void visitSources (Set <IndictmentSource > visited , long [] involvedNodeIds , Consumer <Object > sourceConsumer );
19+
20+ Map <Long , Set <IndictmentSource >> support ();
21+
22+ default void visitSources (long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
23+ visitSources (new HashSet <>(), involvedNodeIds , sourceConsumer );
24+ }
25+
26+ default Set <IndictmentSource > getSupportForNodeId (long nodeId ) {
27+ return support ().computeIfAbsent (nodeId , ignored -> new LinkedHashSet <>());
28+ }
29+
30+ static boolean checkIfAlreadyVisitedAndVisitSupport (IndictmentSource self , Set <IndictmentSource > visited ,
31+ long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
32+ if (!visited .add (self )) {
33+ return true ;
34+ }
35+ for (var nodeId : involvedNodeIds ) {
36+ for (var indictmentSource : self .support ().getOrDefault (nodeId , Collections .emptySet ())) {
37+ indictmentSource .visitSources (visited , involvedNodeIds , sourceConsumer );
38+ }
39+ }
40+ return false ;
41+ }
1342
1443 static IndictmentSource of (Object source ) {
15- return new RootIndictmentSource (source );
44+ return new RootIndictmentSource (source , new LinkedHashMap <>() );
1645 }
1746
1847 static IndictmentSource joining (Tuple left , Tuple right ) {
1948 if (left .getIndictmentSource () == DISABLED ) {
2049 return DISABLED ;
2150 }
22- return new JoinedIndictmentSource (left .getIndictmentSource (), right .getIndictmentSource ());
51+ return new JoinedIndictmentSource (left .getIndictmentSource (), right .getIndictmentSource (), new LinkedHashMap <>() );
2352 }
2453
2554 static IndictmentSource aggregating (Tuple elementTuple , Tuple groupTuple ) {
@@ -32,7 +61,7 @@ static IndictmentSource aggregating(Tuple elementTuple, Tuple groupTuple) {
3261 } else {
3362 var collection = new ArrayList <IndictmentSource >();
3463 collection .add (elementTuple .getIndictmentSource ());
35- return new AggregateIndictmentSource (collection );
64+ return new AggregateIndictmentSource (collection , new LinkedHashMap <>() );
3665 }
3766 }
3867
@@ -46,62 +75,103 @@ static IndictmentSource removeFromAggregate(Tuple elementTuple, Tuple groupTuple
4675 } else {
4776 var collection = new ArrayList <IndictmentSource >();
4877 collection .add (elementTuple .getIndictmentSource ());
49- return new AggregateIndictmentSource (collection );
78+ return new AggregateIndictmentSource (collection , new LinkedHashMap <>() );
5079 }
5180 }
5281
5382 static void addSupport (long nodeId , Tuple carry , Tuple support ) {
5483 if (carry .getIndictmentSource () == DISABLED ) {
5584 return ;
5685 }
57- carry .getIndictmentSupportForNodeId (nodeId ).add (support .getIndictmentSource ());
86+ carry .getIndictmentSource (). getSupportForNodeId (nodeId ).add (support .getIndictmentSource ());
5887 }
5988
6089 static void removeSupport (long nodeId , Tuple carry , Tuple support ) {
6190 if (carry .getIndictmentSource () == DISABLED ) {
6291 return ;
6392 }
64- carry .getIndictmentSupportForNodeId (nodeId ).remove (support .getIndictmentSource ());
93+ carry .getIndictmentSource (). getSupportForNodeId (nodeId ).remove (support .getIndictmentSource ());
6594 }
6695
6796 record DisabledIndictmentSource () implements IndictmentSource {
6897 @ Override
69- public void visitSources (Consumer <Object > sourceConsumer ) {
98+ public void visitSources (Set <IndictmentSource > visited , long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
99+ throw new UnsupportedOperationException ("Impossible state: indictments are disabled." );
100+ }
101+
102+ @ Override
103+ public Map <Long , Set <IndictmentSource >> support () {
70104 throw new UnsupportedOperationException ("Impossible state: indictments are disabled." );
71105 }
72106 }
73107
74- record RootIndictmentSource (Object source ) implements IndictmentSource {
108+ record RootIndictmentSource (Object source , Map < Long , Set < IndictmentSource >> support ) implements IndictmentSource {
75109 @ Override
76- public void visitSources (Consumer <Object > sourceConsumer ) {
110+ public void visitSources (Set <IndictmentSource > visited , long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
111+ if (checkIfAlreadyVisitedAndVisitSupport (this , visited , involvedNodeIds , sourceConsumer )) {
112+ return ;
113+ }
77114 sourceConsumer .accept (source );
78115 }
79- }
80116
81- record JoinedIndictmentSource (IndictmentSource left , IndictmentSource right ) implements IndictmentSource {
82117 @ Override
83- public void visitSources (Consumer <Object > sourceConsumer ) {
84- left .visitSources (sourceConsumer );
85- right .visitSources (sourceConsumer );
118+ public boolean equals (Object o ) {
119+ return this == o ;
120+ }
121+
122+ @ Override
123+ public int hashCode () {
124+ return System .identityHashCode (this );
86125 }
87126 }
88127
89- record AggregateIndictmentSource (List <IndictmentSource > sourceList ) implements IndictmentSource {
128+ record JoinedIndictmentSource (IndictmentSource left , IndictmentSource right ,
129+ Map <Long , Set <IndictmentSource >> support ) implements IndictmentSource {
90130 @ Override
91- public void visitSources (Consumer <Object > sourceConsumer ) {
92- for ( var source : sourceList ) {
93- source . visitSources ( sourceConsumer ) ;
131+ public void visitSources (Set < IndictmentSource > visited , long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
132+ if ( checkIfAlreadyVisitedAndVisitSupport ( this , visited , involvedNodeIds , sourceConsumer ) ) {
133+ return ;
94134 }
135+ left .visitSources (visited , involvedNodeIds , sourceConsumer );
136+ right .visitSources (visited , involvedNodeIds , sourceConsumer );
137+ }
138+
139+ @ Override
140+ public boolean equals (Object o ) {
141+ return this == o ;
142+ }
143+
144+ @ Override
145+ public int hashCode () {
146+ return System .identityHashCode (this );
95147 }
96148 }
97149
98- record IndictmentSourceWithSupport (IndictmentSource source , List <IndictmentSource > support ) implements IndictmentSource {
150+ record AggregateIndictmentSource (List <IndictmentSource > sourceList ,
151+ Map <Long , Set <IndictmentSource >> support ) implements IndictmentSource {
99152 @ Override
100- public void visitSources (Consumer <Object > sourceConsumer ) {
101- source .visitSources (sourceConsumer );
102- for (var support : support ) {
103- support .visitSources (sourceConsumer );
153+ public void visitSources (Set <IndictmentSource > visited , long [] involvedNodeIds , Consumer <Object > sourceConsumer ) {
154+ if (checkIfAlreadyVisitedAndVisitSupport (this , visited , involvedNodeIds , sourceConsumer )) {
155+ return ;
156+ }
157+ for (var source : sourceList ) {
158+ source .visitSources (involvedNodeIds , sourceConsumer );
104159 }
105160 }
161+
162+ @ Override
163+ public Set <IndictmentSource > getSupportForNodeId (long nodeId ) {
164+ return support .computeIfAbsent (nodeId , ignored -> new LinkedHashSet <>());
165+ }
166+
167+ @ Override
168+ public boolean equals (Object o ) {
169+ return this == o ;
170+ }
171+
172+ @ Override
173+ public int hashCode () {
174+ return System .identityHashCode (this );
175+ }
106176 }
107177}
0 commit comments