001    /*--------------------------------------------------------------------------+
002    $Id: Range.java 26268 2010-02-18 10:44:30Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.math;
019    
020    import java.text.NumberFormat;
021    
022    import edu.tum.cs.commons.assertion.CCSMPre;
023    import edu.tum.cs.commons.string.StringUtils;
024    
025    /**
026     * A class that represents ranges that may include or exclude the upper and
027     * lower bounds. This class is immutable.
028     * <p>
029     * Note: If a range is constructed where the upper and lower bounds are equal
030     * and one of them is exclusive, this range is considered empty, i.e. no number
031     * can be contained in it.
032     * 
033     * 
034     * @author deissenb
035     * @author $Author: juergens $
036     * @version $Rev: 26268 $
037     * @levd.rating GREEN Hash: 71F5CBF843D6B94E6071CAF614C19E6B
038     */
039    public class Range {
040            /** The lower bound. */
041            private final double lower;
042    
043            /** The upper bound. */
044            private final double upper;
045    
046            /** Flag that indicates if lower bound is inclusive or not. */
047            private final boolean lowerIsInclusive;
048    
049            /** Flag that indicates if upper bound is inclusive or not. */
050            private final boolean upperIsInclusive;
051    
052            /** Create range where both bounds are inclusive. */
053            public Range(double lower, double upper) {
054                    this(lower, true, upper, true);
055            }
056    
057            /**
058             * Create range.
059             * 
060             * @param lower
061             *            lower bound
062             * @param lowerIsInclusive
063             *            flag that indicates if lower bound is inclusive or not
064             * @param upper
065             *            upper bound
066             * @param upperIsInclusive
067             *            flag that indicates if upper bound is inclusive or not
068             */
069            public Range(double lower, boolean lowerIsInclusive, double upper,
070                            boolean upperIsInclusive) {
071                    CCSMPre.isFalse(Double.isNaN(lower), "Lower bound must not be NaN");
072                    CCSMPre.isFalse(Double.isNaN(upper), "Upper bound must not be NaN");
073                    this.lower = lower;
074                    this.lowerIsInclusive = lowerIsInclusive;
075                    this.upper = upper;
076                    this.upperIsInclusive = upperIsInclusive;
077            }
078    
079            /** Checks is a number is contained in the range. */
080            public boolean contains(double number) {
081                    if (lowerIsInclusive) {
082                            if (number < lower) {
083                                    return false;
084                            }
085                    } else {
086                            if (number <= lower) {
087                                    return false;
088                            }
089                    }
090    
091                    if (upperIsInclusive) {
092                            if (number > upper) {
093                                    return false;
094                            }
095                    } else {
096                            if (number >= upper) {
097                                    return false;
098                            }
099                    }
100    
101                    return true;
102            }
103    
104            /**
105             * Hash code includes bound and the flags that indicate if the bounds are
106             * inclusive or not.
107             */
108            @Override
109            public int hashCode() {
110                    if (isEmpty()) {
111                            return 0;
112                    }
113    
114                    int result = hash(upper) + 37 * hash(lower);
115    
116                    // indicate bounds by bit flips; which bit is used is not really
117                    // relevant
118                    if (lowerIsInclusive) {
119                            result ^= 0x100000;
120                    }
121                    if (upperIsInclusive) {
122                            result ^= 0x4000;
123                    }
124    
125                    return result;
126            }
127    
128            /** Code for hashing a double is copied from {@link Double#hashCode()}. */
129            private int hash(double number) {
130                    long bits = Double.doubleToLongBits(number);
131                    return (int) (bits ^ (bits >>> 32));
132            }
133    
134            /**
135             * Two ranges are equal if their bounds are equal and the flags that
136             * indicate if the bounds are inclusive or not are equal, too. Empty ranges
137             * are considered equal regardless for there specific bounds.
138             */
139            @Override
140            public boolean equals(Object obj) {
141                    if (obj == this) {
142                            return true;
143                    }
144                    if (!(obj instanceof Range)) {
145                            return false;
146                    }
147    
148                    Range other = (Range) obj;
149    
150                    if (isEmpty() && other.isEmpty()) {
151                            return true;
152                    }
153    
154                    return lowerIsInclusive == other.lowerIsInclusive
155                                    && upperIsInclusive == other.upperIsInclusive
156                                    && lower == other.lower && upper == other.upper;
157            }
158    
159            /** Get lower bound. */
160            public double getLower() {
161                    return lower;
162            }
163    
164            /** Get upper bound. */
165            public double getUpper() {
166                    return upper;
167            }
168    
169            /** Get flag that indicates if the lower bound is inclusive. */
170            public boolean isLowerInclusive() {
171                    return lowerIsInclusive;
172            }
173    
174            /** Get flag that indicates if the upper bound is inclusive. */
175            public boolean isUpperInclusive() {
176                    return upperIsInclusive;
177            }
178    
179            /** Checks if a range is empty. */
180            public boolean isEmpty() {
181                    if (lowerIsInclusive && upperIsInclusive) {
182                            return lower > upper;
183                    }
184                    return lower >= upper;
185            }
186    
187            /**
188             * Returns the size of the range. In case of empty ranges this returns 0.
189             * Note that inclusiveness of bound is not relevant for this method.
190             */
191            public double size() {
192                    return Math.max(0, upper - lower);
193            }
194    
195            /** This forwards to <code>format(null);</code>. */
196            @Override
197            public String toString() {
198                    return format(null);
199            }
200    
201            /**
202             * String representation contains the bounds and brackets that indicate if
203             * the bounds are inclusive or exclusive.
204             * 
205             * @param numberFormat
206             *            number format used for formatting the numbers. If this is
207             *            <code>null</code>, no special formatting is applied.
208             */
209            public String format(NumberFormat numberFormat) {
210                    StringBuilder result = new StringBuilder();
211                    if (lowerIsInclusive) {
212                            result.append("[");
213                    } else {
214                            result.append("]");
215                    }
216                    result.append(StringUtils.format(lower, numberFormat) + ";"
217                                    + StringUtils.format(upper, numberFormat));
218                    if (upperIsInclusive) {
219                            result.append("]");
220                    } else {
221                            result.append("[");
222                    }
223                    return result.toString();
224            }
225    }