# 1. Interval¶

Basic interval arithmetic.

## 1.1. Usage¶

### 1.1.1. Creating intervals¶

The class `Interval` is used to create intervals, i. e. subsets of a set of values, by defining a lower and an upper endpoint.

The simplest way is calling `Interval` without arguments, resulting in both endpoints to be infinite:

```>>> ival = Interval()
>>> ival
Interval()
>>> str(ival)
'(-inf .. +inf)'
```

For getting a more useful interval, it’s neccessary to specify atleast one endpoint:

```>>> ival = Interval(LowerClosedLimit(0))
>>> ival
Interval(lower_limit=Limit(True, 0, True))
>>> str(ival)
'[0 .. +inf)'
```
```>>> ival = Interval(upper_limit=UpperClosedLimit(100.))
>>> ival
Interval(upper_limit=Limit(False, 100.0, True))
>>> str(ival)
'(-inf .. 100.0]'
```
```>>> ival = Interval(LowerClosedLimit(0), UpperOpenLimit(27))
>>> ival
Interval(lower_limit=Limit(True, 0, True), upper_limit=Limit(False, 27, False))
>>> str(ival)
'[0 .. 27)'
```

Any type which defines a total ordering can be used for the limits:

```>>> ClosedInterval('a', 'zzz')
Interval(lower_limit=Limit(True, 'a', True), upper_limit=Limit(False, 'zzz', True))
```

Several factory functions can be used as shortcut. For example:

```>>> LowerClosedInterval(30)
Interval(lower_limit=Limit(True, 30, True))
>>> UpperOpenInterval(0)
Interval(upper_limit=Limit(False, 0, False))
>>> ClosedInterval(1, 3)
Interval(lower_limit=Limit(True, 1, True), upper_limit=Limit(False, 3, True))
>>> ChainableInterval(0, 5)
Interval(lower_limit=Limit(True, 0, True), upper_limit=Limit(False, 5, False))
```

### 1.1.2. Operations on intervals¶

The limits of an interval can be retrieved via properties:

```>>> ival = ClosedInterval(0, 100)
>>> ival.lower_limit
Limit(True, 0, True)
>>> ival.upper_limit
Limit(False, 100, True)
>>> ival.limits
(Limit(True, 0, True), Limit(False, 100, True))
```

Several methods can be used to test for specifics of an interval. For example:

```>>> ival.is_bounded()
True
>>> ival.is_finite()
True
>>> ival.is_left_open()
False
```

Intervals can be tested for including a value:

```>>> 74 in ival
True
>>> -4 in ival
False
```

Intervals can be compared:

```>>> ival2 = LowerOpenInterval(100)
>>> ival3 = LowerClosedInterval(100)
>>> ival < ival2
True
>>> ival < ival3
True
>>> ival2 < ival3
False
>>> ival2 == ival3
False
>>> ival3 < ival2
True
False
False
>>> ival4 = UpperClosedInterval(100)
True
>>> ival.is_overlapping(ival3)
True
>>> ival.is_subset(ival4)
True
```

## 1.2. Classes¶

class `ivalutils.interval.``Limit`(lower, value, closed=True)

Lower / upper limit of an Interval.

Parameters: lower (bool) – specifies which endpoint of an interval this limit defines: True -> lower endpoint, False -> upper endpoint value () – limiting value (can be of any type that defines a total ordering) closed (bool) – specifies whether value itself is the endpoint or not: True -> the interval that has this limit includes value, False -> the interval that has this limit does not includes value instance of `Limit` `AssertionError` – lower is not instance of bool `AssertionError` – value is None `AssertionError` – closed is not instance of bool
`__eq__`(other)

self == other

`__ge__`(other)

self >= other

`__gt__`(other)

self > other

`__hash__`()

hash(self)

`__le__`(other)

self <= other

`__lt__`(other)

self < other

`adjacent_limit`()

Return the limit adjacent to self.

class `ivalutils.interval.``Interval`(lower_limit=None, upper_limit=None)

An Interval defines a subset of a set of values by optionally giving a lower and / or an upper limit.

