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 }