1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
|
/** An implementation of a numerical interval.
* <p>This is a rewrite of the old SPC code using <b>Generics</b>.
* @param <T> The type of the interval.
* @author fabriceb
* @version 1.1 2008/10/22
* <ul>
* <li>Added support for bound rules.</li>
* </ul>
* @version 1.0
* <ul>
* <li>Initial release.</li>
* </ul>
*/
public final class Interval<T extends Number> implements Cloneable {
/**
* Bound rules values.
* @author fabriceb
* @version 1.0
* <ul>
* <li>Initial release.</li>
* </ul>
*/
public enum Rule {
/**
* The bound is inclusive.
*/
INCLUSIVE,
/**
* The bound is exclusive.
*/
EXCLUSIVE;
}
/** Min bound rule.
* <br>Default value is <code>Rule.INCLUSIVE</code> to stay compatible with version 1.0
* @since 1.1
*/
private Rule minimumRule = Rule.INCLUSIVE;
/** Max bound rule.
* <br>Default value is <code>Rule.INCLUSIVE</code> to stay compatible with version 1.0
* @since 1.1
*/
private Rule maximumRule = Rule.INCLUSIVE;
/** Holds the minimum value.
*/
private T minimum;
/** Holds the maximum value.
*/
private T maximum;
/** Creates a new instance.
* @param minimum The minimum value.
* @param maximum The maximum value.
* @throws IllegalArgumentException If either <code>minimum</code> or <code>maximum</code> is <code>null</code>.
*/
public Interval(T minimum, T maximum) throws IllegalArgumentException {
this(minimum, maximum, Rule.INCLUSIVE, Rule.INCLUSIVE);
}
/** Creates a new instance.
* @param minimum The minimum value.
* @param maximum The maximum value.
* @param minimumRule The rule for the minimum bound; can be <code>null</code>.
* <br>If <code>null</code> will be set to <code>Rule.INCLUSIVE</code>.
* @param maximumRule The rule for the maximum bound; can be <code>null</code>.
* <br>If <code>null</code> will be set to <code>Rule.INCLUSIVE</code>.
* @throws IllegalArgumentException If either <code>minimum</code> or <code>maximum</code> is <code>null</code> or if the specified interval is invalid.
* <br>Typically, an invalid interval may be ]x, x[, [x, x[ or ]x, x].
* @since 1.1
*/
public Interval(T minimum, T maximum, Rule minimumRule, Rule maximumRule) throws IllegalArgumentException {
if (minimum == null || maximum == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
minimumRule = (minimumRule == null) ? Rule.INCLUSIVE : minimumRule;
maximumRule = (maximumRule == null) ? Rule.INCLUSIVE : maximumRule;
if ((minimum.doubleValue() == maximum.doubleValue()) && ((minimumRule == Rule.EXCLUSIVE) || (maximumRule == Rule.EXCLUSIVE))) {
throw new IllegalArgumentException("The specified interval is invalid");
}
else if (minimum.doubleValue() <= maximum.doubleValue()) {
this.minimum = minimum;
this.maximum = maximum;
}
else {
this.minimum = maximum;
this.maximum = minimum;
}
}
/////////////////////////////////
/**
* Creates a copy of this interval.
* @return A copy of this interval.
*/
@Override
@SuppressWarnings("unchecked")
public Interval<T> clone() {
Interval<T> copy = this;
try {
copy = (Interval<T>) super.clone();
}
catch (CloneNotSupportedException cnse) {
// Silently consume exception.
}
return copy;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int hash = 3;
hash = 79 * hash + (this.minimum != null ? this.minimum.hashCode() : 0);
return hash;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
return ((obj instanceof Interval) && equals((Interval) obj));
}
/**
* @param interval The interval to be tested.
* @return <code>True</code> if the test is verified; <code>false</code> otherwise.
*/
public boolean equals(Interval<?> interval) {
return ((interval != null) && (minimum.doubleValue() == interval.minimum()) && (maximum.doubleValue() == interval.maximum()) && (minimumRule == interval.minimumRule) && (maximumRule == interval.maximumRule));
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return (minimumRule == Rule.INCLUSIVE ? "[" : "]") + minimum + ", " + maximum + (maximumRule == Rule.INCLUSIVE ? "]" : "[");
}
/////////////////////////////////
/**
* Gets the minimum value.
* @return A <code>T</code> instance.
*/
public T getMinimum() {
return minimum;
}
/**
* Gets the maximum value.
* @return A <code>T</code> instance.
*/
public T getMaximum() {
return maximum;
}
/**
* Gets the minimum value in <code>double</code> precision.
* @return A <code>double</code>.
*/
public double minimum() {
return minimum.doubleValue();
}
/**
* Gets the maximum value in <code>double</code> precision.
* @return A <code>double</code>.
*/
public double maximum() {
return maximum.doubleValue();
}
/**
* Gets the <code>maximum - minimum</code> value in <code>double</code> precision.
* @return A <code>double</code>.
*/
public double range() {
return maximum.doubleValue() - minimum.doubleValue();
}
/**
* Gets the rule of the minimum bound.
* @return A <code>Rule<code> instance; never <code>null</code>.
* @since 1.1
*/
public Rule getMinimumRule() {
return minimumRule;
}
/**
* Gets the rule of the maximum bound.
* @return A <code>Rule<code> instance; never <code>null</code>.
* @since 1.1
*/
public Rule getMaximumRule() {
return maximumRule;
}
/**
* Indicates whether the provided value belongs to this interval.
* @param number The value to be tested.
* @return <code>True</code> if the test is verified; <code>false</code> otherwise.
* @throws java.lang.IllegalArgumentException If <code>number</code> is <code>null</code> or this interval is invalid.
*/
public boolean contains(Number number) throws IllegalArgumentException {
if (number == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
boolean containsMin = (minimumRule == Rule.INCLUSIVE) ? (minimum.doubleValue() <= number.doubleValue()) : (minimum.doubleValue() < number.doubleValue());
boolean containsMax = (maximumRule == Rule.INCLUSIVE) ? (number.doubleValue() <= maximum.doubleValue()) : (number.doubleValue() < maximum.doubleValue());
return containsMin && containsMax;
}
/**
* Indicates whether the provided interval belongs to this interval.
* @param interval The interval to be tested.
* @return <code>True</code> if the test is verified; <code>false</code> otherwise.
* @throws java.lang.IllegalArgumentException If <code>interval</code> is <code>null</code>.
*/
public boolean contains(Interval<?> interval) throws IllegalArgumentException {
if (interval == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
boolean containsMin = (minimumRule == Rule.INCLUSIVE) ? (minimum.doubleValue() <= interval.minimum()) : (minimum.doubleValue() < interval.minimum());
boolean containsMax = (maximumRule == Rule.INCLUSIVE) ? (interval.maximum() <= maximum.doubleValue()) : (interval.maximum() < maximum.doubleValue());
return containsMin && containsMax;
}
/**
* Indicates whether the provided interval intersects to this interval.
* @param interval The interval to be tested.
* @return <code>True</code> if the test is verified; <code>false</code> otherwise.
* @throws java.lang.IllegalArgumentException If <code>interval</code> is <code>null</code>.
* @since 1.1
*/
public boolean intersects(Interval<?> interval) throws IllegalArgumentException {
if (interval == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
boolean containsMin = (minimumRule == Rule.INCLUSIVE) ? (minimum.doubleValue() <= interval.minimum()) : (minimum.doubleValue() < interval.minimum());
boolean containsMax = (maximumRule == Rule.INCLUSIVE) ? (interval.maximum() <= maximum.doubleValue()) : (interval.maximum() < maximum.doubleValue());
return containsMin || containsMax;
}
/**
* Rescale the provided number to fit into this interval.
* @param number The number to rescale.
* @return A <code>double</code>.
* @throws java.lang.IllegalArgumentException If <code>number</code> is <code>null</code>.
*/
public double rescale(Number number) throws IllegalArgumentException {
double result = rescale(number, this);
return result;
}
/**
* Rescale the provided number to fit into the provided interval.
* @param number The number to rescale.
* @param interval The source interval.
* @return A <code>double</code>.
* @throws java.lang.IllegalArgumentException If <code>number</code> or <code>intervall</code>. is <code>null</code>.
* @since 1.1
*/
public static double rescale(Number number, Interval<? extends Number> interval) throws IllegalArgumentException {
if (number == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
else if (interval == null) {
throw new IllegalArgumentException("Source interval cannot be null.");
}
double result = rescale(number.doubleValue(), interval.minimum(), interval.maximum());
if (interval.getMinimumRule() == Rule.EXCLUSIVE && result == interval.minimum()) {
result = Math.min(result + Double.MIN_VALUE, interval.maximum());
}
else if (interval.getMaximumRule() == Rule.EXCLUSIVE && result == interval.maximum()) {
result = Math.max(result - Double.MIN_VALUE, interval.minimum());
}
return result;
}
/**
* Rescale the provided number to fit into the <code>[minimum, maximum]</code> interval.
* @param value The value to rescale.
* @param minimum The minimum value.
* @param maximum The maximum value.
* @return A <code>double</code>.
*/
public static double rescale(double value, double minimum, double maximum) {
double min = Math.min(minimum, maximum);
double max = Math.max(minimum, maximum);
double result = Math.min(value, max);
result = Math.max(result, min);
return result;
}
/**
* Project interval <code>[source]</code> on interval <code>[destination]</code> ands return the image of the given value.
* @param number The value to be projected.
* @param source The source interval (value must be contained within the interval).
* @param destination The destination interval.
* @return The image of the given value after the projection.
* @throws IllegalArgumentException If <code>value</code>, <code>source</code> or <code>destination</code> is <code>null</code> or if <code>source</code> has a range of 0.
*/
public static double project(Number number, Interval<? extends Number> source, Interval<? extends Number> destination) throws IllegalArgumentException {
if (number == null) {
throw new IllegalArgumentException("Value cannot be null.");
}
else if (source == null) {
throw new IllegalArgumentException("Source interval cannot be null.");
}
else if (source.range() == 0) {
throw new IllegalArgumentException("Range of source interval must not be 0.");
}
else if (destination == null) {
throw new IllegalArgumentException("Destination interval cannot be null.");
}
return project(number.doubleValue(), source.minimum(), source.maximum(), destination.minimum(), destination.maximum());
}
/**
* Project interval <code>[inMin, inMax]</code> on interval <code>[outMin, outMax]</code> and return the image of the given value.
* <BR>This method does not create new <code>Interval</code> objects.
* @param value The value to be projected.
* @param inMin The source minimum.
* @param inMax The source maximum.
* @param outMin The destination minimum.
* @param outMax the destination maximum.
* @return The image of the given value after the projection.
* @throws IllegalArgumentException If the source interval has a range of 0.
*/
public static double project(double value, double inMin, double inMax, double outMin, double outMax) throws IllegalArgumentException {
double min1 = Math.min(inMin, inMax);
double max1 = Math.max(inMin, inMax);
double range1 = max1 - min1;
if (range1 == 0) {
throw new IllegalArgumentException("Range of source interval must not be 0.");
}
double value1 = value;
value1 = Math.max(min1, value1);
value1 = Math.min(max1, value1);
double min2 = Math.min(outMin, outMax);
double max2 = Math.max(outMin, outMax);
double range2 = max2 - min2;
double value2 = min2 + (range2 * (value1 - min1)) / range1;
return value2;
}
} |
Partager