The base set of values - and therefore the given limits - must have a common base type which defines a total order on the values.

If both limits are given, the interval is said to be bounded or finite, if only one or neither of them is given, the interval is said to be unbounded or infinite.

If only the lower limit is given, the interval is called lower bounded or left bounded (maybe also upper unbounded, upper infinite, right unbounded or right infinite). Correspondingly, if only the upper limit is given, the interval is called upper bounded or right bounded (maybe also lower unbounded, lower infinite, left unbounded or left infinite).

For both limits (aka endpoints) must be specified whether the given value is included in the interval or not. In the first case the limit is called closed, otherwise open.

Parameters: lower_limit (`Limit`) – lower limit (default: None) upper_limit (`Limit`) – upper limit (default: None) instance of `Interval`

If None is given as lower_limit, the resulting interval is lower infinite. If None is given as upper_limit, the resulting interval is upper infinite.

Raises: `InvalidInterval` – lower_limit is not a lower limit `InvalidInterval` – upper_limit is not a upper limit `InvalidInterval` – lower_limit > upper_limit `IncompatibleLimits` – values of lower_limit and upper_limit are not comparable
`__and__`(other)

self & other

`__contains__`(value)

True if value does not exceed the limits of self.

`__copy__`()

Return self (Interval instances are immutable).

`__eq__`(other)

self == other.

True if all elements contained in self are also contained in other and all elements contained in other are also contained in self.

This is exactly the case if self.limits == other.limits.

`__ge__`(other)

self >= other.

`__gt__`(other)

self > other.

True if there is an element in self which is greater than all elements in other or there is an element in other which is smaller than all elements in self.

This is exactly the case if self.limits > other.limits.

`__hash__`()

hash(self)

`__le__`(other)

self <= other.

`__lt__`(other)

self < other.

True if there is an element in self which is smaller than all elements in other or there is an element in other which is greater than all elements in self.

This is exactly the case if self.limits < other.limits.

`__or__`(other)

self | other

`__sub__`(other)

self - other

`is_adjacent`(other)

`is_disjoint`(other)

True if self contains no elements in common with other.

`is_lower_adjacent`(other)

`is_overlapping`(other)

True if there is a common element in self and other.

`is_subset`(other)

True if self defines a proper subset of other, i.e. all elements contained in self are also contained in other, but not the other way round.

`is_upper_adjacent`(other)

`limits`

Lower and upper limit as tuple.

`lower_limit`

Lower limit (LowerInfiniteLimit, if no lower limit was given.)

`upper_limit`

Upper limit (UpperInfiniteLimit, if no upper limit was given.)

## 1.3. Factoryfunctions¶

`ivalutils.interval.``LowerLimit`(value, closed=True)

Create a lower limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) closed (bool) – specifies whether value itself is the endpoint or not: True -> the interval that has this limit includes value, False -> the interval that has this limit does not includes value instance of `Limit` `AssertionError` – value is None `AssertionError` – closed is not instance of bool
`ivalutils.interval.``LowerInfiniteLimit`()

Create a lower infinite limit (a singleton).

`ivalutils.interval.``UpperLimit`(value, closed=True)

Create an upper limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) closed (bool) – specifies whether value itself is the endpoint or not: True -> the interval that has this limit includes value, False -> the interval that has this limit does not includes value instance of `Limit` `AssertionError` – value is None `AssertionError` – closed is not instance of bool
`ivalutils.interval.``UpperInfiniteLimit`()

Create an upper infinite limit (a singleton).

`ivalutils.interval.``LowerClosedLimit`(value)

Create a lower closed limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) instance of `Limit` `AssertionError` – value is None
`ivalutils.interval.``LowerOpenLimit`(value)

Create a lower open limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) instance of `Limit` `AssertionError` – value is None
`ivalutils.interval.``UpperClosedLimit`(value)

Create an upper closed limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) instance of `Limit` `AssertionError` – value is None
`ivalutils.interval.``UpperOpenLimit`(value)

Create an upper open limit.

