001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.lang.math;
018
019 import java.io.Serializable;
020
021 /**
022 * <p><code>DoubleRange</code> represents an inclusive range of <code>double</code>s.</p>
023 *
024 * @author Apache Software Foundation
025 * @since 2.0
026 * @version $Id: DoubleRange.java 905636 2010-02-02 14:03:32Z niallp $
027 */
028 public final class DoubleRange extends Range implements Serializable {
029
030 /**
031 * Required for serialization support.
032 *
033 * @see java.io.Serializable
034 */
035 private static final long serialVersionUID = 71849363892740L;
036
037 /**
038 * The minimum number in this range (inclusive).
039 */
040 private final double min;
041 /**
042 * The maximum number in this range (inclusive).
043 */
044 private final double max;
045
046 /**
047 * Cached output minObject (class is immutable).
048 */
049 private transient Double minObject = null;
050 /**
051 * Cached output maxObject (class is immutable).
052 */
053 private transient Double maxObject = null;
054 /**
055 * Cached output hashCode (class is immutable).
056 */
057 private transient int hashCode = 0;
058 /**
059 * Cached output toString (class is immutable).
060 */
061 private transient String toString = null;
062
063 /**
064 * <p>Constructs a new <code>DoubleRange</code> using the specified
065 * number as both the minimum and maximum in this range.</p>
066 *
067 * @param number the number to use for this range
068 * @throws IllegalArgumentException if the number is <code>NaN</code>
069 */
070 public DoubleRange(double number) {
071 super();
072 if (Double.isNaN(number)) {
073 throw new IllegalArgumentException("The number must not be NaN");
074 }
075 this.min = number;
076 this.max = number;
077 }
078
079 /**
080 * <p>Constructs a new <code>DoubleRange</code> using the specified
081 * number as both the minimum and maximum in this range.</p>
082 *
083 * @param number the number to use for this range, must not
084 * be <code>null</code>
085 * @throws IllegalArgumentException if the number is <code>null</code>
086 * @throws IllegalArgumentException if the number is <code>NaN</code>
087 */
088 public DoubleRange(Number number) {
089 super();
090 if (number == null) {
091 throw new IllegalArgumentException("The number must not be null");
092 }
093 this.min = number.doubleValue();
094 this.max = number.doubleValue();
095 if (Double.isNaN(min) || Double.isNaN(max)) {
096 throw new IllegalArgumentException("The number must not be NaN");
097 }
098 if (number instanceof Double) {
099 this.minObject = (Double) number;
100 this.maxObject = (Double) number;
101 }
102 }
103
104 /**
105 * <p>Constructs a new <code>DoubleRange</code> with the specified
106 * minimum and maximum numbers (both inclusive).</p>
107 *
108 * <p>The arguments may be passed in the order (min,max) or (max,min). The
109 * getMinimum and getMaximum methods will return the correct values.</p>
110 *
111 * @param number1 first number that defines the edge of the range, inclusive
112 * @param number2 second number that defines the edge of the range, inclusive
113 * @throws IllegalArgumentException if either number is <code>NaN</code>
114 */
115 public DoubleRange(double number1, double number2) {
116 super();
117 if (Double.isNaN(number1) || Double.isNaN(number2)) {
118 throw new IllegalArgumentException("The numbers must not be NaN");
119 }
120 if (number2 < number1) {
121 this.min = number2;
122 this.max = number1;
123 } else {
124 this.min = number1;
125 this.max = number2;
126 }
127 }
128
129 /**
130 * <p>Constructs a new <code>DoubleRange</code> with the specified
131 * minimum and maximum numbers (both inclusive).</p>
132 *
133 * <p>The arguments may be passed in the order (min,max) or (max,min). The
134 * getMinimum and getMaximum methods will return the correct values.</p>
135 *
136 * @param number1 first number that defines the edge of the range, inclusive
137 * @param number2 second number that defines the edge of the range, inclusive
138 * @throws IllegalArgumentException if either number is <code>null</code>
139 * @throws IllegalArgumentException if either number is <code>NaN</code>
140 */
141 public DoubleRange(Number number1, Number number2) {
142 super();
143 if (number1 == null || number2 == null) {
144 throw new IllegalArgumentException("The numbers must not be null");
145 }
146 double number1val = number1.doubleValue();
147 double number2val = number2.doubleValue();
148 if (Double.isNaN(number1val) || Double.isNaN(number2val)) {
149 throw new IllegalArgumentException("The numbers must not be NaN");
150 }
151 if (number2val < number1val) {
152 this.min = number2val;
153 this.max = number1val;
154 if (number2 instanceof Double) {
155 this.minObject = (Double) number2;
156 }
157 if (number1 instanceof Double) {
158 this.maxObject = (Double) number1;
159 }
160 } else {
161 this.min = number1val;
162 this.max = number2val;
163 if (number1 instanceof Double) {
164 this.minObject = (Double) number1;
165 }
166 if (number2 instanceof Double) {
167 this.maxObject = (Double) number2;
168 }
169 }
170 }
171
172 // Accessors
173 //--------------------------------------------------------------------
174
175 /**
176 * <p>Returns the minimum number in this range.</p>
177 *
178 * @return the minimum number in this range
179 */
180 public Number getMinimumNumber() {
181 if (minObject == null) {
182 minObject = new Double(min);
183 }
184 return minObject;
185 }
186
187 /**
188 * <p>Gets the minimum number in this range as a <code>long</code>.</p>
189 *
190 * <p>This conversion can lose information for large values or decimals.</p>
191 *
192 * @return the minimum number in this range
193 */
194 public long getMinimumLong() {
195 return (long) min;
196 }
197
198 /**
199 * <p>Gets the minimum number in this range as a <code>int</code>.</p>
200 *
201 * <p>This conversion can lose information for large values or decimals.</p>
202 *
203 * @return the minimum number in this range
204 */
205 public int getMinimumInteger() {
206 return (int) min;
207 }
208
209 /**
210 * <p>Gets the minimum number in this range as a <code>double</code>.</p>
211 *
212 * @return the minimum number in this range
213 */
214 public double getMinimumDouble() {
215 return min;
216 }
217
218 /**
219 * <p>Gets the minimum number in this range as a <code>float</code>.</p>
220 *
221 * <p>This conversion can lose information for large values.</p>
222 *
223 * @return the minimum number in this range
224 */
225 public float getMinimumFloat() {
226 return (float) min;
227 }
228
229 /**
230 * <p>Returns the maximum number in this range.</p>
231 *
232 * @return the maximum number in this range
233 */
234 public Number getMaximumNumber() {
235 if (maxObject == null) {
236 maxObject = new Double(max);
237 }
238 return maxObject;
239 }
240
241 /**
242 * <p>Gets the maximum number in this range as a <code>long</code>.</p>
243 *
244 * <p>This conversion can lose information for large values or decimals.</p>
245 *
246 * @return the maximum number in this range
247 */
248 public long getMaximumLong() {
249 return (long) max;
250 }
251
252 /**
253 * <p>Gets the maximum number in this range as a <code>int</code>.</p>
254 *
255 * <p>This conversion can lose information for large values or decimals.</p>
256 *
257 * @return the maximum number in this range
258 */
259 public int getMaximumInteger() {
260 return (int) max;
261 }
262
263 /**
264 * <p>Gets the maximum number in this range as a <code>double</code>.</p>
265 *
266 * @return the maximum number in this range
267 */
268 public double getMaximumDouble() {
269 return max;
270 }
271
272 /**
273 * <p>Gets the maximum number in this range as a <code>float</code>.</p>
274 *
275 * <p>This conversion can lose information for large values.</p>
276 *
277 * @return the maximum number in this range
278 */
279 public float getMaximumFloat() {
280 return (float) max;
281 }
282
283 // Tests
284 //--------------------------------------------------------------------
285
286 /**
287 * <p>Tests whether the specified <code>number</code> occurs within
288 * this range using <code>double</code> comparison.</p>
289 *
290 * <p><code>null</code> is handled and returns <code>false</code>.</p>
291 *
292 * @param number the number to test, may be <code>null</code>
293 * @return <code>true</code> if the specified number occurs within this range
294 */
295 public boolean containsNumber(Number number) {
296 if (number == null) {
297 return false;
298 }
299 return containsDouble(number.doubleValue());
300 }
301
302 /**
303 * <p>Tests whether the specified <code>double</code> occurs within
304 * this range using <code>double</code> comparison.</p>
305 *
306 * <p>This implementation overrides the superclass for performance as it is
307 * the most common case.</p>
308 *
309 * @param value the double to test
310 * @return <code>true</code> if the specified number occurs within this
311 * range by <code>double</code> comparison
312 */
313 public boolean containsDouble(double value) {
314 return value >= min && value <= max;
315 }
316
317 // Range tests
318 //--------------------------------------------------------------------
319
320 /**
321 * <p>Tests whether the specified range occurs entirely within this range
322 * using <code>double</code> comparison.</p>
323 *
324 * <p><code>null</code> is handled and returns <code>false</code>.</p>
325 *
326 * @param range the range to test, may be <code>null</code>
327 * @return <code>true</code> if the specified range occurs entirely within this range
328 * @throws IllegalArgumentException if the range is not of this type
329 */
330 public boolean containsRange(Range range) {
331 if (range == null) {
332 return false;
333 }
334 return containsDouble(range.getMinimumDouble())
335 && containsDouble(range.getMaximumDouble());
336 }
337
338 /**
339 * <p>Tests whether the specified range overlaps with this range
340 * using <code>double</code> comparison.</p>
341 *
342 * <p><code>null</code> is handled and returns <code>false</code>.</p>
343 *
344 * @param range the range to test, may be <code>null</code>
345 * @return <code>true</code> if the specified range overlaps with this range
346 */
347 public boolean overlapsRange(Range range) {
348 if (range == null) {
349 return false;
350 }
351 return range.containsDouble(min)
352 || range.containsDouble(max)
353 || containsDouble(range.getMinimumDouble());
354 }
355
356 // Basics
357 //--------------------------------------------------------------------
358
359 /**
360 * <p>Compares this range to another object to test if they are equal.</p>.
361 *
362 * <p>To be equal, the class, minimum and maximum must be equal.</p>
363 *
364 * @param obj the reference object with which to compare
365 * @return <code>true</code> if this object is equal
366 */
367 public boolean equals(Object obj) {
368 if (obj == this) {
369 return true;
370 }
371 if (obj instanceof DoubleRange == false) {
372 return false;
373 }
374 DoubleRange range = (DoubleRange) obj;
375 return (Double.doubleToLongBits(min) == Double.doubleToLongBits(range.min) &&
376 Double.doubleToLongBits(max) == Double.doubleToLongBits(range.max));
377 }
378
379 /**
380 * <p>Gets a hashCode for the range.</p>
381 *
382 * @return a hash code value for this object
383 */
384 public int hashCode() {
385 if (hashCode == 0) {
386 hashCode = 17;
387 hashCode = 37 * hashCode + getClass().hashCode();
388 long lng = Double.doubleToLongBits(min);
389 hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
390 lng = Double.doubleToLongBits(max);
391 hashCode = 37 * hashCode + ((int) (lng ^ (lng >> 32)));
392 }
393 return hashCode;
394 }
395
396 /**
397 * <p>Gets the range as a <code>String</code>.</p>
398 *
399 * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p>
400 *
401 * @return the <code>String</code> representation of this range
402 */
403 public String toString() {
404 if (toString == null) {
405 StringBuffer buf = new StringBuffer(32);
406 buf.append("Range[");
407 buf.append(min);
408 buf.append(',');
409 buf.append(max);
410 buf.append(']');
411 toString = buf.toString();
412 }
413 return toString;
414 }
415
416 }