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