1 package org.opentrafficsim.base.parameters.constraint;
2
3 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
4 import org.djutils.exceptions.Throw;
5
6 /**
7 * Continuous constraints with a single bound. To allow both {@code Double} and {@code DoubleScalar<?, ?>} constraints, the
8 * generic type is restricted to {@code Number}. However, that also allows other subclasses of {@code Number}, e.g.
9 * {@code Integer}. Due to rounding and value limits from the type (e.g. {@code Integer.MAX_VALEU}), bounds may not function
10 * correctly after a call to {@code Number.doubleValue()}. To restrict the usage, the constructor is private and static factory
11 * methods for {@code Double} and {@code DoubleScalar<?, ?>} are supplied.
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 <T> value type
20 */
21 public class SingleBound<T extends Number> implements Constraint<T>
22 {
23
24 /** The bound. */
25 private final Bound bound;
26
27 /** Message about values that do not comply with the bound. */
28 private final String failMessage;
29
30 /**
31 * Creates a lower inclusive bound; {@code value >= bound}.
32 * @param bound bound value
33 * @return lower inclusive bound
34 */
35 public static final SingleBound<Double> lowerInclusive(final double bound)
36 {
37 return createLowerInclusive(bound);
38 }
39
40 /**
41 * Creates a lower inclusive bound; {@code value >= bound}.
42 * @param bound bound value
43 * @param <T> value type
44 * @return lower inclusive bound
45 */
46 public static final <T extends DoubleScalar<?, ?>> SingleBound<T> lowerInclusive(final T bound)
47 {
48 return createLowerInclusive(bound);
49 }
50
51 /**
52 * Creates a lower inclusive bound; {@code value >= bound}.
53 * @param bound bound value
54 * @param <T> value type
55 * @return lower inclusive bound
56 */
57 private static <T extends Number> SingleBound<T> createLowerInclusive(final T bound)
58 {
59 return new SingleBound<>(new LowerBoundInclusive<>(bound),
60 String.format("Value is not greater than or equal to %s", bound));
61 }
62
63 /**
64 * Creates a lower exclusive bound; {@code value > bound}.
65 * @param bound bound value
66 * @return lower exclusive bound
67 */
68 public static final SingleBound<Double> lowerExclusive(final double bound)
69 {
70 return createLowerExclusive(bound);
71 }
72
73 /**
74 * Creates a lower exclusive bound; {@code value > bound}.
75 * @param bound bound value
76 * @param <T> value type
77 * @return lower exclusive bound
78 */
79 public static final <T extends DoubleScalar<?, ?>> SingleBound<T> lowerExclusive(final T bound)
80 {
81 return createLowerExclusive(bound);
82 }
83
84 /**
85 * Creates a lower exclusive bound; {@code value > bound}.
86 * @param bound bound value
87 * @param <T> value type
88 * @return lower exclusive bound
89 */
90 private static <T extends Number> SingleBound<T> createLowerExclusive(final T bound)
91 {
92 return new SingleBound<>(new LowerBoundExclusive<>(bound), String.format("Value is not greater than %s", bound));
93 }
94
95 /**
96 * Creates an upper inclusive bound; {@code value <= bound}.
97 * @param bound bound value
98 * @return upper inclusive bound
99 */
100 public static final SingleBound<Double> upperInclusive(final double bound)
101 {
102 return createUpperInclusive(bound);
103 }
104
105 /**
106 * Creates an upper inclusive bound; {@code value <= bound}.
107 * @param bound bound value
108 * @return upper inclusive bound
109 * @param <T> value type
110 */
111 public static final <T extends DoubleScalar<?, ?>> SingleBound<T> upperInclusive(final T bound)
112 {
113 return createUpperInclusive(bound);
114 }
115
116 /**
117 * Creates an upper inclusive bound; {@code value <= bound}.
118 * @param bound bound value
119 * @param <T> value type
120 * @return upper inclusive bound
121 */
122 private static <T extends Number> SingleBound<T> createUpperInclusive(final T bound)
123 {
124 return new SingleBound<>(new UpperBoundInclusive<>(bound),
125 String.format("Value is not smaller than or equal to %s", bound));
126 }
127
128 /**
129 * Creates an upper exclusive bound; {@code value < bound}.
130 * @param bound bound value
131 * @return upper exclusive bound
132 */
133 public static final SingleBound<Double> upperExclusive(final double bound)
134 {
135 return createUpperExclusive(bound);
136 }
137
138 /**
139 * Creates an upper exclusive bound; {@code value < bound}.
140 * @param bound bound value
141 * @param <T> value type
142 * @return upper exclusive bound
143 */
144 public static final <T extends DoubleScalar<?, ?>> SingleBound<T> upperExclusive(final T bound)
145 {
146 return createUpperExclusive(bound);
147 }
148
149 /**
150 * Creates an upper exclusive bound; {@code value < bound}.
151 * @param bound bound value
152 * @param <T> value type
153 * @return upper exclusive bound
154 */
155 private static <T extends Number> SingleBound<T> createUpperExclusive(final T bound)
156 {
157 return new SingleBound<>(new UpperBoundExclusive<>(bound), String.format("Value is not smaller than %s", bound));
158 }
159
160 /**
161 * Constructor.
162 * @param bound bound
163 * @param failMessage message about values that do not comply with the bound
164 */
165 SingleBound(final Bound bound, final String failMessage)
166 {
167 this.bound = bound;
168 this.failMessage = failMessage;
169 }
170
171 @Override
172 public boolean accept(final T value)
173 {
174 return this.bound.accept(value);
175 }
176
177 @Override
178 public String failMessage()
179 {
180 return this.failMessage;
181 }
182
183 /**
184 * Returns the bound.
185 * @return bound.
186 */
187 public Bound getBound()
188 {
189 return this.bound;
190 }
191
192 /**
193 * Super class for classes that implement a specific numeric check.
194 * <p>
195 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
196 * <br>
197 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
198 * </p>
199 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
200 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
201 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
202 */
203 abstract static class Bound
204 {
205
206 /** Value of the bound. */
207 @SuppressWarnings("checkstyle:visibilitymodifier")
208 final Number bound;
209
210 /** Hashcode of the value class. */
211 @SuppressWarnings("checkstyle:visibilitymodifier")
212 final int classHashcode;
213
214 /** String representation of this bound with %s for the value. */
215 private final String stringFormat;
216
217 /**
218 * Constructor.
219 * @param bound value of the bound
220 * @param stringFormat string representation of this bound with %s for the value
221 */
222 Bound(final Number bound, final String stringFormat)
223 {
224 Throw.whenNull(bound, "Bound may not be null.");
225 Throw.whenNull(bound, "String format may not be null.");
226 Throw.when(Double.isNaN(bound.doubleValue()), IllegalArgumentException.class, "Bound value may not be NaN.");
227 this.bound = bound;
228 this.classHashcode = bound.getClass().hashCode();
229 this.stringFormat = stringFormat;
230 }
231
232 /**
233 * Returns true if the bound accepts the value.
234 * @param value the value to check
235 * @return true if the bound accepts the value
236 */
237 abstract boolean accept(Number value);
238
239 @Override
240 public final String toString()
241 {
242 return String.format(this.stringFormat, this.bound);
243 }
244 }
245
246 /**
247 * Class implementing a lower inclusive bound; {@code value >= bound}.
248 * <p>
249 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
250 * <br>
251 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
252 * </p>
253 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
254 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
255 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
256 * @param <T> value type
257 */
258 static class LowerBoundInclusive<T extends Number> extends Bound
259 {
260
261 /**
262 * Constructor.
263 * @param bound bound
264 */
265 LowerBoundInclusive(final T bound)
266 {
267 super(bound, "%s <= value");
268 }
269
270 @Override
271 protected boolean accept(final Number value)
272 {
273 return this.bound.doubleValue() <= value.doubleValue();
274 }
275
276 }
277
278 /**
279 * Class implementing a lower exclusive bound; {@code value > bound}.
280 * <p>
281 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
282 * <br>
283 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
284 * </p>
285 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
286 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
287 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
288 * @param <T> value type
289 */
290 static class LowerBoundExclusive<T extends Number> extends Bound
291 {
292
293 /**
294 * Constructor.
295 * @param bound bound
296 */
297 LowerBoundExclusive(final T bound)
298 {
299 super(bound, "%s < value");
300 }
301
302 @Override
303 protected boolean accept(final Number value)
304 {
305 return this.bound.doubleValue() < value.doubleValue();
306 }
307
308 }
309
310 /**
311 * Class implementing an upper inclusive bound; {@code value <= bound}.
312 * <p>
313 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
314 * <br>
315 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
316 * </p>
317 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
318 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
319 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
320 * @param <T> value type
321 */
322 static class UpperBoundInclusive<T extends Number> extends Bound
323 {
324
325 /**
326 * Constructor.
327 * @param bound bound
328 */
329 UpperBoundInclusive(final T bound)
330 {
331 super(bound, "value <= %s");
332 }
333
334 @Override
335 protected boolean accept(final Number value)
336 {
337 return this.bound.doubleValue() >= value.doubleValue();
338 }
339
340 }
341
342 /**
343 * Class implementing an upper exclusive bound; {@code value < bound}.
344 * <p>
345 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
346 * <br>
347 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
348 * </p>
349 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
350 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
351 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
352 * @param <T> value type
353 */
354 static class UpperBoundExclusive<T extends Number> extends Bound
355 {
356
357 /**
358 * Constructor.
359 * @param bound bound
360 */
361 UpperBoundExclusive(final T bound)
362 {
363 super(bound, "value < %s");
364 }
365
366 @Override
367 protected boolean accept(final Number value)
368 {
369 return this.bound.doubleValue() > value.doubleValue();
370 }
371
372 }
373
374 @Override
375 public String toString()
376 {
377 return "SingleBound [" + this.bound + "]";
378 }
379
380 }