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}