Parameters: value () – limiting value (can be of any type that defines a total ordering) instance of `Limit` `AssertionError` – value is None
`ivalutils.interval.``ChainableInterval`(lower_value, upper_value, lower_closed=True)

Create Interval with one closed and one open endpoint.

`ivalutils.interval.``ClosedInterval`(lower_value, upper_value)

Create Interval with closed endpoints.

`ivalutils.interval.``LowerClosedInterval`(lower_value)

Create Interval with closed lower and infinite upper endpoint.

`ivalutils.interval.``LowerOpenInterval`(lower_value)

Create Interval with open lower and infinite upper endpoint.

`ivalutils.interval.``OpenBoundedInterval`(lower_value, upper_value)

Create Interval with open endpoints.

`ivalutils.interval.``OpenFiniteInterval`(lower_value, upper_value)

Create Interval with open endpoints.

`ivalutils.interval.``UpperClosedInterval`(upper_value)

Create Interval with infinite lower and closed upper endpoint.

`ivalutils.interval.``UpperOpenInterval`(upper_value)

Create Interval with infinite lower and open upper endpoint.

## 1.4. Exceptions¶

class `ivalutils.interval.``IncompatibleLimits`

Raised when comparing limits with incompatible types of values.

class `ivalutils.interval.``InvalidInterval`

Raised when an invalid Interval would be created.

# 2. IntervalChain¶

## 2.1. Usage¶

### 2.1.1. Creating interval chains¶

The class `IntervalChain` is used to create sequences of adjacent intervals:

```>>> ic = IntervalChain(('a', 'd', 'g', 'z'))
>>> ic
IntervalChain(('a', 'd', 'g', 'z'))
```

The default is to create an interval sequence which is lower-bound and upper-infinite and containing lower-closed intervals:

```>>> str(ic)
"[['a' .. 'd'), ['d' .. 'g'), ['g' .. 'z'), ['z' .. +inf)]"
```

By specifying additional parameters, you can determine which endpoints will be closed and whether a lower and / or upper infinite endpoint will be added:

```>>> ic = IntervalChain(('a', 'd', 'g', 'z'), lower_closed = False, add_lower_inf=True, add_upper_inf=False)
>>> str(ic)
"[(-inf .. 'a'], ('a' .. 'd'], ('d' .. 'g'], ('g' .. 'z']]"
```

### 2.1.2. Operations on interval chains¶

Interval chains can be indexed and iterated like lists ...:

```>>> ic
Interval(lower_limit=Limit(True, 'd', False), upper_limit=Limit(False, 'g', True))
>>> [ival.upper_limit.value for ival in ic]
['a', 'd', 'g', 'z']
```

... and can be searched for the index of the interval holding a specified value:

```>>> ic.map2idx('b')
1
>>> ic.map2idx('a')
0
>>> ic.map2idx('aa')
1
```

## 2.2. Classes¶

class `ivalutils.interval_chain.``IntervalChain`(limits, lower_closed=True, add_lower_inf=False, add_upper_inf=True)

An IntervalChain is a list of adjacent intervals.

It is constructed from a list of limiting values.

Parameters: limits (Iterable) – an iterable of the limiting values, must be ordered from smallest to greatest lower_closed (boolean) – defines which endpoint of the contained intervals will be closed: if True, lower endpoint closed, upper open (default), if False, lower endpoint open, upper closed add_lower_inf (boolean) – defines whether a lower infinite interval will be added as first interval: if True, infinite interval as lowest interval, if False, no infinite interval as lowest interval (default) add_upper_inf (boolean) – defines whether an upper infinite interval will be added as last interval: if True, infinite interval as last interval (default), if False, no infinite interval as last interval instance of `IntervalChain` `EmptyIntervalChain` – given limits do not define any interval `InvalidInterval` – given limits do not define a sequence of adjacent intervals `IncompatibleLimits` – given limits are not comparable
`__copy__`()

Return self (IntervalChain instances are immutable).

`__eq__`(other)

self == other

`__getitem__`(idx)

self[idx]

`__iter__`()

iter(self)

`__len__`()

len(self)

