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,
8 * the 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://tudelft.nl/staff/p.knoppers-1">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 double; 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 T; 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 T; 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 double; 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 T; 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 T; 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 double; 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 T; 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 T; 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 double; 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 T; 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 T; 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; bound
163 * @param failMessage String; 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 /** {@inheritDoc} */
172 @Override
173 public boolean accept(final T value)
174 {
175 return this.bound.accept(value);
176 }
177
178 /** {@inheritDoc} */
179 @Override
180 public String failMessage()
181 {
182 return this.failMessage;
183 }
184
185 /**
186 * @return bound.
187 */
188 public Bound getBound()
189 {
190 return this.bound;
191 }
192
193 /**
194 * Super class for classes that implement a specific numeric check.
195 * <p>
196 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
197 * <br>
198 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
199 * </p>
200 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
201 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
202 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
203 */
204 abstract static class Bound
205 {
206
207 /** Value of the bound. */
208 @SuppressWarnings("checkstyle:visibilitymodifier")
209 final Number bound;
210
211 /** Hashcode of the value class. */
212 @SuppressWarnings("checkstyle:visibilitymodifier")
213 final int classHashcode;
214
215 /** String representation of this bound with %s for the value. */
216 private final String stringFormat;
217
218 /**
219 * Constructor.
220 * @param bound Number; value of the bound
221 * @param stringFormat String; string representation of this bound with %s for the value
222 */
223 Bound(final Number bound, final String stringFormat)
224 {
225 Throw.whenNull(bound, "Bound may not be null.");
226 Throw.whenNull(bound, "String format may not be null.");
227 Throw.when(Double.isNaN(bound.doubleValue()), IllegalArgumentException.class, "Bound value may not be NaN.");
228 this.bound = bound;
229 this.classHashcode = bound.getClass().hashCode();
230 this.stringFormat = stringFormat;
231 }
232
233 /**
234 * Returns true if the bound accepts the value.
235 * @param value Number; the value to check
236 * @return true if the bound accepts the value
237 */
238 abstract boolean accept(Number value);
239
240 /** {@inheritDoc} */
241 @Override
242 public final String toString()
243 {
244 return String.format(this.stringFormat, this.bound);
245 }
246 }
247
248 /**
249 * Class implementing a lower inclusive bound; {@code value >= bound}.
250 * <p>
251 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
252 * <br>
253 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
254 * </p>
255 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
256 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
257 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
258 * @param <T> value type
259 */
260 static class LowerBoundInclusive<T extends Number> extends Bound
261 {
262
263 /**
264 * Constructor.
265 * @param bound T; bound
266 */
267 LowerBoundInclusive(final T bound)
268 {
269 super(bound, "%s <= value");
270 }
271
272 /** {@inheritDoc} */
273 @Override
274 protected boolean accept(final Number value)
275 {
276 return this.bound.doubleValue() <= value.doubleValue();
277 }
278
279 }
280
281 /**
282 * Class implementing a lower exclusive bound; {@code value > bound}.
283 * <p>
284 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
285 * <br>
286 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
287 * </p>
288 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
289 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
290 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
291 * @param <T> value type
292 */
293 static class LowerBoundExclusive<T extends Number> extends Bound
294 {
295
296 /**
297 * Constructor.
298 * @param bound T; bound
299 */
300 LowerBoundExclusive(final T bound)
301 {
302 super(bound, "%s < value");
303 }
304
305 /** {@inheritDoc} */
306 @Override
307 protected boolean accept(final Number value)
308 {
309 return this.bound.doubleValue() < value.doubleValue();
310 }
311
312 }
313
314 /**
315 * Class implementing an upper inclusive bound; {@code value <= bound}.
316 * <p>
317 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
318 * <br>
319 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
320 * </p>
321 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
322 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
323 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
324 * @param <T> value type
325 */
326 static class UpperBoundInclusive<T extends Number> extends Bound
327 {
328
329 /**
330 * Constructor.
331 * @param bound T; bound
332 */
333 UpperBoundInclusive(final T bound)
334 {
335 super(bound, "value <= %s");
336 }
337
338 /** {@inheritDoc} */
339 @Override
340 protected boolean accept(final Number value)
341 {
342 return this.bound.doubleValue() >= value.doubleValue();
343 }
344
345 }
346
347 /**
348 * Class implementing an upper exclusive bound; {@code value < bound}.
349 * <p>
350 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
351 * <br>
352 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
353 * </p>
354 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
355 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
356 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
357 * @param <T> value type
358 */
359 static class UpperBoundExclusive<T extends Number> extends Bound
360 {
361
362 /**
363 * Constructor.
364 * @param bound T; bound
365 */
366 UpperBoundExclusive(final T bound)
367 {
368 super(bound, "value < %s");
369 }
370
371 /** {@inheritDoc} */
372 @Override
373 protected boolean accept(final Number value)
374 {
375 return this.bound.doubleValue() > value.doubleValue();
376 }
377
378 }
379
380 /** {@inheritDoc} */
381 @Override
382 public String toString()
383 {
384 return "SingleBound [" + this.bound + "]";
385 }
386
387 }