001/* 002 * Copyright (c) 2005 Peter Palotas, Fredrik Johansson, Einar Pehrson, 003 * Sebastian Kekkonen, Lars Magnus Lang, Malin Johansson and Sofia Nilsson 004 * 005 * This file is part of 006 * CleanSheets Extension for Assertions 007 * 008 * CleanSheets Extension for Assertions is free software; you can 009 * redistribute it and/or modify it under the terms of the GNU General Public 010 * License as published by the Free Software Foundation; either version 2 of 011 * the License, or (at your option) any later version. 012 * 013 * CleanSheets Extension for Assertions is distributed in the hope that 014 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 015 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with CleanSheets Extension for Assertions; if not, write to the 020 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 021 * Boston, MA 02111-1307 USA 022 */ 023package csheets.ext.assertion; 024 025import java.io.Serializable; 026import java.util.Iterator; 027import java.util.List; 028import java.util.TreeSet; 029 030/** A class representing (possibly) multiple intervals. This can be used to specify 031 any combination of values to be included in an interval. The MultiInterval is 032 built up by specifying normal intervals to be included or excluded from the 033 MultiInterval. 034 @author Peter Palotas 035 @author Fredrik Johansson 036*/ 037public class MultiInterval implements Iterable<Interval>, Serializable { 038 039 /** The unique version identifier used for serialization */ 040 private static final long serialVersionUID = 3311313307922046224L; 041 042 /** 043 Contains a list of the intervals included in this multi interval. 044 <p><b>Invariant:</b> intervalSet does not contain any intersecting or 045 bordering intervals. (Two bordering intervals are eg. [0 to 1[ [1 to 2] 046 which could easily be written as one interval [0 to 2]. 047 */ 048 private TreeSet<Interval> intervalSet; 049 050 /** Default constructor. Constructs an empty MultiInterval containing no values. Note that if the 051 first operation called on this interval after its construction is <code>exclude()</code>, 052 <b>all</b> values <b>except</b> the ones excluded will be included in this interval. 053 */ 054 public MultiInterval() { 055 intervalSet = new TreeSet<Interval>(); 056 } 057 058 /** Add an interval to be included in this MultiInterval. 059 @param interval An interval specifying values to be included in this MultiInterval. 060 */ 061 public void include(Interval interval) { 062 if (intervalSet.isEmpty()) { 063 intervalSet.add(interval); 064 return; 065 } 066 067 // Check if the new interval intersects with any one already included 068 for (Iterator<Interval> iter = intervalSet.iterator(); iter.hasNext(); ) { 069 Interval i = iter.next(); 070 071 // Try to create a union, if successful we should 072 Interval union = Interval.union(i, interval); 073 if (union != null) { 074 // remove the old interval intersecting with the new one and 075 // include the union between them instead. 076 iter.remove(); 077 include(union); 078 return; 079 } 080 } 081 082 // The new interval did not intersect with any already included, just add it. 083 intervalSet.add(interval); 084 } 085 086 /** Exclude a specific interval from this MultiInterval. If this MultiInterval is 087 empty (i.e. does not include anything) when this function is called, the MultiInterval 088 will accept <i>any value except the ones included in the interval specified</i>. 089 @param interval an interval specifying which values to exclude from this MultiInterval. 090 */ 091 public void exclude(Interval interval) { 092 // If list is empty, we assume we want to include all possible values 093 // except the ones specified. So we add an interval covering all 094 // values to the list. 095 if (intervalSet.isEmpty()) { 096 intervalSet.add(new Interval(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false, false)); 097 } 098 099 TreeSet<Interval> nlist = new TreeSet<Interval>(); 100 101 for (Iterator<Interval> iter = intervalSet.iterator(); iter.hasNext(); ) { 102 Interval i = iter.next(); 103 104 105 if (i.intersects(interval)) { 106 // Check if the lower part of the interval should be included 107 if (i.getLowerLimit() < interval.getLowerLimit() || i.getLowerLimit() == interval.getLowerLimit() && 108 i.isLowerLimitClosed() && !interval.isLowerLimitClosed()) { 109 nlist.add(new Interval(i.getLowerLimit(), interval.getLowerLimit(), i.isLowerLimitClosed(), !interval.isLowerLimitClosed())); 110 } 111 112 // Check if the upper part of the interval should be included 113 if (i.getUpperLimit() > interval.getUpperLimit() || i.getUpperLimit() == interval.getUpperLimit() && 114 i.isUpperLimitClosed() && !interval.isUpperLimitClosed()) { 115 nlist.add(new Interval(interval.getUpperLimit(), i.getUpperLimit(), !interval.isUpperLimitClosed(), i.isUpperLimitClosed())); 116 } 117 118 } else { 119 // If the interval to exclude does not intersect the current interval 120 // we keep the current interval as is. 121 nlist.add(i); 122 } 123 } 124 intervalSet = nlist; 125 } 126 127 /** Check if a value is contained in this MultiInterval. <p><b>NOTE:</b> Infinity and NaN is never 128 reported to be included in a MultiInterval. 129 @param value The value to check for. 130 @return <code>true</code> if the value has been specified to be included in this 131 MultiInterval, <code>false</code> otherwise. 132 */ 133 public boolean contains(double value) { 134 135 if (Double.isNaN(value) || Double.isInfinite(value)) 136 return false; 137 138 for (Iterator<Interval> iter = intervalSet.iterator(); iter.hasNext(); ) { 139 Interval i = iter.next(); 140 if (i.contains(value)) 141 return true; 142 143 if (i.getUpperLimit() > value) 144 return false; 145 } 146 return false; 147 } 148 149 150 /** 151 * Calculates a negation av a <code>MultiInterval</code> 152 * @param term is the <code>MultiInterval</code> to be negated 153 * @return the negated <code>MultiInterval</code> 154 */ 155 public static MultiInterval negate(MultiInterval term) { 156 MultiInterval negation = new MultiInterval(); 157 for (Interval i:term.intervalSet) 158 negation.include(Interval.negate(i)); 159 return negation; 160 } 161 162 /** 163 * Calculates the sum of two <code>MultiInterval</code>s 164 * @param term1 is the first term 165 * @param term2 is the second term 166 * @return the summarized <code>MultiInterval</code>s 167 */ 168 public static MultiInterval add(MultiInterval term1, MultiInterval term2) { 169 MultiInterval sum = new MultiInterval(); 170 for (Interval i1:term1.intervalSet) 171 for (Interval i2:term2.intervalSet) 172 sum.include(Interval.add(i1,i2)); 173 return sum; 174 } 175 176 /** 177 * Calculates the difference of two <code>MultiInterval</code>s 178 * @param term1 is the first term 179 * @param term2 is the second term 180 * @return the difference of the two <code>MultiInterval</code>s 181 */ 182 public static MultiInterval sub(MultiInterval term1, MultiInterval term2) { 183 MultiInterval difference = new MultiInterval(); 184 for (Interval i1:term1.intervalSet) 185 for (Interval i2:term2.intervalSet) 186 difference.include(Interval.sub(i1,i2)); 187 return difference; 188 } 189 190 /** 191 * Calculates the product of two <code>MultiInterval</code>s 192 * @param factor1 is the first factor 193 * @param factor2 is the second factor 194 * @return the product of thw two <code>MultiInterval</code>s 195 */ 196 public static MultiInterval mul(MultiInterval factor1, MultiInterval factor2) { 197 MultiInterval product = new MultiInterval(); 198 for (Interval i1:factor1.intervalSet) 199 for (Interval i2:factor2.intervalSet) 200 product.include(Interval.mul(i1,i2)); 201 return product; 202 } 203 204 /** 205 * Calculates the quotient of two <code>MultiInterval</code>s 206 * @param numerator is the numerator 207 * @param denominator is the denominator 208 * @return the quotient of the two <code>MultiInterval</code>s 209 */ 210 public static MultiInterval div(MultiInterval numerator, MultiInterval denominator) throws MathException { 211 MultiInterval quotient = new MultiInterval(); 212 for (Interval i1:numerator.intervalSet) 213 for (Interval i2:denominator.intervalSet) 214 quotient.include(Interval.div(i1,i2)); 215 return quotient; 216 } 217 218 219 /** 220 * Calculates the first <code>MultiInterval</code> raised to the power of the second <code>MultiInterval</code> 221 * @param base is the first <code>MultiInterval</code> 222 * @param exponent is the second <code>MultiInterval</code> 223 * @return the first <code>MultiInterval</code> raised to the power of the second <code>MultiInterval</code> 224 * @throws MathException if the first <code>MultiInterval</code> contains values below zero and the second <code>MultiInterval</code> is not build of singel integer value <code>Interval</code>s 225 */ 226 public static MultiInterval pow(MultiInterval base, MultiInterval exponent) throws MathException { 227 MultiInterval product = new MultiInterval(); 228 for (Interval baseI : base.intervalSet) 229 for (Interval expI : exponent.intervalSet) 230 product.include(Interval.pow(baseI, expI)); 231 return product; 232 } 233 234 235 /** 236 * Calculates the cosine <code>MultiInterval</code> of a <code>MultiInterval</code> 237 * @param mInterval is the <code>MultiInterval</code> 238 * @return the cosine <code>MultiInterval</code> of the <code>MultiInterval</code> 239 */ 240 public static MultiInterval cos(MultiInterval mInterval) { 241 MultiInterval result = new MultiInterval(); 242 for (Interval i : mInterval.intervalSet) 243 result.include(Interval.cos(i)); 244 return result; 245 } 246 247 /** 248 * Calculates the sine <code>MultiInterval</code> of a <code>MultiInterval</code> 249 * @param mInterval is the <code>MultiInterval</code> 250 * @return the sine <code>MultiInterval</code> of the <code>MultiInterval</code> 251 */ 252 public static MultiInterval sin(MultiInterval mInterval) { 253 MultiInterval result = new MultiInterval(); 254 for (Interval i : mInterval.intervalSet) 255 result.include(Interval.sin(i)); 256 return result; 257 } 258 259 /** 260 * Calculates the tangent <code>MultiInterval</code> of a <code>MultiInterval</code> 261 * @param mInterval is the <code>MultiInterval</code> 262 * @return the tangent <code>MultiInterval</code> of the <code>MultiInterval</code> 263 */ 264 public static MultiInterval tan(MultiInterval mInterval) { 265 MultiInterval result = new MultiInterval(); 266 for (Interval i : mInterval.intervalSet) 267 result.include(Interval.tan(i)); 268 return result; 269 } 270 271 /** 272 * Calculates the natural logarithm of a <code>MultiInterval</code> 273 * @param mInterval is the <code>MultiInterval</code> 274 * @return the natural logarithm of the <code>MultiInterval</code> 275 * @throws MathException if the <code>MultiInterval</code> contains values below zero 276 */ 277 278 public static MultiInterval ln(MultiInterval mInterval) throws MathException { 279 MultiInterval log = new MultiInterval(); 280 for (Interval i:mInterval.intervalSet) 281 log.include(Interval.ln(i)); 282 return log; 283 } 284 285 /** 286 * Calculates the base 10 logarithm of a <code>MultiInterval</code> 287 * @param mInterval is the <code>MultiInterval</code> 288 * @return the base 10 logarithm of the <code>MultiInterval</code> 289 * @throws MathException if the <code>MultiInterval</code> contains values below zero 290 */ 291 public static MultiInterval log10(MultiInterval mInterval) throws MathException { 292 MultiInterval log = new MultiInterval(); 293 for (Interval i : mInterval.intervalSet) 294 log.include(Interval.log10(i)); 295 return log; 296 } 297 298 /** 299 * Calculates Eulers number e raised to a <code>MultiInterval</code> 300 * @param exponent is the <code>MultiInterval</code> 301 * @return Eulers number e raised to the <code>MultiInterval</code> 302 */ 303 public static MultiInterval exp(MultiInterval exponent) { 304 MultiInterval exp = new MultiInterval(); 305 for (Interval i : exponent.intervalSet) 306 exp.include(Interval.exp(i)); 307 return exp; 308 } 309 310 /** 311 * Calculates the square root of a <code>MultiInterval</code> 312 * @param mInterval is the <code>MultiInterval</code> 313 * @return the square root of the <code>MultiInterval</code> 314 * @throws MathException if the <code>MultiInterval</code> contains values below zero 315 */ 316 public static MultiInterval sqrt(MultiInterval mInterval) throws MathException { 317 MultiInterval root = new MultiInterval(); 318 for (Interval i : mInterval.intervalSet) 319 root.include(Interval.sqrt(i)); 320 return root; 321 } 322 323 /** 324 * Calculates the <code>MultiInterval</code> you get if you convert all values from <code>double</code> to <code>int</code> 325 * @param mInterval is the <code>MultiInterval</code> 326 * @return the <code>MultiInterval</code> you get if you convert the values from <code>double</code> to <code>int</code> 327 */ 328 public static MultiInterval toInt(MultiInterval mInterval) { 329 MultiInterval result = new MultiInterval(); 330 for (Interval i : mInterval.intervalSet) 331 result.include(Interval.toInt(i)); 332 return result; 333 } 334 335 /** 336 * Calculates the absolute value <code>MultiInterval</code> of a <code>MultiInterval</code> 337 * @param mInterval is the <code>MultiInterval</code> 338 * @return the absolute value <code>MultiInterval</code> of the <code>MultiInterval</code> 339 */ 340 public static MultiInterval abs(MultiInterval mInterval) { 341 MultiInterval result = new MultiInterval(); 342 for (Interval i : mInterval.intervalSet) 343 result.include(Interval.abs(i)); 344 return result; 345 } 346 347 /** 348 * Returns a <code>MultiInterval</code> holding all possible values you get from the <code>Math.random()</code> method 349 * @return a <code>MultiInterval</code> holding all possible values you get from the <code>Math.random()</code> mathod 350 */ 351 public static MultiInterval rand() { 352 MultiInterval result = new MultiInterval(); 353 result.include(Interval.rand()); 354 return result; 355 } 356 357 /** 358 * Calculates the factorial of a <code>MultiInterval</code> 359 * @param mInterval is the <code>MultiInterval</code> 360 * @return the factorial of the <code>MultiInterval</code> 361 * @throws MathException if the <code>MultiInterval</code> contains values below zero 362 */ 363 public static MultiInterval fact(MultiInterval mInterval) throws MathException { 364 MultiInterval result = new MultiInterval(); 365 for (Interval i : mInterval.intervalSet) 366 result.include(Interval.fact(i)); 367 return result; 368 } 369 370 /** 371 * Calculates the sum of a <code>List</code> of <code>MultiInterval</code>s 372 * @param terms is the terms to be summarized 373 * @return the sum of the <code>MultiInterval</code>s 374 */ 375 public static MultiInterval sum(List<MultiInterval> terms) { 376 MultiInterval sum = new MultiInterval(); 377 Iterator<MultiInterval> iter = terms.iterator(); 378 if (iter.hasNext()) 379 sum = iter.next().clone(); 380 while (iter.hasNext()) { 381 sum = MultiInterval.add(sum,iter.next()); 382 } 383 return sum; 384 } 385 386 /** 387 * Calculates the average of a <code>List</code> of <code>MultiInterval</code>s 388 * @param terms is the terms 389 * @return the average of the <code>MultiInterval</code>s 390 */ 391 public static MultiInterval avg(List<MultiInterval> terms) { 392 MultiInterval size = new MultiInterval(); 393 size.include(new Interval(terms.size())); 394 MultiInterval sum = MultiInterval.sum(terms); 395 MultiInterval result = new MultiInterval(); // for the compiler to be happy 396 try { 397 result = MultiInterval.div(sum,size); 398 } catch (MathException e) { // should never happen 399 e.printStackTrace(); 400 } 401 return result; 402 } 403 404 405 /** Returns an iterator over the intervals in this MultiInterval in proper sequence. 406 Note that this iterator cannot be used to modify the MultiInterval. 407 @return an iterator over the intervals in this MultiInterval in proper sequence. 408 */ 409 public Iterator<Interval> iterator() { 410 return new ConstMultiIntervalIterator(intervalSet.iterator()); 411 } 412 413 /** Compares the specified MultiInterval with this MultiInterval for equality. 414 @return <code>true</code> if the two intervals represent the same ranges of values, 415 <code>false</code> otherwise. 416 */ 417 public boolean equals(Object o) { 418 if (o == null) 419 return false; 420 421 if (!(o instanceof MultiInterval)) 422 return false; 423 424 MultiInterval mi = (MultiInterval)o; 425 426 return intervalSet.equals(mi.intervalSet); 427 } 428 429 /** 430 * Makes a copy of this <code>MultiInterval</code> intance 431 */ 432 public MultiInterval clone() { 433 MultiInterval newMI = new MultiInterval(); 434 for (Interval i:intervalSet) 435 newMI.include(i); 436 return newMI; 437 } 438 439 440 /** Returns a string representation of this MultiInterval. */ 441 public String toString() { 442 String s = ""; 443 444 for (Iterator<Interval> iter = intervalSet.iterator(); iter.hasNext(); ) { 445 Interval i = iter.next(); 446 s += i.toString(); 447 if (iter.hasNext()) 448 s += " or "; 449 } 450 return s; 451 } 452}