package org.opentrafficsim.base.parameters.constraint; import org.djunits.value.vdouble.scalar.base.AbstractDoubleScalar; import org.djutils.exceptions.Throw; /** * Continuous constraints with a single bound. To allow both {@code Double} and {@code AbstractDoubleScalar} constraints, * the generic type is restricted to {@code Number}. However, that also allows other subclasses of {@code Number}, e.g. * {@code Integer}. Due to rounding and value limits from the type (e.g. {@code Integer.MAX_VALEU}), bounds may not function * correctly after a call to {@code Number.doubleValue()}. To restrict the usage, the constructor is private and static factory * methods for {@code Double} and {@code AbstractDoubleScalar} are supplied. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version Aug 14, 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param value type */ public class SingleBound implements Constraint { /** The bound. */ private final Bound bound; /** Message about values that do not comply with the bound. */ private final String failMessage; /** * Creates a lower inclusive bound; {@code value >= bound}. * @param bound double; bound value * @return lower inclusive bound */ public static final SingleBound lowerInclusive(final double bound) { return createLowerInclusive(bound); } /** * Creates a lower inclusive bound; {@code value >= bound}. * @param bound T; bound value * @param value type * @return lower inclusive bound */ public static final > SingleBound lowerInclusive(final T bound) { return createLowerInclusive(bound); } /** * Creates a lower inclusive bound; {@code value >= bound}. * @param bound T; bound value * @param value type * @return lower inclusive bound */ private static SingleBound createLowerInclusive(final T bound) { return new SingleBound<>(new LowerBoundInclusive<>(bound), String.format("Value is not greater than or equal to %s", bound)); } /** * Creates a lower exclusive bound; {@code value > bound}. * @param bound double; bound value * @return lower exclusive bound */ public static final SingleBound lowerExclusive(final double bound) { return createLowerExclusive(bound); } /** * Creates a lower exclusive bound; {@code value > bound}. * @param bound T; bound value * @param value type * @return lower exclusive bound */ public static final > SingleBound lowerExclusive(final T bound) { return createLowerExclusive(bound); } /** * Creates a lower exclusive bound; {@code value > bound}. * @param bound T; bound value * @param value type * @return lower exclusive bound */ private static SingleBound createLowerExclusive(final T bound) { return new SingleBound<>(new LowerBoundExclusive<>(bound), String.format("Value is not greater than %s", bound)); } /** * Creates an upper inclusive bound; {@code value <= bound}. * @param bound double; bound value * @return upper inclusive bound */ public static final SingleBound upperInclusive(final double bound) { return createUpperInclusive(bound); } /** * Creates an upper inclusive bound; {@code value <= bound}. * @param bound T; bound value * @return upper inclusive bound * @param value type */ public static final > SingleBound upperInclusive(final T bound) { return createUpperInclusive(bound); } /** * Creates an upper inclusive bound; {@code value <= bound}. * @param bound T; bound value * @param value type * @return upper inclusive bound */ private static SingleBound createUpperInclusive(final T bound) { return new SingleBound<>(new UpperBoundInclusive<>(bound), String.format("Value is not smaller than or equal to %s", bound)); } /** * Creates an upper exclusive bound; {@code value < bound}. * @param bound double; bound value * @return upper exclusive bound */ public static final SingleBound upperExclusive(final double bound) { return createUpperExclusive(bound); } /** * Creates an upper exclusive bound; {@code value < bound}. * @param bound T; bound value * @param value type * @return upper exclusive bound */ public static final > SingleBound upperExclusive(final T bound) { return createUpperExclusive(bound); } /** * Creates an upper exclusive bound; {@code value < bound}. * @param bound T; bound value * @param value type * @return upper exclusive bound */ private static SingleBound createUpperExclusive(final T bound) { return new SingleBound<>(new UpperBoundExclusive<>(bound), String.format("Value is not smaller than %s", bound)); } /** * Constructor. * @param bound Bound; bound * @param failMessage String; message about values that do not comply with the bound */ SingleBound(final Bound bound, final String failMessage) { this.bound = bound; this.failMessage = failMessage; } /** {@inheritDoc} */ @Override public boolean accept(final T value) { return this.bound.accept(value); } /** {@inheritDoc} */ @Override public String failMessage() { return this.failMessage; } /** * @return bound. */ public Bound getBound() { return this.bound; } /** * Super class for classes that implement a specific numeric check. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. *
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 sep. 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel */ abstract static class Bound { /** Value of the bound. */ @SuppressWarnings("checkstyle:visibilitymodifier") final Number bound; /** Hashcode of the value class. */ @SuppressWarnings("checkstyle:visibilitymodifier") final int classHashcode; /** String representation of this bound with %s for the value. */ private final String stringFormat; /** * Constructor. * @param bound Number; value of the bound * @param stringFormat String; string representation of this bound with %s for the value */ Bound(final Number bound, final String stringFormat) { Throw.whenNull(bound, "Bound may not be null."); Throw.whenNull(bound, "String format may not be null."); Throw.when(Double.isNaN(bound.doubleValue()), IllegalArgumentException.class, "Bound value may not be NaN."); this.bound = bound; this.classHashcode = bound.getClass().hashCode(); this.stringFormat = stringFormat; } /** * Returns true if the bound accepts the value. * @param value Number; the value to check * @return true if the bound accepts the value */ abstract boolean accept(Number value); /** {@inheritDoc} */ @Override public final String toString() { return String.format(this.stringFormat, this.bound); } } /** * Class implementing a lower inclusive bound; {@code value >= bound}. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. *
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 sep. 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param value type */ static class LowerBoundInclusive extends Bound { /** * Constructor. * @param bound T; bound */ LowerBoundInclusive(final T bound) { super(bound, "%s <= value"); } /** {@inheritDoc} */ @Override protected boolean accept(final Number value) { return this.bound.doubleValue() <= value.doubleValue(); } } /** * Class implementing a lower exclusive bound; {@code value > bound}. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. *
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 sep. 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param value type */ static class LowerBoundExclusive extends Bound { /** * Constructor. * @param bound T; bound */ LowerBoundExclusive(final T bound) { super(bound, "%s < value"); } /** {@inheritDoc} */ @Override protected boolean accept(final Number value) { return this.bound.doubleValue() < value.doubleValue(); } } /** * Class implementing an upper inclusive bound; {@code value <= bound}. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. *
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 sep. 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param value type */ static class UpperBoundInclusive extends Bound { /** * Constructor. * @param bound T; bound */ UpperBoundInclusive(final T bound) { super(bound, "value <= %s"); } /** {@inheritDoc} */ @Override protected boolean accept(final Number value) { return this.bound.doubleValue() >= value.doubleValue(); } } /** * Class implementing an upper exclusive bound; {@code value < bound}. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. *
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 sep. 2017
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel * @param value type */ static class UpperBoundExclusive extends Bound { /** * Constructor. * @param bound T; bound */ UpperBoundExclusive(final T bound) { super(bound, "value < %s"); } /** {@inheritDoc} */ @Override protected boolean accept(final Number value) { return this.bound.doubleValue() > value.doubleValue(); } } /** {@inheritDoc} */ @Override public String toString() { return "SingleBound [" + this.bound + "]"; } }