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 }