1 package org.opentrafficsim.road.gtu.lane.perception;
2
3 import java.util.Iterator;
4 import java.util.function.Function;
5 import java.util.function.Supplier;
6
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
9
10 /**
11 * Iterable that additionally provides support for PerceptionCollectors. These gather raw data, to only 'perceive' the result.
12 * <p>
13 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15 * </p>
16 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
17 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
18 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
19 * @param <H> headway type
20 * @param <U> underlying object type
21 */
22 public interface PerceptionCollectable<H extends Headway, U> extends PerceptionIterable<H>
23 {
24
25 /**
26 * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
27 * @param collector collector
28 * @param <C> collection result type
29 * @param <I> intermediate type
30 * @return collection result
31 */
32 default <C, I> C collect(final PerceptionCollector<C, ? super U, I> collector)
33 {
34 return collect(collector.getIdentity(), collector.getAccumulator(), collector.getFinalizer());
35 }
36
37 /**
38 * Collect the underlying objects in to a perceived result. This methodology is loosely based on Stream.collect().
39 * @param identity the initial intermediate result value
40 * @param accumulator accumulator
41 * @param finalizer finalizer
42 * @param <C> collection result type
43 * @param <I> intermediate type
44 * @return collection result
45 */
46 <C, I> C collect(Supplier<I> identity, PerceptionAccumulator<? super U, I> accumulator, Function<I, C> finalizer);
47
48 /**
49 * Returns an iterator over the underlying objects.
50 * @return iterator
51 */
52 Iterator<U> underlying();
53
54 /**
55 * Returns an iterator over the underlying objects coupled with the distance.
56 * @return iterator
57 */
58 Iterator<UnderlyingDistance<U>> underlyingWithDistance();
59
60 /**
61 * Combination of an accumulator and a finalizer.
62 * @param <C> collection result type
63 * @param <U> underlying object type
64 * @param <I> intermediate result type
65 */
66 interface PerceptionCollector<C, U, I>
67 {
68 /**
69 * Returns the identity value, the initial intermediate value.
70 * @return identity value, the initial intermediate value
71 */
72 Supplier<I> getIdentity();
73
74 /**
75 * Returns the accumulator.
76 * @return accumulator
77 */
78 PerceptionAccumulator<U, I> getAccumulator();
79
80 /**
81 * Returns the finalizer.
82 * @return finalizer
83 */
84 Function<I, C> getFinalizer();
85 }
86
87 /**
88 * Accumulates an object one at a time in to an accumulating intermediate result.
89 * @param <U> underlying object type
90 * @param <I> intermediate result type
91 */
92 interface PerceptionAccumulator<U, I>
93 {
94 /**
95 * Accumulate the next object to intermediate result.
96 * @param intermediate intermediate result before accumulation of object
97 * @param object next object to include
98 * @param distance distance to the considered object
99 * @return intermediate result after accumulation of object
100 */
101 Intermediate<I> accumulate(Intermediate<I> intermediate, U object, Length distance);
102 }
103
104 /**
105 * Wrapper of intermediate result with info for the iterator algorithm.
106 * @param <I> intermediate result type
107 */
108 class Intermediate<I>
109 {
110 /** Number of underlying object being iterated. */
111 private int number = 0;
112
113 /** Intermediate object. */
114 private I object;
115
116 /** Whether to stop accumulating. */
117 private boolean stop = false;
118
119 /**
120 * Constructor.
121 * @param object identity value
122 */
123 public Intermediate(final I object)
124 {
125 this.object = object;
126 }
127
128 /**
129 * Get intermediate object.
130 * @return intermediate object
131 */
132 public I getObject()
133 {
134 return this.object;
135 }
136
137 /**
138 * Set intermediate object.
139 * @param object intermediate object
140 */
141 public void setObject(final I object)
142 {
143 this.object = object;
144 }
145
146 /**
147 * Returns the number of the underlying object currently being accumulated, starts at 0 for the first.
148 * @return number of the underlying object currently being accumulated
149 */
150 public int getNumber()
151 {
152 return this.number;
153 }
154
155 /**
156 * Method for the iterator to increase the underlying object number.
157 */
158 public void step()
159 {
160 this.number++;
161 }
162
163 /**
164 * Method for the accumulator to indicate the iterator can stop.
165 */
166 public void stop()
167 {
168 this.stop = true;
169 }
170
171 /**
172 * Method for the iterator to check if it can stop.
173 * @return whether the iterator can stop
174 */
175 public boolean isStop()
176 {
177 return this.stop;
178 }
179 }
180
181 /**
182 * Wrapper for object and its distance.
183 * @param object underlying object
184 * @param distance distance to object
185 * @param <U> underlying object type
186 */
187 record UnderlyingDistance<U>(U object, Length distance) implements Comparable<UnderlyingDistance<U>>
188 {
189 @Override
190 public int compareTo(final UnderlyingDistance<U> o)
191 {
192 int out = this.distance.compareTo(o.distance);
193 if (out != 0)
194 {
195 return out;
196 }
197 if (this.object == null)
198 {
199 if (o.object == null)
200 {
201 return 0;
202 }
203 return -1;
204 }
205 if (o.object == null)
206 {
207 return 1;
208 }
209 return Integer.compare(this.object.hashCode(), o.object.hashCode());
210 }
211 }
212
213 }