View Javadoc
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 }