001 /*
002 * Copyright (c) 2005 Peter Palotas, Fredrik Johansson, Einar Pehrson,
003 * Sebastian Kekkonen, Lars Magnus Lång, 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 */
023 package csheets.ext.assertion;
024
025 import java.io.Serializable;
026 import java.util.List;
027 import 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 */
040 public 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 }