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.List; 027import java.util.TreeSet; 028 029/** 030 * This class represents a non-empty interval. 031 * 032 * A single number can be represented by setting <code>lowerLimit == upperLimit</code> 033 * 034 * A one sided interval is represented by either setting <code>upperLimit</code> 035 * to <code>Double.POSITIVE_INFINITY</code> or <code>lowerLimit</code> to <code>Double.NEGATIVE_INFINITY</code>. 036 * 037 * @author Fredrik Johansson 038 * @author Peter Palotas 039 */ 040public class Interval implements Comparable<Interval>, Cloneable, Serializable { 041 042 /** The unique version identifier used for serialization */ 043 private static final long serialVersionUID = -1176661760834511074L; 044 045 /** The lower limit of the interval */ 046 private double lowerLimit; 047 048 /** The upper limit of the interval */ 049 private double upperLimit; 050 051 /** Denotes wether the interval is closed at its lower limit or not */ 052 private boolean lowerLimitClosed; 053 054 /** Denotes wether the interval is closed at its upper limit or not */ 055 private boolean upperLimitClosed; 056 057 /** Constructs a new instance of Interval. 058 @param lowerLimit The value of the lower limit of this interval. This must be less than or equal to <code>upperLimit</code>. 059 @param upperLimit The value of the upper limit of this interval. This must be greater than or equal to <code>lowerLimit</code>. 060 @param lowerLimitClosed if <code>true</code> the value specified by <code>lowerLimit</code> will 061 be included in the interval, otherwise it will not be. Note that this <i>must</i> be <code>false</code> if 062 <code>lowerLimit</code> is infinite. 063 @param upperLimitClosed if <code>true</code> the value specified by <code>upperLimit</code> will 064 be included in the interval, otherwise it will not be. Note that this <i>must</i> be <code>false</code> if 065 <code>upperLimit</code> is infinite. 066 @throws IllegalArgumentException if illegal limits were passed. Limits must not be <code>Double.NaN</code>and 067 <code>lowerLimit</code> must be less than or equal to <code>upperLimit</code>. 068 */ 069 public Interval(double lowerLimit, double upperLimit, 070 boolean lowerLimitClosed, boolean upperLimitClosed) 071 throws IllegalArgumentException { 072 073 if (lowerLimit > upperLimit) 074 throw new IllegalArgumentException("Lower limit of interval must not be larger than upper limit"); 075 076 if (Double.isNaN(lowerLimit) || 077 Double.isNaN(upperLimit)) 078 throw new IllegalArgumentException("An interval limit must not be NaN"); 079 080 if (Double.isInfinite(lowerLimit) && lowerLimitClosed || Double.isInfinite(upperLimit) && upperLimitClosed) { 081 throw new IllegalArgumentException("A limit that is infinite cannot be closed"); 082 } 083 084 this.lowerLimit = lowerLimit; 085 this.upperLimit = upperLimit; 086 this.lowerLimitClosed = lowerLimitClosed; 087 this.upperLimitClosed = upperLimitClosed; 088 } 089 090 /** Constructs a new instance of Interval containing a single value only. 091 @param constant The single value this Interval should contain. 092 @throws IllegalArgumentException if <code>constant</code> is <code>Double.NaN</code> or infinite. 093 */ 094 public Interval(double constant) throws IllegalArgumentException { 095 if (Double.isNaN(constant)) 096 throw new IllegalArgumentException("An interval limit must not be NaN"); 097 098 if (Double.isInfinite(constant)) 099 throw new IllegalArgumentException("A single value interval must not be infinity"); 100 101 this.lowerLimit = this.upperLimit = constant; 102 this.lowerLimitClosed = this.upperLimitClosed = true; 103 } 104 105 /** Return the lower limit of this Interval. 106 <B>NOTE!</B> If <code>isLowerLimitClosed()</code> returns <code>false</code>, 107 the value returned by this function is <I>NOT</I> part of the interval. 108 @return the lower limit of this Interval. 109 */ 110 public double getLowerLimit() { 111 return lowerLimit; 112 } 113 114 /** Return the upper limit of this Interval. 115 <B>NOTE!</B> If <code>isUpperLimitClosed()</code> returns <code>false</code>, 116 the value returned by this function is <I>NOT</I> part of the interval. 117 @return the lower limit of this Interval. 118 */ 119 public double getUpperLimit() { 120 return upperLimit; 121 } 122 123 /** Indicates if this interval is closed at its lower limit or not (i.e. wether 124 the value returned by <code>getLowerLimit()</code> is included in the interval 125 or not). 126 @return <code>true</code> if the value returned by <code>getLowerLimit()</code> 127 is included in the Interval, <code>false</code> otherwise. 128 */ 129 public boolean isLowerLimitClosed() { 130 return lowerLimitClosed; 131 } 132 133 /** Indicates if this interval is closed at its upper limit or not (i.e. wether 134 the value returned by <code>getUpperLimit()</code> is included in the interval 135 or not). 136 @return <code>true</code> if the value returned by <code>getUpperLimit()</code> 137 is included in the Interval, <code>false</code> otherwise. 138 */ 139 public boolean isUpperLimitClosed() { 140 return upperLimitClosed; 141 } 142 143 /** Indicates wether the specified value is contained in this Interval or not. 144 @param value the value to check against this Interval. 145 @return <code>true</code> if <code>value</code> is contained in this Interval, 146 <code>false</code> otherwise. 147 */ 148 public boolean contains(double value) { 149 return (value > lowerLimit && value < upperLimit) || 150 (lowerLimitClosed && value == lowerLimit) || 151 (upperLimitClosed && value == upperLimit); 152 } 153 154 155 /** Indicates wether this interval intersects with another interval. 156 @param interval The interval to check this one against. 157 @return <code>true</code> if the intervals do intersect, <code>false</code> otherwise. 158 */ 159 public boolean intersects(Interval interval) { 160 if (interval == null) 161 return false; 162 163 if (interval == this) 164 return true; 165 166 if (equals(interval)) 167 return true; 168 169 // We need a special check if the intervals are adjacent and atleast 170 // one of them are open. 171 if (upperLimit == interval.lowerLimit && 172 (!upperLimitClosed || !interval.lowerLimitClosed)) { 173 return false; 174 } 175 else if (interval.upperLimit == lowerLimit && 176 (!interval.upperLimitClosed || !lowerLimitClosed)) { 177 return false; 178 } 179 180 return contains(interval.getLowerLimit()) || 181 contains(interval.getUpperLimit()) || 182 interval.contains(getLowerLimit()) || 183 interval.contains(getUpperLimit()); 184 } 185 186 /** Indicates wether this Interval fully encloses another interval. 187 @param interval The interval to check wether it is enclosed in this interval or not. 188 @return <code>true</code> if <code>interval</code> is fully enclosed within this Interval, 189 <code>false</code> otherwise. 190 */ 191 public boolean encloses(Interval interval) { 192 return contains(interval.lowerLimit) && 193 contains(interval.upperLimit); 194 } 195 196 public boolean equals(Object obj) { 197 if (obj == null) 198 return false; 199 200 if (!(obj instanceof Interval)) 201 return false; 202 203 Interval i = (Interval)obj; 204 return (lowerLimit == i.lowerLimit) && (upperLimit == i.upperLimit) && (lowerLimitClosed == i.lowerLimitClosed) && (upperLimitClosed == i.upperLimitClosed); 205 } 206 207 208 /** Compares two intervals, by essentially comparing their lower limit. 209 If lower limits are equal (as well as isLowerLimitClosed()), 210 the upper limits are compared. 211 @param i the interval to compare this interval to. 212 @return A negative value if the lower limit of this interval is less than 213 that of the compared one, zero (0) if the two intervals are equal, 214 and a positive value if the lower limit of this interval is greater 215 than that of the compared one. */ 216 public int compareTo(Interval i) { 217 if (lowerLimit < i.lowerLimit) 218 return -1; 219 220 if (lowerLimit > i.lowerLimit) 221 return 1; 222 223 // Lower limit are equal 224 if (lowerLimitClosed && !i.lowerLimitClosed) 225 return -1; 226 227 if (!lowerLimitClosed && i.lowerLimitClosed) 228 return 1; 229 230 // Lower limits and closure are equal, comparing upper limits. 231 if (upperLimit < i.upperLimit) 232 return -1; 233 234 if (upperLimit > i.upperLimit) 235 return 1; 236 237 // Upper limits are equal 238 if (upperLimitClosed && !i.upperLimitClosed) 239 return 1; 240 241 if (!upperLimitClosed && i.upperLimitClosed) 242 return -1; 243 244 // Intervals are indeed equal 245 return 0; 246 } 247 248 /** Returns the union of two intersecting or bordering intervals. 249 @param i1 An interval 250 @param i2 An interval intersecting or bordering <code>i1</code>. 251 @return A new interval containing the union of the the two intervals specified, or <code>null</code> if 252 the two intervals cannot be unioned into a single interval. 253 */ 254 public static Interval union(Interval i1, Interval i2) { 255 if (i1.intersects(i2)) { 256 // Order the intervals 257 if (i1.compareTo(i2) > 0) { 258 Interval temp = i1; 259 i1 = i2; 260 i2 = temp; 261 } 262 263 double llimit = i1.lowerLimit; 264 boolean llimitclosed = i1.lowerLimitClosed; 265 266 double ulimit; 267 boolean ulimitclosed; 268 269 if (i1.upperLimit > i2.upperLimit) { 270 ulimit = i1.upperLimit; 271 ulimitclosed = i1.upperLimitClosed; 272 } else if (i1.upperLimit < i2.upperLimit) { 273 ulimit = i2.upperLimit; 274 ulimitclosed = i2.upperLimitClosed; 275 } else { 276 ulimit = i1.upperLimit; 277 ulimitclosed = (i1.upperLimitClosed || i2.upperLimitClosed); 278 } 279 280 return new Interval(llimit, ulimit, llimitclosed, ulimitclosed); 281 } else if (i1.lowerLimit == i2.upperLimit && (i1.lowerLimitClosed || i2.upperLimitClosed) 282 || i1.upperLimit == i2.lowerLimit && (i1.upperLimitClosed || i2.lowerLimitClosed)) { 283 if (i2.upperLimit == i1.lowerLimit) { 284 Interval temp = i2; 285 i2 = i1; 286 i1 = temp; 287 } 288 289 return new Interval(i1.lowerLimit, i2.upperLimit, i1.lowerLimitClosed, i2.upperLimitClosed); 290 } else { 291 return null; 292 } 293 } 294 295 /** 296 * Calculates the negation of an interval 297 * @param interval is the interval 298 * @return the negated interval 299 */ 300 public static Interval negate(Interval interval) { 301 return new Interval(-interval.upperLimit, -interval.lowerLimit, 302 interval.upperLimitClosed, interval.lowerLimitClosed); 303 } 304 305 /** 306 * Calculates the sum of two intervals 307 * @param interval1 is the first interval 308 * @param interval2 is the second interval 309 * @return an interval describing the sum of the two intervals 310 */ 311 public static Interval add(Interval interval1, Interval interval2) { 312 boolean lowerLimitClosed = 313 interval1.lowerLimitClosed && interval2.lowerLimitClosed; 314 boolean upperLimitClosed = 315 interval1.upperLimitClosed && interval2.upperLimitClosed; 316 return new Interval(interval1.lowerLimit+interval2.lowerLimit, 317 interval1.upperLimit+interval2.upperLimit, 318 lowerLimitClosed, upperLimitClosed); 319 } 320 321 /** 322 * Calculates the difference between two intervals 323 * @param interval1 is the first interval 324 * @param interval2 is the second interval 325 * @return an interval describing the difference between the two intervals 326 */ 327 public static Interval sub(Interval interval1, Interval interval2) { 328 boolean lowerLimitClosed = 329 interval1.lowerLimitClosed && interval2.upperLimitClosed; 330 boolean upperLimitClosed = 331 interval1.upperLimitClosed && interval2.lowerLimitClosed; 332 333 return new Interval(interval1.lowerLimit-interval2.upperLimit, 334 interval1.upperLimit-interval2.lowerLimit, 335 lowerLimitClosed, upperLimitClosed); 336 } 337 338 339 /** 340 * Calculates the product of two intervals 341 * @param interval1 is the first interval 342 * @param interval2 is the second interval 343 * @return an interval describing the product of the two intervals 344 */ 345 public static Interval mul(Interval interval1, Interval interval2) { 346 TreeSet<Double> valuesClosed = new TreeSet<Double>(); 347 TreeSet<Double> valuesOpen = new TreeSet<Double>(); 348 349 double value = interval1.lowerLimit * interval2.lowerLimit; 350 if (interval1.lowerLimitClosed && interval2.lowerLimitClosed) 351 valuesClosed.add(value); 352 else 353 valuesOpen.add(value); 354 355 value = interval1.lowerLimit * interval2.upperLimit; 356 if (interval1.lowerLimitClosed && interval2.upperLimitClosed) 357 valuesClosed.add(value); 358 else 359 valuesOpen.add(value); 360 361 value = interval1.upperLimit * interval2.lowerLimit; 362 if (interval1.upperLimitClosed && interval2.lowerLimitClosed) 363 valuesClosed.add(value); 364 else 365 valuesOpen.add(value); 366 367 value = interval1.upperLimit * interval2.upperLimit; 368 if (interval1.upperLimitClosed && interval2.upperLimitClosed) 369 valuesClosed.add(value); 370 else 371 valuesOpen.add(value); 372 373 double lowerLimit, upperLimit; 374 boolean lowerLimitClosed, upperLimitClosed; 375 376 if (valuesClosed.size() > 0 && valuesOpen.size() > 0) { 377 if (valuesClosed.first()<=valuesOpen.first()) { 378 lowerLimit = valuesClosed.first(); 379 lowerLimitClosed = true; 380 } else { 381 lowerLimit = valuesOpen.first(); 382 lowerLimitClosed = false; 383 } 384 if (valuesClosed.last()>=valuesOpen.last()) { 385 upperLimit = valuesClosed.last(); 386 upperLimitClosed = true; 387 } else { 388 upperLimit = valuesOpen.last(); 389 upperLimitClosed = false; 390 } 391 } else { 392 if (valuesClosed.size() > 0) { 393 lowerLimitClosed = true; 394 upperLimitClosed = true; 395 lowerLimit = valuesClosed.first(); 396 upperLimit = valuesClosed.last(); 397 } else { 398 lowerLimitClosed = false; 399 upperLimitClosed = false; 400 lowerLimit = valuesOpen.first(); 401 upperLimit = valuesOpen.last(); 402 } 403 } 404 405 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 406 } 407 408 409 /** Calculates the cosine interval of an interval. 410 @return the interval corresponding to the possible values of running cosine on the values of this interval. 411 */ 412 public static Interval cos(Interval interval) { 413 if (interval.size() > 2 * Math.PI) { 414 return new Interval(-1, 1, true, true); 415 } 416 417 double llimit = interval.lowerLimit % (Math.PI * 2); 418 double ulimit = interval.upperLimit % (Math.PI * 2); 419 boolean llimc = interval.lowerLimitClosed; 420 boolean ulimc = interval.upperLimitClosed; 421 if (llimit > ulimit) { 422 llimit-=Math.PI*2; 423 } 424 425 Interval tmp = new Interval(llimit, ulimit, llimc, ulimc); 426 427 if (tmp.lowerLimit >= Math.PI) 428 return Interval.negate(Interval.cos(Interval.sub(interval, new Interval(Math.PI)))); 429 430 if (tmp.lowerLimit < 0 && tmp.upperLimit < 0) { 431 Interval vv = new Interval(Math.PI * 2 * Math.ceil(-tmp.lowerLimit)); 432 Interval qq = Interval.add(vv, tmp); 433 return Interval.cos(qq); 434 } 435 436 if (tmp.lowerLimit < 0 && tmp.upperLimit >= 0) { 437 Interval neg = new Interval(0, -tmp.lowerLimit, false, tmp.lowerLimitClosed); 438 Interval pos = new Interval(0, tmp.upperLimit, true, tmp.upperLimitClosed); 439 return Interval.union(Interval.cos(neg), Interval.cos(pos)); 440 } 441 442 double l = tmp.lowerLimit; 443 double u = tmp.upperLimit; 444 445 if (u <= Math.PI) { 446 return new Interval(Math.cos(u), Math.cos(l), tmp.upperLimitClosed, tmp.lowerLimitClosed); } 447 else if (u <= (Math.PI * 2)) 448 return new Interval(-1, Math.cos(Math.min((Math.PI * 2) - u, l)), true, ((Math.PI * 2) - u < l) ? interval.upperLimitClosed : interval.lowerLimitClosed); 449 else 450 return new Interval(-1, 1, true, true); 451 } 452 453 /** Calculates the sine interval of an interval. 454 @return the interval corresponding to the possible values of running sine on the values of this interval. 455 */ 456 public static Interval sin(Interval interval) { 457 Interval tmp = Interval.sub(interval, new Interval(Math.PI / 2)); 458 return Interval.cos(tmp); 459 } 460 461 public static Interval tan(Interval interval) throws MathException { 462 Interval s = Interval.sin(interval); 463 Interval c = Interval.cos(interval); 464 try { 465 Interval d = Interval.div(s,c); 466 return d; 467 } catch (MathException e) { 468 throw new MathException("Illegal tan value condition"); 469 } 470 //System.out.println("tan(" + interval + ") = " + s + " / " + c + " = " + d); 471 472 } 473 474 /** 475 * Calculates the quotient of two intervals 476 * @param interval1 is the first interval 477 * @param interval2 is the second interval 478 * @return an interval describing the quotient of the two intervals 479 * @throws ArithmeticException if the second interval contains the value 0. Can't divide by zero. 480 */ 481 public static Interval div(Interval interval1, Interval interval2) throws MathException { 482 if (interval2.contains(0.0)) 483 throw new MathException("Division by zero condition"); 484 485 TreeSet<Double> valuesOpen = new TreeSet<Double>(); 486 TreeSet<Double> valuesClosed = new TreeSet<Double>(); 487 double value; 488 489 if (interval2.lowerLimit != Double.NEGATIVE_INFINITY) { 490 if (interval2.lowerLimit!=0) { 491 value = interval1.lowerLimit / interval2.lowerLimit; 492 if (interval1.lowerLimitClosed && interval2.lowerLimitClosed) 493 valuesClosed.add(value); 494 else 495 valuesOpen.add(value); 496 value = interval1.upperLimit / interval2.lowerLimit; 497 if (interval1.upperLimitClosed && interval2.lowerLimitClosed) 498 valuesClosed.add(value); 499 else 500 valuesOpen.add(value); 501 } 502 else { // we now know that the lower limit of interval2 is positive 503 if (interval1.lowerLimit < 0) 504 valuesOpen.add(Double.NEGATIVE_INFINITY); 505 if (interval1.upperLimit > 0) 506 valuesOpen.add(Double.POSITIVE_INFINITY); 507 } 508 } else { 509 valuesOpen.add(0.0); 510 } 511 512 if (interval2.upperLimit != Double.POSITIVE_INFINITY) { 513 if (interval2.upperLimit!=0) { 514 value = interval1.lowerLimit / interval2.upperLimit; 515 if (interval1.lowerLimitClosed && interval2.upperLimitClosed) 516 valuesClosed.add(value); 517 else 518 valuesOpen.add(value); 519 value = interval1.upperLimit / interval2.upperLimit; 520 if (interval1.upperLimitClosed && interval2.upperLimitClosed) 521 valuesClosed.add(value); 522 else 523 valuesOpen.add(value); 524 } 525 else { // we now know that the upper limit of interval2 is negative 526 if (interval1.lowerLimit < 0) 527 valuesOpen.add(Double.POSITIVE_INFINITY); 528 if (interval1.upperLimit > 0) 529 valuesOpen.add(Double.NEGATIVE_INFINITY); 530 } 531 } else { 532 valuesOpen.add(0.0); 533 } 534 535 double lowerLimit, upperLimit; 536 boolean lowerLimitClosed, upperLimitClosed; 537 538 if (valuesClosed.size() > 0 && valuesOpen.size() > 0) { 539 if (valuesClosed.first()<=valuesOpen.first()) { 540 lowerLimit = valuesClosed.first(); 541 lowerLimitClosed = true; 542 } else { 543 lowerLimit = valuesOpen.first(); 544 lowerLimitClosed = false; 545 } 546 if (valuesClosed.last()>=valuesOpen.last()) { 547 upperLimit = valuesClosed.last(); 548 upperLimitClosed = true; 549 } else { 550 upperLimit = valuesOpen.last(); 551 upperLimitClosed = false; 552 } 553 } else { 554 if (valuesClosed.size() > 0) { 555 lowerLimitClosed = true; 556 upperLimitClosed = true; 557 lowerLimit = valuesClosed.first(); 558 upperLimit = valuesClosed.last(); 559 } else { 560 lowerLimitClosed = false; 561 upperLimitClosed = false; 562 lowerLimit = valuesOpen.first(); 563 upperLimit = valuesOpen.last(); 564 } 565 } 566 567 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 568 } 569 570 571 /** 572 * Calculates the first <code>Interval raised to the power of the second <code>Interval</code> 573 * @param base is the first <code>Interval</code> 574 * @param exponent is the second <code>Interval</code> 575 * @return the first <code>Interval</code> raised to the power of the second <code>Interval</code> 576 * @throws MathException if the first <code>Interval</code> contains values below zero and the second <code>Interval</code> is not a singel integer value 577 */ 578 public static Interval pow(Interval base, Interval exponent) throws MathException { 579 if (base.lowerLimit < 0 && Math.ceil(exponent.lowerLimit) != Math.floor(exponent.upperLimit)) 580 throw new MathException("Negative number raised to a non integer condition"); 581 582 TreeSet<Double> valuesOpen = new TreeSet<Double>(); 583 TreeSet<Double> valuesClosed = new TreeSet<Double>(); 584 if (base.lowerLimitClosed && exponent.lowerLimitClosed) 585 valuesClosed.add(Math.pow(base.lowerLimit, exponent.lowerLimit)); 586 else 587 valuesOpen.add(Math.pow(base.lowerLimit, exponent.lowerLimit)); 588 589 if (base.lowerLimitClosed && exponent.upperLimitClosed) 590 valuesClosed.add(Math.pow(base.lowerLimit, exponent.upperLimit)); 591 else 592 valuesOpen.add(Math.pow(base.lowerLimit, exponent.upperLimit)); 593 594 if (base.upperLimitClosed && exponent.lowerLimitClosed) 595 valuesClosed.add(Math.pow(base.upperLimit, exponent.lowerLimit)); 596 else 597 valuesOpen.add(Math.pow(base.upperLimit, exponent.lowerLimit)); 598 599 if (base.upperLimitClosed && exponent.upperLimitClosed) 600 valuesClosed.add(Math.pow(base.upperLimit, exponent.upperLimit)); 601 else 602 valuesOpen.add(Math.pow(base.upperLimit, exponent.upperLimit)); 603 604 if (base.contains(0)) 605 valuesClosed.add(0.0); 606 607 608 double lowerLimit, upperLimit; 609 boolean lowerLimitClosed, upperLimitClosed; 610 611 if (valuesClosed.size() > 0 && valuesOpen.size() > 0) { 612 if (valuesClosed.first()<=valuesOpen.first()) { 613 lowerLimit = valuesClosed.first(); 614 lowerLimitClosed = true; 615 } else { 616 lowerLimit = valuesOpen.first(); 617 lowerLimitClosed = false; 618 } 619 if (valuesClosed.last()>=valuesOpen.last()) { 620 upperLimit = valuesClosed.last(); 621 upperLimitClosed = true; 622 } else { 623 upperLimit = valuesOpen.last(); 624 upperLimitClosed = false; 625 } 626 } else { 627 if (valuesClosed.size() > 0) { 628 lowerLimitClosed = true; 629 upperLimitClosed = true; 630 lowerLimit = valuesClosed.first(); 631 upperLimit = valuesClosed.last(); 632 } else { 633 lowerLimitClosed = false; 634 upperLimitClosed = false; 635 lowerLimit = valuesOpen.first(); 636 upperLimit = valuesOpen.last(); 637 } 638 } 639 640 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 641 } 642 643 644 /** 645 * Calculates the natural logarithm for an <code>Interval</code> 646 * @param interval is the <code>Interval</code> 647 * @return the natural logarithm for the <code>Interval</code> 648 * @throws MathException if the <code>Interval</code> contains values below zero 649 */ 650 public static Interval ln(Interval interval) throws MathException { 651 if (interval.lowerLimit<0 || interval.contains(0.0)) 652 throw new MathException("Natural logarithm on a value below zero condition"); 653 654 double lowerLimit = Math.log(interval.lowerLimit); 655 double upperLimit = Math.log(interval.upperLimit); 656 boolean lowerLimitClosed, upperLimitClosed; 657 658 if (lowerLimit==Double.NEGATIVE_INFINITY) 659 lowerLimitClosed = false; 660 else 661 lowerLimitClosed = interval.lowerLimitClosed; 662 663 // if upperLimit == Double.NEGATIVE_INFINITY then interval.upperLimitClosed must be false 664 // so we don't have to check for that condition. 665 upperLimitClosed = interval.upperLimitClosed; 666 667 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 668 } 669 670 /** 671 * Calculates the base 10 logarithm of an <code>Interval</code> 672 * @param interval is the <code>Interval</code> 673 * @return the base 10 logarithm of the <code>Interval</code> 674 * @throws MathException if the <code>Interval</code> contains values below zero 675 */ 676 public static Interval log10(Interval interval) throws MathException { 677 if (interval.lowerLimit<0 || interval.contains(0.0)) 678 throw new MathException("Logarithm on a value below zero condition"); 679 680 double lowerLimit = Math.log10(interval.lowerLimit); 681 double upperLimit = Math.log10(interval.upperLimit); 682 boolean lowerLimitClosed, upperLimitClosed; 683 684 if (lowerLimit==Double.NEGATIVE_INFINITY) 685 lowerLimitClosed = false; 686 else 687 lowerLimitClosed = interval.lowerLimitClosed; 688 689 // if upperLimit == Double.NEGATIVE_INFINITY then interval.upperLimitClosed must be false 690 // so we don't have to check for that condition. 691 upperLimitClosed = interval.upperLimitClosed; 692 693 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 694 } 695 696 /** 697 * Calculates Eulers number e raised to an <code>Interval</code> 698 * @param interval is the <code>Interval</code> 699 * @return Eulers number e raised to the <code>Interval</code> 700 */ 701 public static Interval exp(Interval interval) { 702 double lowerLimit = Math.exp(interval.lowerLimit); 703 double upperLimit = Math.exp(interval.upperLimit); 704 boolean lowerLimitClosed, upperLimitClosed; 705 if (upperLimit == Double.POSITIVE_INFINITY) 706 upperLimitClosed = false; 707 else 708 upperLimitClosed = interval.upperLimitClosed; 709 lowerLimitClosed = interval.lowerLimitClosed; 710 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 711 } 712 713 /** 714 * Calculates the square root of an <code>Interval</code> 715 * @param interval is the <code>Interval</code> 716 * @return the square root of the <code>Interval</code> 717 * @throws MathException if the <code>Interval</code> contains values below zero 718 */ 719 public static Interval sqrt(Interval interval) throws MathException { 720 if (interval.lowerLimit < 0) 721 throw new MathException("Square root of a value below zero condition"); 722 return new Interval(Math.sqrt(interval.lowerLimit), Math.sqrt(interval.upperLimit), 723 interval.lowerLimitClosed, interval.upperLimitClosed); 724 } 725 726 /** 727 * Calculates the <code>Interval</code> you get if you convert the values from <code>double</code> to <code>int</code> 728 * @param interval is the <code>Interval</code> 729 * @return the <code>Interval</code> you get if you convert the values from <code>double</code> to <code>int</code> 730 */ 731 public static Interval toInt(Interval interval) { 732 double lowerLimit, upperLimit; 733 if (interval.lowerLimit < 0 && 734 Math.ceil(interval.lowerLimit) == Math.floor(interval.lowerLimit) && 735 !interval.lowerLimitClosed) 736 lowerLimit = ((int)interval.lowerLimit) + 1; 737 else 738 lowerLimit = (int)interval.lowerLimit; 739 740 if (interval.upperLimit > 0 && 741 Math.ceil(interval.upperLimit) == Math.floor(interval.upperLimit) && 742 !interval.upperLimitClosed) 743 upperLimit = ((int)interval.upperLimit) - 1; 744 else 745 upperLimit = (int)interval.upperLimit; 746 747 return new Interval(lowerLimit, upperLimit, true, true); 748 } 749 750 751 /** 752 * Calculates the absolute value <code>Interval</code> of an <code>Interval</code> 753 * @param interval is the <code>Interval</code> 754 * @return the absolute value <code>Interval</code> of the <code>Interval</code> 755 */ 756 public static Interval abs(Interval interval) { 757 boolean lowerLimitClosed, upperLimitClosed; 758 double lowerLimit, upperLimit; 759 double lowerAbs = Math.abs(interval.lowerLimit); 760 double upperAbs = Math.abs(interval.upperLimit); 761 if (interval.contains(0)) { 762 lowerLimit = 0; 763 lowerLimitClosed = true; 764 if (lowerAbs<upperAbs) { 765 upperLimit = upperAbs; 766 upperLimitClosed = interval.upperLimitClosed; 767 } else if (lowerAbs>upperAbs) { 768 upperLimit = lowerAbs; 769 upperLimitClosed = interval.lowerLimitClosed; 770 } else { // lowerAbs == upperAbs 771 upperLimit = upperAbs; 772 upperLimitClosed = interval.lowerLimitClosed || interval.upperLimitClosed; 773 } 774 } else { 775 if (lowerAbs<upperAbs) { 776 lowerLimit = lowerAbs; 777 upperLimit = upperAbs; 778 lowerLimitClosed = interval.lowerLimitClosed; 779 upperLimitClosed = interval.upperLimitClosed; 780 } else { 781 lowerLimit = upperAbs; 782 upperLimit = lowerAbs; 783 lowerLimitClosed = interval.upperLimitClosed; 784 upperLimitClosed = interval.lowerLimitClosed; 785 } 786 } 787 788 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 789 } 790 791 /** 792 * Returns an <code>Interval</code> holding all possible values you get from the <code>Math.random()</code> method 793 * @return an <code>Interval</code> holding all possible values you get from the <code>Math.random()</code> mathod 794 */ 795 public static Interval rand() { 796 return new Interval(0, 1, true, false); 797 } 798 799 /** 800 * Calculates the factorial of an <code>Interval</code> 801 * @param interval is the <code>Interval</code> 802 * @return the factorial of the <code>Interval</code> 803 * @throws MathException if the <code>Interval</code> contains values below zero 804 */ 805 public static Interval fact(Interval interval) throws MathException { 806 if (interval.lowerLimit<0) 807 throw new MathException("Faculty on a value below zero condition"); 808 809 double lowerLimit = fac(interval.lowerLimit); 810 double upperLimit; 811 if (!interval.upperLimitClosed && Math.floor(interval.upperLimit) == Math.ceil(interval.upperLimit)) 812 upperLimit = fac(interval.upperLimit-1); 813 else 814 upperLimit = fac(interval.upperLimit); 815 816 return new Interval(lowerLimit, upperLimit, true, true); 817 } 818 819 /** 820 * Calculates the sum of a <code>List</code> of intervals 821 * @param intervals is the intervals to be summarized 822 * @return an <code>Interval</code> describing the sum of the intervals 823 */ 824 public static Interval sum(List<Interval> intervals) { 825 double lowerLimit = 0; 826 double upperLimit = 0; 827 boolean lowerLimitClosed = true; 828 boolean upperLimitClosed = true; 829 for (Interval interval:intervals) { 830 lowerLimit+=interval.lowerLimit; 831 upperLimit+=interval.upperLimit; 832 lowerLimitClosed&=interval.lowerLimitClosed; 833 upperLimitClosed&=interval.upperLimitClosed; 834 } 835 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 836 } 837 838 /** 839 * Calculates the average interval for a <code>List</code> of intervals 840 * @param intervals is the intervals to calculate the average for 841 * @return an <code>Interval</code> describing the average of the intervals 842 */ 843 public static Interval avg(List<Interval> intervals) { 844 Interval result = new Interval(0); // for the compiler to be happy 845 try { 846 result = div(sum(intervals), new Interval(intervals.size())); 847 } catch (MathException e) { 848 e.printStackTrace(); // should never happen 849 } 850 return result; 851 } 852 853 public Interval clone() { 854 return new Interval(lowerLimit, upperLimit, lowerLimitClosed, upperLimitClosed); 855 } 856 857 858 /** 859 Retrieve the size of this interval. 860 @return the size (or width) of this interval. 861 */ 862 public double size() { 863 double clow = lowerLimitClosed ? lowerLimit : (lowerLimit + Math.ulp(lowerLimit)); 864 double cup = upperLimitClosed ? upperLimit : (upperLimit - Math.ulp(upperLimit)); 865 return cup - clow; 866 } 867 868 869 /** Converts this Interval into a string representation. 870 @return a String containing a textual representation of this interval with the 871 same syntax as is used for specifying assertions. 872 */ 873 public String toString() { 874 String s = ""; 875 876 // Check if interval denotes a single value 877 if (lowerLimitClosed && upperLimitClosed && lowerLimit == upperLimit) { 878 s += lowerLimit; 879 return s; 880 } 881 882 // Check if interval denotes a one-sided interval 883 if (lowerLimit == Double.NEGATIVE_INFINITY && upperLimit != Double.POSITIVE_INFINITY) { 884 s += (upperLimitClosed ? "<= " : "< ") + upperLimit; 885 return s; 886 } 887 else if (upperLimit == Double.POSITIVE_INFINITY && lowerLimit != Double.NEGATIVE_INFINITY) { 888 s += (lowerLimitClosed ? ">= " : "> ") + lowerLimit; 889 return s; 890 } 891 892 // Interval denotes a two sided interval 893 if (lowerLimitClosed) 894 s+="["; 895 else 896 s+="]"; 897 s+=lowerLimit + " to " + upperLimit; 898 if (upperLimitClosed) 899 s+="]"; 900 else 901 s+="["; 902 return s; 903 } 904 905 /** 906 * Private method to calculate the faculty of a number>=0 after converting it to an integer 907 * @param x is the number 908 * @return the faculty of the number x 909 */ 910 private static int fac(double x) { 911 int n = (int)x; 912 int prod = 1; 913 while (n>0) { 914 prod*=n; 915 n--; 916 } 917 return prod; 918 } 919 920 /** 921 * Private method to calculate the modulo of two double values 922 * @param numerator is the numerator 923 * @param denominator is the denominator 924 * @return the modulo as a double of two double values 925 */ 926 @SuppressWarnings("unused") 927 private static double mod(double numerator, double denominator) { 928 long numeratorL = (long)numerator; 929 long denominatorL = (long)denominator; 930 int exp = 0; 931 while (numerator > numeratorL || denominator > denominatorL) { 932 numerator*=10; 933 denominator*=10; 934 numeratorL = (long)numerator; 935 denominatorL = (long)denominator; 936 exp++; 937 } 938 return (numeratorL%denominatorL) / Math.pow(10,exp); 939 } 940 941}