# Copyright (C) 2002, 2003 by Intevation GmbH
# Authors:
# Thomas Koester <tkoester@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

"""
Range class for Scientific Parameter
"""

__version__ = "$Revision: 1.5 $"
# $Source: /thubanrepository/thuban/Thuban/Model/range.py,v $
# $Id: range.py,v 1.5 2003/07/24 17:52:48 bh Exp $

import re
import types

_inf = float('1e1000')   # FIXME: hack for infinite

class Range:

    number_re = '(?P<%s>-?(\d*\.?\d*([eE][\-+]?\d+)?|oo))'
    brace_re = '(?P<%s>[][])'
    range_re = '^' + brace_re % 'left' + number_re % 'begin' + \
               ';' + number_re % 'end' + brace_re % 'right' + '$'
    parse_range = re.compile(range_re)

    def __init__(self, range=None):
        self._SetRange(range)

    def _SetRange(self, range):
        if isinstance(range, Range):
            self._SetRange(range.GetRange())
        elif range in [None, '']:
            self._SetRange(']-oo;oo[')
        elif type(range) == types.TupleType:
            if (len(range) == 4 and range[0] in ['[', ']']
                and range[3] in ['[', ']']):
                self._left = range[0]
                self._begin = self.float(range[1])
                self._end = self.float(range[2])
                self._right = range[3]
                if (self._begin > self._end or
                    (self._begin == self._end and
                     (self._left != '[' or self._right != ']'))):
                    raise ValueError("illegal range: %s" % (range,))
            else:
                raise ValueError("can't parse range: %r" % (range,))
        else:
            self._range = range
            match = self.parse_range.match(self._range)
            if match:
                self._SetRange((match.group('left'),
                              match.group('begin'),
                              match.group('end'),
                              match.group('right')))
            else:
                raise ValueError("can't parse range: %s" % (range,))

    def GetRange(self):
        """return internal representation of range

        4-tuple ('[' or ']', begin(float), end(float), '[' or ']')

        """
        return (self._left, self._begin, self._end, self._right)

    def float(self, value):
        """convert string or number to float"""
        if value == 'oo':
            return _inf
        elif value == '-oo':
            return -_inf
        else:
            return float(value)

    def _float2string(self, value):
        """convert float value to string

        (minus) infinity will be converted to (-)oo,
        scientific notation will be used if necessary.

        """
        if value == _inf:
            return 'oo'
        elif value == -_inf:
            return '-oo'
        else:
            return "%g" % (value,)

    def string(self, range):
        """convert internal representation to string"""
        left, begin, end, right = range
        return "%s%s;%s%s" % (left, self._float2string(begin),
                              self._float2string(end), right)

    def __contains__(self, value):
        if self._left == ']':
            contains = value > self._begin
        else:
            contains = value >= self._begin
        if self._right == '[':
            contains = contains and (value < self._end)
        else:
            contains = contains and (value <= self._end)
        return contains

    def __eq__(self, other):
        return (self.GetRange() == other.GetRange())

    def __ne__(self, other):
        return not self.__eq__(other)

    def __str__(self):
        return self.string(self.GetRange())


def _test():
    range1 = Range(']0;99]')
    print 'range1 =', range1, range1.GetRange()
    for i in [-0.1, 0, 0.1, 9.9, 99, 99.9]:
        print '%4.1f in range1 =' % i, i in range1
    range2 = Range(']-oo;10[')
    print 'range2 =', range2, range2.GetRange()
    for i in [-0.1, 0, 0.1, 9.9, 10, 10.1]:
        print '%4.1f not in range2 =' % i, i not in range2
    range3 = Range(']1e-1;1E2]')
    print 'range3 =', range3, range3.GetRange()
    for i in [0, 0.1, 0.11, 10, 100, 101]:
        print '%4.1f not in range3 =' % i, i not in range3
    print 'range3 != range2 =', range3 != range2
    print 'range3 != Range("]1e-1;1E2]") =', range3 != Range("]1e-1;1E2]")

    range4 = Range('')
    print 'range4 =', range4, range4.GetRange()

    range5 = Range(']0;99E+00]')
    print 'range5 =', range5, range5.GetRange()
    range6 = Range(']0;99E+01]')
    print 'range6 =', range6, range6.GetRange()
    range7 = Range(']0;99E-01]')
    print 'range7 =', range7, range7.GetRange()

if __name__ == "__main__":
    _test()