`__repr__`()

repr(self)

`__str__`()

str(self)

`is_lower_infinite`()

True if first interval is lower infinite.

`is_upper_infinite`()

True if last interval is upper infinite.

`map2idx`(value)

Return the index of the interval which contains value.

Raises ValueError if value is not contained in any of the intervals in self.

`limits`

The limiting values.

`total_interval`

Returns the interval between lower endpoint of first interval in self and upper endpoint of last interval in self.

## 2.3. Exceptions¶

class `ivalutils.interval_chain.``EmptyIntervalChain`

Raised when an empty IntervalChain would be created.

# 3. IntervalMapping¶

Mappings on intervals

## 3.1. Usage¶

### 3.1.1. Creating interval mappings¶

The class `IntervalMapping` is used to create a mapping from intervals to arbitrary values.

Instances can be created by giving an IntervalChain and a sequence of associated values ...:

```>>> im1 = IntervalMapping(IntervalChain((0, 300, 500, 1000)), (0., .10, .15, .20))
```

... or a sequence of limiting values and a sequence of associated values ...:

```>>> im2 = IntervalMapping((0, 300, 500, 1000), (0., .10, .15, .20))
```

... or a sequence of tuples, each holding a limiting value and an associated value:

```>>> im3 = IntervalMapping(((0, 0.), (300, .10), (500, .15), (1000, .20)))
>>> im1 == im2 == im3
True
```

### 3.1.2. Operations on IntervalMappings¶

Interval mappings behave like ordinary mappings:

```>>> list(im3.keys())
[Interval(lower_limit=Limit(True, 0, True), upper_limit=Limit(False, 300, False)),
Interval(lower_limit=Limit(True, 300, True), upper_limit=Limit(False, 500, False)),
Interval(lower_limit=Limit(True, 500, True), upper_limit=Limit(False, 1000, False)),
Interval(lower_limit=Limit(True, 1000, True))]
>>> list(im3.values())
[0.0, 0.1, 0.15, 0.2]
>>> im3[Interval(lower_limit=Limit(True, 300, True), upper_limit=Limit(False, 500, False))]
0.1
```

In addition they can be looked-up for the value associated with the interval which contains a given value:

```>>> im3.map(583)
0.15
```

As a short-cut, the interval mapping can be used like a function:

```>>> im3(412)
0.1
```

Use cases for interval mappings are for example:

• determine the discount to be applied depending on an order value,
• rating customers depending on their sales turnover,
• classifying cities based on the number of inhabitants,
• mapping booking dates to accounting periods,
• grouping of measured values in discrete ranges.

## 3.2. Classes¶

class `ivalutils.interval_map.``IntervalMapping`(*args)

An IntervalMapping is a container of associated interval / value pairs.

It is constructed from either
• an IntervalChain and a sequence of associated values,
• a sequence of limiting values and a sequence of associated values,
• a sequence of tuples, each holding a limiting value and an associated value.

1. Form

Parameters: arg0 (`IntervalChain`) – sequence of intervals to be mapped arg1 (Sequence) – sequence of associated values

2. Form

Parameters: arg0 (Sequence) – sequence of values limiting the intervals to be mapped arg1 (Sequence) – sequence of associated values

3. Form

Parameters: arg0 (Sequence) – sequence of tuples containing a limiting value and an associated value

If no IntervalChain is given, the given limiting values must be comparable and must be given in ascending order.

Returns: instance of `IntervalMapping` `AssertionError` – given sequences do not have the same length `AssertionError` – given sequences of limiting values is empty `InvalidInterval` – given limits do not define a sequence of adjacent intervals `IncompatibleLimits` – given limits are not comparable `TypeError` – given sequence is not a sequence of 2-tuples `TypeError` – wrong number of arguments
`__call__`(val)

Return the value associated with interval which contains val.

`__copy__`()

Return self (IntervalMapping instances are immutable).

`__eq__`(other)

self == other

`__getitem__`(key)

self[key]

`__iter__`()

iter(self)

`__len__`()

len(self)

`map`(val)

Return the value associated with interval which contains val.