# this class contains an implementation of interval arithmetic in Python

class IntervalRangeError(Exception):
    pass

class IntervalComparisonError(Exception):
    pass

class Interval(object):

    def __init__(self, min=0, max=None):
        if max == None:
            max = min
        if min > max:
            raise IntervalRangeError
        self.min = min
        self.max = max

    def __add__(self, y):
        if not isinstance(y, Interval):
            raise TypeError
        return Interval(self.min+y.min, self.max+y.max)

    def __lt__(self, y):
        if not isinstance(y, Interval):
            raise TypeError
        if self.max < y.min:
            return True
        elif y.max < self.min:
            return False
        else:
            raise IntervalComparisonError

    def __sub__(self, y):
        if not isinstance(y, Interval):
            raise TypeError
        return Interval(self.min-y.min, self.max-y.max)

    def __pow__(self, expo):
        if expo > 0:
            return Interval(self.min**expo, self.max**expo)
        else:
            # use division to do the negative power, in case of zero-spanning
            return Interval(1,1)/Interval(self.min**(-expo), self.min**(-expo))

    def __mul__(self, y):
        if not isinstance(y, Interval):
            raise TypeError
        extrema = [self.min*y.min, self.min*y.max,
                self.max*y.min, self.max*y.max]
        return Interval(min(extrema), max(extrema))

    def __truediv__(self, y):
        if not isinstance(y, Interval):
            raise TypeError
        if y.min <= 0 <= y.max:
            raise ZeroDivisionError
        extrema = [self.min/y.min, self.min/y.max,
                self.max/y.min, self.max/y.max]
        return Interval(min(extrema), max(extrema))

    def __repr__(self):
        return "[%f..%f]" % (self.min, self.max)

if __name__ == '__main__':
    a = Interval(0.1, 0.25)
    b = Interval(0.125, 0.185)
    c = Interval(3)
    print('Test of the interval arithmetic module:')
    print('          a =', a)
    print('          b =', b)
    print('          c =', c)
    print('        a+b =', a+b)
    print('        a-b =', a-b)
    print('        a*b =', a*b)
    print('        a/b =', a/b)
    print('       a**2 =', a**2)
    print('  a**(-0.5) =', a**(-0.5))
    print('(a*b)/(a+b) =', (a*b)/(a+b))
    d = Interval(-1,1)
    try:
        e = Interval(1)/d
    except ZeroDivisionError:
        print("division by interval containing zero was caught")
    try:
        if a < c:
            print("less-than comparison works")
        if c > a:
            print("greater-than comparison works")
        if a < d:
            pass  # should trigger exception
    except IntervalComparisonError:
        print("interval comparison exception handled")






