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 }