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    }