{ "info": { "author": "Alexandre Decan", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Information Technology", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Scientific/Engineering :: Mathematics" ], "description": "# Python data structure and operations for intervals\n\n[![Travis](https://travis-ci.org/AlexandreDecan/python-intervals.svg?branch=master)](https://travis-ci.org/AlexandreDecan/python-intervals)\n[![Coverage Status](https://coveralls.io/repos/github/AlexandreDecan/python-intervals/badge.svg?branch=master)](https://coveralls.io/github/AlexandreDecan/python-intervals?branch=master)\n[![PyPI](https://badge.fury.io/py/python-intervals.svg)](https://pypi.org/project/python-intervals)\n\n\nThis library provides data structure and operations for intervals in Python 2.7+ and Python 3.4+.\n\n * [Features](#features)\n * [Installation](#installation)\n * [Documentation & usage](#documentation--usage)\n * [Interval creation](#interval-creation)\n * [Interval operations](#interval-operations)\n * [Comparison operators](#comparison-operators)\n * [Bounds of an interval](#bounds-of-an-interval)\n * [Interval transformation](#interval-transformation)\n * [Discrete iteration](#discrete-iteration)\n * [Map intervals to data](#map-intervals-to-data)\n * [Import & export intervals to strings](#import--export-intervals-to-strings)\n * [Import & export intervals to Python built-in data types](#import--export-intervals-to-python-built-in-data-types)\n * [Contributions](#contributions)\n * [Licence](#licence)\n * [Changelog](#changelog)\n\n\n## Features\n\n - Support intervals of any (comparable) objects.\n - Closed or open, finite or (semi-)infinite intervals.\n - Atomic intervals and interval sets are supported.\n - Automatic simplification of intervals.\n - Support comparison, transformation, intersection, union, complement, difference and containment.\n - Discrete iterations on the values of an interval.\n - Import and export intervals to strings and to Python built-in data types.\n - Dict-like structure to map intervals to data.\n\n\n## Installation\n\nYou can use `pip` to install it, as usual: `pip install python-intervals`.\n\nThis will install the latest available version from [PyPI](https://pypi.org/project/python-intervals).\nPrereleases are available from the *master* branch on [GitHub](https://github.com/AlexandreDecan/python-intervals).\n\nFor convenience, the library is contained within a single Python file, and can thus be easily integrated in other\nprojects without the need for an explicit dependency (hint: don't do that!).\n\n\n## Documentation & usage\n\n### Interval creation\n\nAssuming this library is imported using `import intervals as I`, intervals can be easily created using one of the\nfollowing helpers:\n\n```python\n>>> I.open(1, 2)\n(1,2)\n>>> I.closed(1, 2)\n[1,2]\n>>> I.openclosed(1, 2)\n(1,2]\n>>> I.closedopen(1, 2)\n[1,2)\n>>> I.singleton(1)\n[1]\n>>> I.empty()\n()\n\n```\n\nIntervals created with this library are `Interval` instances.\nAn `Interval` object is a disjunction of atomic intervals that represent single intervals (e.g. `[1,2]`) corresponding to `AtomicInterval` instances.\nExcept when atomic intervals are explicitly created or retrieved, only `Interval` instances are exposed.\n\nThe bounds of an interval can be any arbitrary values, as long as they are comparable:\n\n```python\n>>> I.closed(1.2, 2.4)\n[1.2,2.4]\n>>> I.closed('a', 'z')\n['a','z']\n>>> import datetime\n>>> I.closed(datetime.date(2011, 3, 15), datetime.date(2013, 10, 10))\n[datetime.date(2011, 3, 15),datetime.date(2013, 10, 10)]\n\n```\n\n\nInfinite and semi-infinite intervals are supported using `I.inf` and `-I.inf` as upper or lower bounds.\nThese two objects support comparison with any other object.\nWhen infinities are used as a lower or upper bound, the corresponding boundary is automatically converted to an open one.\n\n```python\n>>> I.inf > 'a', I.inf > 0, I.inf > True\n(True, True, True)\n>>> I.openclosed(-I.inf, 0)\n(-inf,0]\n>>> I.closed(-I.inf, I.inf) # Automatically converted to an open interval\n(-inf,+inf)\n\n```\n\nEmpty intervals always resolve to `(I.inf, -I.inf)`, regardless of the provided bounds:\n\n```python\n>>> I.empty() == I.open(I.inf, -I.inf)\nTrue\n>>> I.closed(4, 3) == I.open(I.inf, -I.inf)\nTrue\n>>> I.openclosed('a', 'a') == I.open(I.inf, -I.inf)\nTrue\n\n```\n\nFor convenience, intervals are automatically simplified:\n```python\n>>> I.closed(0, 2) | I.closed(2, 4)\n[0,4]\n>>> I.closed(1, 2) | I.closed(3, 4) | I.closed(2, 3)\n[1,4]\n>>> I.empty() | I.closed(0, 1)\n[0,1]\n>>> I.closed(1, 2) | I.closed(2, 3) | I.closed(4, 5)\n[1,3] | [4,5]\n\n```\n\nNote that discrete intervals are **not** supported, e.g., combining `[0,1]` with `[2,3]` will **not** result\nin `[0,3]` even if there is no integer between `1` and `2`.\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Interval operations\n\nBoth `Interval` and `AtomicInterval` support following interval operations:\n\n - `x.is_empty()` tests if the interval is empty.\n ```python\n >>> I.closed(0, 1).is_empty()\n False\n >>> I.closed(0, 0).is_empty()\n False\n >>> I.openclosed(0, 0).is_empty()\n True\n >>> I.empty().is_empty()\n True\n\n ```\n\n - `x.intersection(other)` or `x & other` return the intersection of two intervals.\n ```python\n >>> I.closed(0, 2) & I.closed(1, 3)\n [1,2]\n >>> I.closed(0, 4) & I.open(2, 3)\n (2,3)\n >>> I.closed(0, 2) & I.closed(2, 3)\n [2]\n >>> I.closed(0, 2) & I.closed(3, 4)\n ()\n\n ```\n\n - `x.union(other)` or `x | other` return the union of two intervals.\n ```python\n >>> I.closed(0, 1) | I.closed(1, 2)\n [0,2]\n >>> I.closed(0, 1) | I.closed(2, 3)\n [0,1] | [2,3]\n\n ```\n\n - `x.complement(other)` or `~x` return the complement of the interval.\n ```python\n >>> ~I.closed(0, 1)\n (-inf,0) | (1,+inf)\n >>> ~(I.open(-I.inf, 0) | I.open(1, I.inf))\n [0,1]\n >>> ~I.open(-I.inf, I.inf)\n ()\n\n ```\n\n - `x.difference(other)` or `x - other` return the difference between `x` and `other`.\n ```python\n >>> I.closed(0,2) - I.closed(1,2)\n [0,1)\n >>> I.closed(0, 4) - I.closed(1, 2)\n [0,1) | (2,4]\n\n ```\n\n - `x.contains(other)` or `other in x` return True if given item is contained in the interval.\n Support `Interval`, `AtomicInterval` and arbitrary comparable values.\n ```python\n >>> 2 in I.closed(0, 2)\n True\n >>> 2 in I.open(0, 2)\n False\n >>> I.open(0, 1) in I.closed(0, 2)\n True\n\n ```\n\n - `x.overlaps(other)` tests if there is an overlap between two intervals.\n This method accepts a `adjacent` parameter which defaults to `False`.\n If `True`, it accepts adjacent intervals as well (e.g., [1, 2) and [2, 3] but not\n [1, 2) and (2, 3]).\n ```python\n >>> I.closed(1, 2).overlaps(I.closed(2, 3))\n True\n >>> I.closed(1, 2).overlaps(I.open(2, 3))\n False\n >>> I.closed(1, 2).overlaps(I.openclosed(2, 3), adjacent=True)\n True\n >>> I.closedopen(1, 2).overlaps(I.openclosed(2, 3), adjacent=True)\n False\n\n ```\n\n\nThe following methods are only available for `Interval` instances:\n\n - `x.enclosure()` returns the smallest interval that includes the current one.\n ```python\n >>> (I.closed(0, 1) | I.closed(2, 3)).enclosure()\n [0,3]\n\n ```\n\n - `x.to_atomic()` is equivalent to `x.enclosure()` but returns an `AtomicInterval` instead of an `Interval` object.\n\n - `x.is_atomic()` evaluates to `True` if interval is composed of a single (possibly empty) atomic interval.\n ```python\n >>> I.closed(0, 2).is_atomic()\n True\n >>> (I.closed(0, 1) | I.closed(1, 2)).is_atomic()\n True\n >>> (I.closed(0, 1) | I.closed(2, 3)).is_atomic()\n False\n\n ```\n\n\nIntervals can also be iterated to access the underlying `AtomicInterval` objects, sorted by their lower and upper bounds.\n\n```python\n>>> list(I.open(2, 3) | I.closed(0, 1) | I.closed(21, 24))\n[[0,1], (2,3), [21,24]]\n\n```\n\nThe `AtomicInterval` objects of an `Interval` can also be accessed using their indexes:\n\n```python\n>>> (I.open(2, 3) | I.closed(0, 1) | I.closed(21, 24))[0]\n[0,1]\n>>> (I.open(2, 3) | I.closed(0, 1) | I.closed(21, 24))[-2]\n(2,3)\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Comparison operators\n\nEquality between intervals can be checked with the classical `==` operator:\n\n```python\n>>> I.closed(0, 2) == I.closed(0, 1) | I.closed(1, 2)\nTrue\n>>> I.closed(0, 2) == I.closed(0, 2).to_atomic()\nTrue\n\n```\n\nMoreover, both `Interval` and `AtomicInterval` are comparable using e.g. `>`, `>=`, `<` or `<=`.\nThese comparison operators have a different behaviour than the usual ones.\nFor instance, `a < b` holds if `a` is entirely on the left of the lower bound of `b` and `a > b` holds if `a` is entirely\non the right of the upper bound of `b`.\n\n```python\n>>> I.closed(0, 1) < I.closed(2, 3)\nTrue\n>>> I.closed(0, 1) < I.closed(1, 2)\nFalse\n\n```\n\nSimilarly, `a <= b` holds if `a` is entirely on the left of the upper bound of `b`, and `a >= b`\nholds if `a` is entirely on the right of the lower bound of `b`.\n\n```python\n>>> I.closed(0, 1) <= I.closed(2, 3)\nTrue\n>>> I.closed(0, 2) <= I.closed(1, 3)\nTrue\n>>> I.closed(0, 3) <= I.closed(1, 2)\nFalse\n\n```\n\nIntervals can also be compared with single values. If `i` is an interval and `x` a value, then\n`x < i` holds if `x` is on the left of the lower bound of `i` and `x <= i` holds if `x` is on the\nleft of the upper bound of `i`. This behaviour is similar to the one that could be obtained by first\nconverting `x` to a singleton interval.\n\n```python\n>>> 5 < I.closed(0, 10)\nFalse\n>>> 5 <= I.closed(0, 10)\nTrue\n>>> I.closed(0, 10) < 5\nFalse\n>>> I.closed(0, 10) <= 5\nTrue\n\n```\n\n\nNote that all these semantics differ from classical comparison operators.\nAs a consequence, some intervals are never comparable in the classical sense, as illustrated hereafter:\n\n```python\n>>> I.closed(0, 4) <= I.closed(1, 2) or I.closed(0, 4) >= I.closed(1, 2)\nFalse\n>>> I.closed(0, 4) < I.closed(1, 2) or I.closed(0, 4) > I.closed(1, 2)\nFalse\n>>> I.empty() < I.empty()\nTrue\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Bounds of an interval\n\nThe left and right boundaries, and the lower and upper bounds of an `AtomicInterval` can be respectively accessed\nwith its `left`, `right`, `lower` and `upper` attributes.\nThe `left` and `right` bounds are either `I.CLOSED` (`True`) or `I.OPEN` (`False`).\n\n```python\n>> I.CLOSED, I.OPEN\nTrue, False\n>>> x = I.closedopen(0, 1).to_atomic()\n>>> x.left, x.lower, x.upper, x.right\n(True, 0, 1, False)\n\n```\n\nSimilarly, the bounds of an `Interval` instance can be accessed with its `left`, `right`,\n`lower` and `upper` attributes. In that case, `left` and `lower` refer to the lower bound of its enclosure,\nwhile `right` and `upper` refer to the upper bound of its enclosure:\n\n```python\n>>> x = I.open(0, 1) | I.closed(3, 4)\n>>> x.left, x.lower, x.upper, x.right\n(False, 0, 4, True)\n\n```\n\nOne can easily check for some interval properties based on the bounds of an interval:\n\n```python\n>>> x = I.openclosed(-I.inf, 0)\n>>> # Check that interval is left/right closed\n>>> x.left == I.CLOSED, x.right == I.CLOSED\n(False, True)\n>>> # Check that interval is left/right bounded\n>>> x.lower == -I.inf, x.upper == I.inf\n(True, False)\n>>> # Check for singleton\n>>> x.lower == x.upper\nFalse\n\n```\n\n\nBoth `Interval` and `AtomicInterval` instances are immutable but provide a `replace` method that\ncan be used to create a new instance based on the current one. This method accepts four optional\nparameters `left`, `lower`, `upper`, and `right`:\n\n```python\n>>> i = I.closed(0, 2).to_atomic()\n>>> i.replace(I.OPEN, -1, 3, I.CLOSED)\n(-1,3]\n>>> i.replace(lower=1, right=I.OPEN)\n[1,2)\n\n```\n\nFunctions can be passed instead of values. If a function is passed, it is called with the current corresponding\nvalue except if the corresponding bound is an infinity and parameter `ignore_inf` if set to `False`.\n\n```python\n>>> I.closed(0, 2).replace(upper=lambda x: 2 * x)\n[0,4]\n>>> i = I.closedopen(0, I.inf)\n>>> i.replace(upper=lambda x: 10) # No change, infinity is ignored\n[0,+inf)\n>>> i.replace(upper=lambda x: 10, ignore_inf=False) # Infinity is not ignored\n[0,10)\n\n```\n\nWhen `replace` is applied on an `Interval` that is not atomic, it is extended and/or restricted such that\nits enclosure satisfies the new bounds.\n\n```python\n>>> i = I.openclosed(0, 1) | I.closed(5, 10)\n>>> i.replace(I.CLOSED, -1, 8, I.OPEN)\n[-1,1] | [5,8)\n>>> i.replace(lower=4)\n(4,10]\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Interval transformation\n\nTo apply an arbitrary transformation on an interval, `Interval` instances expose an `apply` method.\nThis method accepts a function that will be applied on each of the underlying atomic intervals to perform the desired transformation.\nThe function is expected to return an `AtomicInterval`, an `Interval` or a 4-uple `(left, lower, upper, right)`.\n\n```python\n>>> i = I.closed(2, 3) | I.open(4, 5)\n>>> # Increment bound values\n>>> i.apply(lambda x: (x.left, x.lower + 1, x.upper + 1, x.right))\n[3,4] | (5,6)\n>>> # Invert bounds\n>>> i.apply(lambda x: (not x.left, x.lower, x.upper, not x.right))\n(2,3) | [4,5]\n\n```\n\nThe `apply` method is very powerful when used in combination with `replace`.\nBecause the latter allows functions to be passed as parameters and can ignore infinities, it can be\nconveniently used to transform intervals in presence of infinities.\n\n```python\n>>> i = I.openclosed(-I.inf, 0) | I.closed(3, 4) | I.closedopen(8, I.inf)\n>>> # Increment bound values\n>>> i.apply(lambda x: x.replace(upper=lambda v: v + 1))\n(-inf,1] | [3,5] | [8,+inf)\n>>> # Intervals are still automatically simplified\n>>> i.apply(lambda x: x.replace(lower=lambda v: v * 2))\n(-inf,0] | [16,+inf)\n>>> # Invert bounds\n>>> i.apply(lambda x: x.replace(left=lambda v: not v, right=lambda v: not v))\n(-inf,0) | (3,4) | (8,+inf)\n>>> # Replace infinities with -10 and 10\n>>> conv = lambda v: -10 if v == -I.inf else (10 if v == I.inf else v)\n>>> i.apply(lambda x: x.replace(lower=conv, upper=conv, ignore_inf=False))\n(-10,0] | [3,4] | [8,10)\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Discrete iteration\n\nThe `iterate` function takes an interval or atomic interval, and returns a generator to iterate over\nthe values of an interval. Obviously, as intervals are continuous, it is required to specify the increment\n `incr` between consecutive values. The iteration then starts from the lower bound and ends on the upper one,\ngiven they are not excluded by the interval:\n\n```python\n>>> list(I.iterate(I.closed(0, 3), incr=1))\n[0, 1, 2, 3]\n>>> list(I.iterate(I.closed(0, 3), incr=2))\n[0, 2]\n>>> list(I.iterate(I.open(0, 3), incr=2))\n[2]\n\n```\n\nWhen an interval represents an union of atomic intervals, `iterate` consecutively iterates on all atomic\nintervals, starting from each lower bound and ending on each upper one:\n\n```python\n>>> list(I.iterate(I.singleton(0) | I.singleton(1) | I.singleton(5), incr=2)) # Won't be [0]\n[0, 1, 5]\n>>> list(I.iterate(I.closed(0, 2) | I.closed(5, 6), incr=3)) # Won't be [0, 6]\n[0, 5]\n\n```\n\nIteration can be performed in reverse order by specifying `reverse=True`. In that case, `incr` will be\nsubtracted instead of being added, implying that `incr` must always be a \"positive\" value:\n\n```python\n>>> list(I.iterate(I.closed(0, 3), incr=1, reverse=True)) # Not incr=-1\n[3, 2, 1, 0]\n>>> list(I.iterate(I.closed(0, 3), incr=2, reverse=True)) # Not incr=-2\n[3, 1]\n\n```\n\nAgain, this library does not make any assumption about the objects being used in an interval, as long as they\nare comparable. However, it is not always possible to provide a meaningful value for `incr` (e.g., what would\nbe the step between two consecutive characters?). In these cases, a callable can be passed instead of a value.\nThis callable will be called with the current value, and is expected to return the next possible value.\n\n```python\n>>> list(I.iterate(I.closed('a', 'd'), incr=lambda d: chr(ord(d) + 1)))\n['a', 'b', 'c', 'd']\n>>> # Notice the reversed order, mind the \"- 1\"\n>>> list(I.iterate(I.closed('a', 'd'), incr=lambda d: chr(ord(d) - 1), reverse=True))\n['d', 'c', 'b', 'a']\n\n```\n\nBy default, the iteration always starts on the lower bound (unless `reverse=True`) of each atomic interval.\nThe `base` parameter can be used to change this behaviour, by specifying how the initial value to start\nthe iteration on must be computed. This parameter accepts a callable that will be called with the lower\nbound (unless `reverse=True`) for each underlying atomic interval, and that must return the first value to\nconsider instead of the lower bound.\n\nThis can be helpful to deal with (semi-)infinite intervals, or to *align* the generated values of\nthe iterator:\n\n```python\n>>> # Restrict values of a (semi-)infinite interval\n>>> list(I.iterate(I.openclosed(-I.inf, 2), incr=1, base=lambda x: max(0, x)))\n[0, 1, 2]\n>>> # Align on integers\n>>> list(I.iterate(I.closed(0.3, 4.9), incr=1, base=int))\n[1, 2, 3, 4]\n\n```\n\nThe `base` parameter can be used to change how `iterate` applies on unions of atomic interval, by\nspecifying a function that returns a single value, as illustrated next:\n\n```python\n>>> interval = I.closed(0, 1) | I.closed(2, 4) | I.closed(5, 6)\n>>> list(I.iterate(interval, incr=3)) # Won't be [0, 3, 6]\n[0, 2, 5]\n>>> list(I.iterate(interval, incr=3, base=lambda x: 0))\n[0, 3, 6]\n\n```\n\nNotice that this approach can be extremely inefficient in terms of performance when the intervals\nare \"far apart\" each other.\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Map intervals to data\n\nThe library provides an `IntervalDict` class, a `dict`-like data structure to store and query data\nalong with intervals. Any value can be stored in such data structure as long as it supports\nequality.\n\n\n```python\n>>> d = I.IntervalDict()\n>>> d[I.closed(0, 3)] = 'banana'\n>>> d[4] = 'apple'\n>>> d\n{[0,3]: 'banana', [4]: 'apple'}\n\n```\n\nWhen a value is defined for an interval that overlaps an existing one, it is automatically updated\nto take the new value into account:\n\n```python\n>>> d[I.closed(2, 4)] = 'orange'\n>>> d\n{[0,2): 'banana', [2,4]: 'orange'}\n\n```\n\nAn `IntervalDict` can be queried using single values or intervals. If a single value is used as a\nkey, its behaviour corresponds to the one of a classical `dict`:\n\n```python\n>>> d[2]\n'orange'\n>>> d[5] # Key does not exist\nTraceback (most recent call last):\n ...\nKeyError: 5\n>>> d.get(5, default=0)\n0\n\n```\n\nWhen an interval is used as a key, a new `IntervalDict` containing the values\nfor that interval is returned:\n\n```python\n>>> d[~I.empty()] # Get all values, similar to d.copy()\n{[0,2): 'banana', [2,4]: 'orange'}\n>>> d[I.closed(1, 3)]\n{[1,2): 'banana', [2,3]: 'orange'}\n>>> d[I.closed(-2, 1)]\n{[0,1]: 'banana'}\n>>> d[I.closed(-2, -1)]\n{}\n\n```\n\nBy using `.get`, a default value (defaulting to `None`) can be specified.\nThis value is used to \"fill the gaps\" if the queried interval is not completely\ncovered by the `IntervalDict`:\n\n```python\n>>> d.get(I.closed(-2, 1), default='peach')\n{[-2,0): 'peach', [0,1]: 'banana'}\n>>> d.get(I.closed(-2, -1), default='peach')\n{[-2,-1]: 'peach'}\n>>> d.get(I.singleton(1), default='peach') # Key is covered, default is not used\n{[1]: 'banana'}\n\n```\n\nFor convenience, an `IntervalDict` provides a way to look for specific data values.\nThe `.find` method always returns a (possibly empty) `Interval` instance for which given\nvalue is defined:\n\n```python\n>>> d.find('banana')\n[0,2)\n>>> d.find('orange')\n[2,4]\n>>> d.find('carrot')\n()\n\n```\n\nThe active domain of an `IntervalDict` can be retrieved with its `.domain` method.\nThis method always returns a single `Interval` instance, where `.keys` returns a list\nof disjoint intervals, one for each stored value.\n\n```python\n>>> d.domain()\n[0,4]\n>>> d.keys()\n[[0,2), [2,4]]\n>>> d.values()\n['banana', 'orange']\n>>> d.items()\n[([0,2), 'banana'), ([2,4], 'orange')]\n\n```\n\nTwo `IntervalDict` instances can be combined together using the `.combine` method.\nThis method returns a new `IntervalDict` whose keys and values are taken from the two\nsource `IntervalDict`. Values corresponding to non-intersecting keys are simply copied,\nwhile values corresponding to intersecting keys are combined together using the provided\nfunction, as illustrated hereafter:\n\n```python\n>>> d1 = I.IntervalDict({I.closed(0, 2): 'banana'})\n>>> d2 = I.IntervalDict({I.closed(1, 3): 'orange'})\n>>> concat = lambda x, y: x + '/' + y\n>>> d1.combine(d2, how=concat)\n{[0,1): 'banana', [1,2]: 'banana/orange', (2,3]: 'orange'}\n\n```\n\nResulting keys always correspond to an outer join. Other joins can be easily simulated\nby querying the resulting `IntervalDict` as follows:\n\n```python\n>>> d = d1.combine(d2, how=concat)\n>>> d[d1.domain()] # Left join\n{[0,1): 'banana', [1,2]: 'banana/orange'}\n>>> d[d2.domain()] # Right join\n{[1,2]: 'banana/orange', (2,3]: 'orange'}\n>>> d[d1.domain() & d2.domain()] # Inner join\n{[1,2]: 'banana/orange'}\n\n```\n\nFinally, similarly to a `dict`, an `IntervalDict` also supports `len`, `in` and `del`, and defines\n`.clear`, `.copy`, `.update`, `.pop`, `.popitem`, and `.setdefault`.\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Import & export intervals to strings\n\nIntervals can be exported to string, either using `repr` (as illustrated above) or with the `to_string` function.\n\n```python\n>>> I.to_string(I.closedopen(0, 1))\n'[0,1)'\n\n```\n\nThis function accepts both `Interval` and `AtomicInterval` instances.\nThe way string representations are built can be easily parametrized using the various parameters supported by\n`to_string`:\n\n```python\n>>> params = {\n... 'disj': ' or ',\n... 'sep': ' - ',\n... 'left_closed': '<',\n... 'right_closed': '>',\n... 'left_open': '..',\n... 'right_open': '..',\n... 'pinf': '+oo',\n... 'ninf': '-oo',\n... 'conv': lambda v: '\"{}\"'.format(v),\n... }\n>>> x = I.openclosed(0, 1) | I.closed(2, I.inf)\n>>> I.to_string(x, **params)\n'..\"0\" - \"1\"> or <\"2\" - +oo..'\n\n```\n\nSimilarly, intervals can be created from a string using the `from_string` function.\nA conversion function (`conv` parameter) has to be provided to convert a bound (as string) to a value.\n\n```python\n>>> I.from_string('[0, 1]', conv=int) == I.closed(0, 1)\nTrue\n>>> I.from_string('[1.2]', conv=float) == I.singleton(1.2)\nTrue\n>>> converter = lambda s: datetime.datetime.strptime(s, '%Y/%m/%d')\n>>> I.from_string('[2011/03/15, 2013/10/10]', conv=converter)\n[datetime.datetime(2011, 3, 15, 0, 0),datetime.datetime(2013, 10, 10, 0, 0)]\n\n```\n\nSimilarly to `to_string`, function `from_string` can be parametrized to deal with more elaborated inputs.\nNotice that as `from_string` expects regular expression patterns, we need to escape some characters.\n\n```python\n>>> s = '..\"0\" - \"1\"> or <\"2\" - +oo..'\n>>> params = {\n... 'disj': ' or ',\n... 'sep': ' - ',\n... 'left_closed': '<',\n... 'right_closed': '>',\n... 'left_open': r'\\.\\.', # from_string expects regular expression patterns\n... 'right_open': r'\\.\\.', # from_string expects regular expression patterns\n... 'pinf': r'\\+oo', # from_string expects regular expression patterns\n... 'ninf': '-oo',\n... 'conv': lambda v: int(v[1:-1]),\n... }\n>>> I.from_string(s, **params)\n(0,1] | [2,+inf)\n\n```\n\nWhen a bound contains a comma or has a representation that cannot be automatically parsed with `from_string`,\nthe `bound` parameter can be used to specify the regular expression that should be used to match its representation.\n\n```python\n>>> s = '[(0, 1), (2, 3)]' # Bounds are expected to be tuples\n>>> I.from_string(s, conv=eval, bound=r'\\(.+?\\)')\n[(0, 1),(2, 3)]\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n### Import & export intervals to Python built-in data types\n\nIntervals can also be exported to a list of 4-uples with `to_data`, e.g., to support JSON serialization.\n\n```python\n>>> x = I.openclosed(0, 1) | I.closedopen(2, I.inf)\n>>> I.to_data(x)\n[(False, 0, 1, True), (True, 2, inf, False)]\n\n```\n\nThe function to convert bounds can be specified with the `conv` parameter.\nThe values that must be used to represent positive and negative infinities can be specified with\n`pinf` and `ninf`. They default to `float('inf')` and `float('-inf')` respectively.\n\n```python\n>>> x = I.closed(datetime.date(2011, 3, 15), datetime.date(2013, 10, 10))\n>>> I.to_data(x, conv=lambda v: (v.year, v.month, v.day))\n[(True, (2011, 3, 15), (2013, 10, 10), True)]\n\n```\n\nIntervals can be imported from such a list of 4-uples with `from_data`.\nThe same set of parameters can be used to specify how bounds and infinities are converted.\n\n```python\n>>> x = [(True, (2011, 3, 15), (2013, 10, 10), False)]\n>>> I.from_data(x, conv=lambda v: datetime.date(*v))\n[datetime.date(2011, 3, 15),datetime.date(2013, 10, 10))\n\n```\n\n\n[↑ back to top](#python-data-structure-and-operations-for-intervals)\n## Contributions\n\nContributions are very welcome!\nFeel free to report bugs or suggest new features using GitHub issues and/or pull requests.\n\n\n## Licence\n\nDistributed under [LGPLv3 - GNU Lesser General Public License, version 3](https://github.com/AlexandreDecan/python-intervals/blob/master/LICENSE.txt).\n\nYou can cite this library using:\n\n```\n@software{python-intervals,\n author = {Decan, Alexandre},\n title = {python-intervals: Python data structure and operations for intervals},\n url = {https://github.com/AlexandreDecan/python-intervals},\n}\n```\n\n\n## Changelog\n\nThis library adheres to a [semantic versioning](https://semver.org) scheme.\n\n\n**1.10.0** (2019-09-26)\n\n - `IntervalDict` has a `.combine` method to merge its keys and values with another `IntervalDict`.\n\n\n**1.9.0** (2019-09-13)\n\n - Discrete iteration on the values of an interval with `iterate`.\n - Map intervals to data with the dict-like `IntervalDict` structure.\n - Faster comparisons between arbitrary values and intervals.\n - Deprecate `permissive` in `.overlaps` in favour of `adjacent`.\n - Fix `.union` when intervals share a bound, one inclusive and one exclusive ([#12](https://github.com/AlexandreDecan/python-intervals/issues/12)).\n - Fix `.overlaps` when intervals share a lower bound, and one interval is contained within the other one ([#13](https://github.com/AlexandreDecan/python-intervals/issues/13)).\n\n\n**1.8.0** (2018-12-15)\n\n - Intervals have a `.left`, `.lower`, `.upper`, and `.right` attribute that refer to its enclosure.\n - Intervals have a `.replace` method to create new intervals based on the current one. This method accepts both values and functions.\n - Intervals have an `.apply` method to apply a function on the underlying atomic intervals.\n - Intervals can be compared with single values as well.\n - `I.empty()` returns the same instance to save memory.\n - Infinities are singleton objects.\n - Set `len(I.empty()) = 1` and `I.empty()[0] == I.empty().to_atomic()` for consistency.\n\n\n**1.7.0** (2018-12-06)\n\n - Import from and export to Python built-in data types (a list of 4-uples) with `from_data` and `to_data` ([#6](https://github.com/AlexandreDecan/python-intervals/issues/6)).\n - Add examples for arbitrary interval transformations.\n\n\n**1.6.0** (2018-08-29)\n\n - Add support for customized infinity representation in `to_string` and `from_string` ([#3](https://github.com/AlexandreDecan/python-intervals/issues/3)).\n\n\n**1.5.4** (2018-07-29)\n\n - Fix `.overlaps` ([#2](https://github.com/AlexandreDecan/python-intervals/issues/2)).\n\n\n**1.5.3** (2018-06-21)\n\n - Fix invalid `repr` for atomic singleton intervals.\n\n\n**1.5.2** (2018-06-15)\n\n - Fix invalid comparisons when both `Interval` and `AtomicInterval` are compared.\n\n\n**1.5.1** (2018-04-25)\n\n - Fix [#1](https://github.com/AlexandreDecan/python-intervals/issues/1) by making empty intervals always resolving to `(I.inf, -I.inf)`.\n\n\n**1.5.0** (2018-04-17)\n\n - `Interval.__init__` accepts `Interval` instances in addition to `AtomicInterval` ones.\n\n\n**1.4.0** (2018-04-17)\n\n - Function `I.to_string` to export an interval to a string, with many options to customize the representation.\n - Function `I.from_string` to create an interval from a string, with many options to customize the parsing.\n\n\n**1.3.2** (2018-04-13)\n\n - Support for Python 2.7.\n\n\n**1.3.1** (2018-04-12)\n\n - Define `__slots__` to lower memory usage, and to speed up attribute access.\n - Define `Interval.__rand__` (and other magic methods) to support `Interval` from `AtomicInterval` instead of\n having a dedicated piece of code in `AtomicInterval`.\n - Fix `__all__`.\n - More tests to cover all comparisons.\n\n\n**1.3.0** (2018-04-04)\n\n - Meaningful `<=` and `>=` comparisons for intervals.\n\n\n**1.2.0** (2018-04-04)\n\n - `Interval` supports indexing to retrieve the underlying `AtomicInterval` objects.\n\n\n**1.1.0** (2018-04-04)\n\n - Both `AtomicInterval` and `Interval` are fully comparable.\n - Add `singleton(x)` to create a singleton interval [x].\n - Add `empty()` to create an empty interval.\n - Add `Interval.enclosure()` that returns the smallest interval that includes the current one.\n - Interval simplification is in O(n) instead of O(n*m).\n - `AtomicInterval` objects in an `Interval` are sorted by lower and upper bounds.\n\n\n**1.0.4** (2018-04-03)\n\n - All operations of `AtomicInterval` (except overlaps) accept `Interval`.\n - Raise `TypeError` instead of `ValueError` if type is not supported (coherent with `NotImplemented`).\n\n\n**1.0.3** (2018-04-03)\n\n - Initial working release on PyPi.\n\n\n**1.0.0** (2018-04-03)\n\n - Initial release.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/AlexandreDecan/python-intervals", "keywords": "interval operation range math", "license": "LGPL3", "maintainer": "", "maintainer_email": "", "name": "python-intervals", "package_url": "https://pypi.org/project/python-intervals/", "platform": "", "project_url": "https://pypi.org/project/python-intervals/", "project_urls": { "Homepage": "https://github.com/AlexandreDecan/python-intervals" }, "release_url": "https://pypi.org/project/python-intervals/1.10.0/", "requires_dist": null, "requires_python": "", "summary": "Python data structure and operations for intervals", "version": "1.10.0" }, "last_serial": 5889080, "releases": { "1.0.2": [ { "comment_text": "", "digests": { "md5": "fb0c5e32b1163bfd7730a9ba550a1637", "sha256": "22f84562c27df982e0c098506b01bed310dcad7fb08c86c700dc321df0a43c8b" }, "downloads": -1, "filename": "python_intervals-1.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "fb0c5e32b1163bfd7730a9ba550a1637", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 6471, "upload_time": "2018-04-03T19:04:00", "url": "https://files.pythonhosted.org/packages/cc/73/8061be44278251a8c76f7e5dddbc791ed5806c9daa393db1d60e4da2f1eb/python_intervals-1.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aac2e378808e6705035a03bac5421279", "sha256": "a0159478643e1035a1e65082e5feca8cf408b8754c3515cf3a150a1b465991cd" }, "downloads": -1, "filename": "python-intervals-1.0.2.tar.gz", "has_sig": false, "md5_digest": "aac2e378808e6705035a03bac5421279", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 10314, "upload_time": "2018-04-03T19:04:01", "url": "https://files.pythonhosted.org/packages/26/b3/46216da4c6c3b7483bac53ab018852f2f64303b7781dabf049f555e29cb6/python-intervals-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "3cafb679576502347c8939d86416966c", "sha256": "2b590742c4b52408f6641f9474e96f2971982befce5e954d7bf62269e96d0335" }, "downloads": -1, "filename": "python_intervals-1.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "3cafb679576502347c8939d86416966c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 6479, "upload_time": "2018-04-03T19:10:25", "url": "https://files.pythonhosted.org/packages/02/e1/8c1d71578ceb8cb09206e665fa7a59e81613adb048b761184434fc25092c/python_intervals-1.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a7566b39d0f4ed64d6cd4b3b8729b174", "sha256": "98b0dc8376fdc75b746414017677de5b40347b323b40353b17ef760b8c328c1c" }, "downloads": -1, "filename": "python-intervals-1.0.3.tar.gz", "has_sig": false, "md5_digest": "a7566b39d0f4ed64d6cd4b3b8729b174", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 10330, "upload_time": "2018-04-03T19:10:26", "url": "https://files.pythonhosted.org/packages/b7/57/862fb20b39e447bffd87be01d73f31f72ca167651894ae37c8f794646464/python-intervals-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "339e882703a9c8cf249d81090fdab6c3", "sha256": "3c7e4cbdd2d4a45b172605cb0ca453f86d5b83d0081c0d5b0840b57de5b3476e" }, "downloads": -1, "filename": "python_intervals-1.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "339e882703a9c8cf249d81090fdab6c3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 6648, "upload_time": "2018-04-03T20:03:43", "url": "https://files.pythonhosted.org/packages/7b/cc/ee07e5aac48757b692880d2b36591bce3454ed16b0a5c801f54eb5687afe/python_intervals-1.0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "67f8ba4ef19b7ceeb729438cc9f13a66", "sha256": "9a8dc9ee3685a4fede4483910530f500b66d325dfcccefba9413f558f94a2c22" }, "downloads": -1, "filename": "python-intervals-1.0.4.tar.gz", "has_sig": false, "md5_digest": "67f8ba4ef19b7ceeb729438cc9f13a66", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 10432, "upload_time": "2018-04-03T20:03:44", "url": "https://files.pythonhosted.org/packages/29/d9/ed916c53e787725eeafd3451fc593555522d9b8205239a44bf61775cceb3/python-intervals-1.0.4.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "0826202814785d236887a66aee234498", "sha256": "d28905afbe05b2ca9686051450a39064168f611c817aff615c8d6af670bb58f8" }, "downloads": -1, "filename": "python_intervals-1.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "0826202814785d236887a66aee234498", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 6965, "upload_time": "2018-04-04T11:34:03", "url": "https://files.pythonhosted.org/packages/9d/98/d78713703fe7db791d64086b191fd505920351796b3b2c54c0f039917784/python_intervals-1.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fd98f79cab0db9f86c4211b43ef2e1a9", "sha256": "3cd57bbdca286b49fd881c72707ad669cad797d9b610350bcbc537e97926c28b" }, "downloads": -1, "filename": "python-intervals-1.1.0.tar.gz", "has_sig": false, "md5_digest": "fd98f79cab0db9f86c4211b43ef2e1a9", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 11079, "upload_time": "2018-04-04T11:34:04", "url": "https://files.pythonhosted.org/packages/92/75/e761f99e9616a740e296531cf7c2ef416de21f332f5318147bdb9e984540/python-intervals-1.1.0.tar.gz" } ], "1.10.0": [ { "comment_text": "", "digests": { "md5": "376cbf3b7ef083404989a3e32e6a4ee3", "sha256": "6d310579752f8e569cd79fd963f7593af494dbefb80368a15bfe2c24ae0a4383" }, "downloads": -1, "filename": "python_intervals-1.10.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "376cbf3b7ef083404989a3e32e6a4ee3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21805, "upload_time": "2019-09-26T07:44:21", "url": "https://files.pythonhosted.org/packages/7f/1c/bfe1e8460ba1d02051b1dc98ed2609c2f91b55632266f13ac0fae0cf9cec/python_intervals-1.10.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8955317ff4e42590c90ba6247b1caaed", "sha256": "0d26746eaed0be78a61dd289bb7a10721b08770bb3e807614835f490d514f2a5" }, "downloads": -1, "filename": "python-intervals-1.10.0.tar.gz", "has_sig": false, "md5_digest": "8955317ff4e42590c90ba6247b1caaed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 40116, "upload_time": "2019-09-26T07:44:24", "url": "https://files.pythonhosted.org/packages/ae/c4/413cd29bfbe8337eeb2c968194f53cbc1424cc48ad60885e1300ef542598/python-intervals-1.10.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "662179d3f7282a6a736db196bfcb7068", "sha256": "918280bc5abe5f6c208edebeaf8cb239f5817f6cc8f414646d4596bb8afef874" }, "downloads": -1, "filename": "python_intervals-1.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "662179d3f7282a6a736db196bfcb7068", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 7294, "upload_time": "2018-04-04T12:11:42", "url": "https://files.pythonhosted.org/packages/9d/b4/70b07f54ed7bece26e58fab5ae164effab3da058a0e05a5328e5b2002d2f/python_intervals-1.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5ba9bf98c0faae223c315386d8a11c8d", "sha256": "8d6a94c248b201e77db78ee5f6f4386bc5e0cef7949823c4f225fbd9f5607d7a" }, "downloads": -1, "filename": "python-intervals-1.2.0.tar.gz", "has_sig": false, "md5_digest": "5ba9bf98c0faae223c315386d8a11c8d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 11539, "upload_time": "2018-04-04T12:11:43", "url": "https://files.pythonhosted.org/packages/0b/f1/2e7f32cfd5aea247af5b7e547981b88cb92ab840856d9295c2153c4408ca/python-intervals-1.2.0.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "efd58796f64b4b1068360a42f091d7a8", "sha256": "17e6e7433deee46dab6178896bf0098e44e85dad068e015e5b84ba9f80c354e5" }, "downloads": -1, "filename": "python_intervals-1.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "efd58796f64b4b1068360a42f091d7a8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 7708, "upload_time": "2018-04-04T20:58:30", "url": "https://files.pythonhosted.org/packages/09/19/ae9b1fbb02d4f5461d718a5ccfee1c705486ffb1c5dd1b5624e010187a64/python_intervals-1.3.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "11ea2a64d94bf8cfde0ddc7d4c2c6603", "sha256": "b8bad24e755647da0f555dd29b1ffcded839e1c7f555734e9cdbf5014a635d1c" }, "downloads": -1, "filename": "python-intervals-1.3.0.tar.gz", "has_sig": false, "md5_digest": "11ea2a64d94bf8cfde0ddc7d4c2c6603", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 13358, "upload_time": "2018-04-04T20:58:31", "url": "https://files.pythonhosted.org/packages/2f/65/a1f6a725c7b1a0bd944c7ddc941932cd61c6bb5ced94f72957617a5271d1/python-intervals-1.3.0.tar.gz" } ], "1.3.1": [ { "comment_text": "", "digests": { "md5": "c0fa75c797505cefe974ee944aaa4a8a", "sha256": "5d91eebff70f8bda14d279a79c2e052927851b7fb2860919c5497abd913f9f87" }, "downloads": -1, "filename": "python_intervals-1.3.1-py3-none-any.whl", "has_sig": false, "md5_digest": "c0fa75c797505cefe974ee944aaa4a8a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.4", "size": 8494, "upload_time": "2018-04-12T08:55:05", "url": "https://files.pythonhosted.org/packages/2d/13/2222621f26054b99c40ee2a90b95066a7a3647fff98e1f9c266fec396bf8/python_intervals-1.3.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2294aa15a6b97836bd69c1f39353e70c", "sha256": "3cb0b3273e9ec1027b184c5b8407bdc644144ff9567a84c27f2c590596c74bb8" }, "downloads": -1, "filename": "python-intervals-1.3.1.tar.gz", "has_sig": false, "md5_digest": "2294aa15a6b97836bd69c1f39353e70c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 14433, "upload_time": "2018-04-12T08:55:06", "url": "https://files.pythonhosted.org/packages/ef/fd/5d57e72e5879a11bf804f78e62b2fa86a4f5433fdc261d7fb4800e62ac3e/python-intervals-1.3.1.tar.gz" } ], "1.3.2": [ { "comment_text": "", "digests": { "md5": "0107bec85628a122489008b9712ce0e5", "sha256": "248cf0ceb606586a29b8a4b921ad4e39a38a18d42aac459fcd1491a831284cba" }, "downloads": -1, "filename": "python_intervals-1.3.2-py3-none-any.whl", "has_sig": false, "md5_digest": "0107bec85628a122489008b9712ce0e5", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 8559, "upload_time": "2018-04-13T09:34:44", "url": "https://files.pythonhosted.org/packages/b2/09/a4d6959e356be5287bca7ab3519cfd1b2d007ec5621de1b61e28b09d8edf/python_intervals-1.3.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "893c1c0ced99a2be5090be95130ba299", "sha256": "42a1cc596688af23e91bc9f9f199d27e9be404dd6f88064fa588e94f3a320f58" }, "downloads": -1, "filename": "python-intervals-1.3.2.tar.gz", "has_sig": false, "md5_digest": "893c1c0ced99a2be5090be95130ba299", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14484, "upload_time": "2018-04-13T09:34:45", "url": "https://files.pythonhosted.org/packages/8b/17/c1094cf5457ebd208ca491c2fd383b8aff6ad9bf006286cd258bf4baa6b2/python-intervals-1.3.2.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "27634bc9e6cc13d494b18f5317db6388", "sha256": "fe1affbc36bd1a354be347ccbe7f3707692f5335971cfeaa88bcb35e29dec1b6" }, "downloads": -1, "filename": "python_intervals-1.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "27634bc9e6cc13d494b18f5317db6388", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10153, "upload_time": "2018-04-17T07:30:24", "url": "https://files.pythonhosted.org/packages/6c/a1/2258ebf8dd46ec91ed55969f96696598eb66619b62e576341ea78dbda57d/python_intervals-1.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8c4b4c08e6b578235281c47b399f3192", "sha256": "fb3985c730a8c33e54002592a1e5d09621d4383d40778b79f9e1d9992d3f8cf3" }, "downloads": -1, "filename": "python-intervals-1.4.0.tar.gz", "has_sig": false, "md5_digest": "8c4b4c08e6b578235281c47b399f3192", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19451, "upload_time": "2018-04-17T07:30:25", "url": "https://files.pythonhosted.org/packages/b8/96/f27d64dab0f9f04cd4be7618fb0a807538dd23b95de15d3f080ced09315b/python-intervals-1.4.0.tar.gz" } ], "1.5.0": [ { "comment_text": "", "digests": { "md5": "61526a005f879d6436533ff6ac0dc3f3", "sha256": "0d154f38af3aa4985db5bf1a610ec7dd17d3ec4eded135a91823efb8076b5d77" }, "downloads": -1, "filename": "python_intervals-1.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "61526a005f879d6436533ff6ac0dc3f3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10234, "upload_time": "2018-04-17T13:10:09", "url": "https://files.pythonhosted.org/packages/3f/89/7fdc6e082d776655ecc941079cedb10f35ed9bc9f6d2ae10238e1a95f00c/python_intervals-1.5.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "03638e1fcb6bddcae6b29c0582f363d6", "sha256": "e9fd8745f6dcf583fa64db10df1166ed6f4e8fe675d2a8e2cdad3711869f47d2" }, "downloads": -1, "filename": "python-intervals-1.5.0.tar.gz", "has_sig": false, "md5_digest": "03638e1fcb6bddcae6b29c0582f363d6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19681, "upload_time": "2018-04-17T13:10:10", "url": "https://files.pythonhosted.org/packages/86/65/d9f1d06b0f07da83c4c3c7e72a6002dba0a84b8f74953979c73e81208cfe/python-intervals-1.5.0.tar.gz" } ], "1.5.1": [ { "comment_text": "", "digests": { "md5": "81c187d511687c1b7bd0ddf4134ebae0", "sha256": "87a421e017038764bccca9e0a16318d9fc4d542d2d99400ed3a135b39fbb1041" }, "downloads": -1, "filename": "python_intervals-1.5.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "81c187d511687c1b7bd0ddf4134ebae0", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10309, "upload_time": "2018-04-25T07:02:22", "url": "https://files.pythonhosted.org/packages/75/56/f0ba157847299f3b5225269e2e59c2f53b1c651d51020f45b51785cc8ebc/python_intervals-1.5.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "24facd50ee4862ad4ec913effcfd2119", "sha256": "6d56c29647aad135950abe972275a7f589cd4f49643eba7f595220316cd7e26d" }, "downloads": -1, "filename": "python-intervals-1.5.1.tar.gz", "has_sig": false, "md5_digest": "24facd50ee4862ad4ec913effcfd2119", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20105, "upload_time": "2018-04-25T07:02:23", "url": "https://files.pythonhosted.org/packages/00/fa/cbc8bcb6a7dc10eb77ef16318e0ba2a60ec3d1227d3f46daa5dd8c9bf8db/python-intervals-1.5.1.tar.gz" } ], "1.5.2": [ { "comment_text": "", "digests": { "md5": "edb8fd931638c561935f1e4a4ad592d2", "sha256": "84dfb2d2436d6728bf3d3ab5fb2ad979fb3893f06207eab82b6ffa8828462558" }, "downloads": -1, "filename": "python_intervals-1.5.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "edb8fd931638c561935f1e4a4ad592d2", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10357, "upload_time": "2018-06-15T12:31:26", "url": "https://files.pythonhosted.org/packages/28/8a/6d3c93e06df25aca2b315681eef67831f3e407a3edb5bc809a6257316d40/python_intervals-1.5.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bc237cc38025bb52822b4e81b93fa0aa", "sha256": "52bca29dbf397a15a9445e13a01033a2181ac4d167aa3e9b7e569f89b693cbbb" }, "downloads": -1, "filename": "python-intervals-1.5.2.tar.gz", "has_sig": false, "md5_digest": "bc237cc38025bb52822b4e81b93fa0aa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20197, "upload_time": "2018-06-15T12:31:27", "url": "https://files.pythonhosted.org/packages/7b/fd/70eeada567b45cceab472fcc91e04e041e5479f718cb301c8d41f5504b43/python-intervals-1.5.2.tar.gz" } ], "1.5.3": [ { "comment_text": "", "digests": { "md5": "422d44267f8b6469ad6bfd5309c6ac6a", "sha256": "cde9f42af7b1a593b5a78c6be8c5ebab01e6d9a9631e823c2657be7d56a5147e" }, "downloads": -1, "filename": "python_intervals-1.5.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "422d44267f8b6469ad6bfd5309c6ac6a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10372, "upload_time": "2018-06-21T11:23:03", "url": "https://files.pythonhosted.org/packages/b6/29/08c8c0912aebd2868568647e17673c23fb8624bf67b8826100d665e6758a/python_intervals-1.5.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4ee2d11d235cafdc3af89b0759183cc7", "sha256": "69b18b0e7682509273cde034f7d7c84f02e1aac1873622c296764033222b0080" }, "downloads": -1, "filename": "python-intervals-1.5.3.tar.gz", "has_sig": false, "md5_digest": "4ee2d11d235cafdc3af89b0759183cc7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20290, "upload_time": "2018-06-21T11:23:04", "url": "https://files.pythonhosted.org/packages/0f/43/733d2a95958200f662a1e6c99201d9b339c838f14ff69f61d46e2f8ea91a/python-intervals-1.5.3.tar.gz" } ], "1.5.4": [ { "comment_text": "", "digests": { "md5": "347ccf310de8ee26f254914c7f28fc63", "sha256": "be8ae75adebb3353d27c920bf648b4e6888c005a1284200ae95b6c0481856c99" }, "downloads": -1, "filename": "python_intervals-1.5.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "347ccf310de8ee26f254914c7f28fc63", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10403, "upload_time": "2018-07-29T08:47:04", "url": "https://files.pythonhosted.org/packages/07/1e/6191dbf5fc9f4a0b3675c5f274fbb7d0521f76b8aab7b051457bff9b64aa/python_intervals-1.5.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a35dd44269fdb134bf19cfd80cb2c120", "sha256": "a8a15b7c2fadb8465310f44aa96e384828b35a29de38ac74738cd665924cb8ac" }, "downloads": -1, "filename": "python-intervals-1.5.4.tar.gz", "has_sig": false, "md5_digest": "a35dd44269fdb134bf19cfd80cb2c120", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20627, "upload_time": "2018-07-29T08:47:05", "url": "https://files.pythonhosted.org/packages/25/c0/50bb51832d13489073b94680e806a5a882bb01b7196d1bfc8d64a59e27c0/python-intervals-1.5.4.tar.gz" } ], "1.6.0": [ { "comment_text": "", "digests": { "md5": "2b486fdbc98c457642fea789d7886a56", "sha256": "cfada668b389de537ec2abf7f6bead1948b797b7afeaf516b9be91ca3e9a7508" }, "downloads": -1, "filename": "python_intervals-1.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2b486fdbc98c457642fea789d7886a56", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 10700, "upload_time": "2018-08-29T19:29:05", "url": "https://files.pythonhosted.org/packages/07/b5/9833c0a166a11a30c6942a657279cfa345a692afe3bf4281ed3ded462d05/python_intervals-1.6.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "18f6fecbb860d46cceb2f6487f39960e", "sha256": "3a83e3a58dcfb10217a96ebe2dc8600e27f8f519f2d451eef3aa88b50316f06a" }, "downloads": -1, "filename": "python-intervals-1.6.0.tar.gz", "has_sig": false, "md5_digest": "18f6fecbb860d46cceb2f6487f39960e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21286, "upload_time": "2018-08-29T19:29:07", "url": "https://files.pythonhosted.org/packages/19/16/b372f85c75993f92bf85fde639d739fc5d41f755f3791eaf6971a7a44187/python-intervals-1.6.0.tar.gz" } ], "1.7.0": [ { "comment_text": "", "digests": { "md5": "300a8ec9314622b0bafae27d92d12e5c", "sha256": "ee909b5aa06ce5859ad2a2c6b72537190a473cff9e3c089fe073c293b15682b3" }, "downloads": -1, "filename": "python_intervals-1.7.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "300a8ec9314622b0bafae27d92d12e5c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 14511, "upload_time": "2018-12-06T12:43:24", "url": "https://files.pythonhosted.org/packages/6e/41/2619fcbb318897fe10b6cd620caf36d65fe347a6096266baca2eaae932d1/python_intervals-1.7.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "037259fb9a03b083427c0caf1dc99ca7", "sha256": "f2a7adb9de515b625210dec7325985360c3635739628885fea633ef4670eb78e" }, "downloads": -1, "filename": "python-intervals-1.7.0.tar.gz", "has_sig": false, "md5_digest": "037259fb9a03b083427c0caf1dc99ca7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19940, "upload_time": "2018-12-06T12:43:26", "url": "https://files.pythonhosted.org/packages/02/7d/240dabcd6c31ed8e0e61402bbf953274a46bfff0654f148ad1c7af441cbe/python-intervals-1.7.0.tar.gz" } ], "1.8.0": [ { "comment_text": "", "digests": { "md5": "a1d047a2328e2ebbc04b785cd801671f", "sha256": "1d3de06b01517548b4a8d5137dfba5c2514780ec9b99778d5ed7920ee10ee22b" }, "downloads": -1, "filename": "python_intervals-1.8.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a1d047a2328e2ebbc04b785cd801671f", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 16506, "upload_time": "2018-12-15T10:21:35", "url": "https://files.pythonhosted.org/packages/c6/70/dc592dbb3512216aec9a1e35ec51691bd8ac41a550fc4b1e2b9fb937d17c/python_intervals-1.8.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1ce27bbb107f2b3e2b9d7cdbb3cde2fd", "sha256": "b55406c44136ca03a9f49d82b466e30824cb7c501614989b23809442d01ebcb0" }, "downloads": -1, "filename": "python-intervals-1.8.0.tar.gz", "has_sig": false, "md5_digest": "1ce27bbb107f2b3e2b9d7cdbb3cde2fd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23251, "upload_time": "2018-12-15T10:21:37", "url": "https://files.pythonhosted.org/packages/71/cd/4b15456e84a08814bea3082344cb823521ec22b00394b773eb905541c062/python-intervals-1.8.0.tar.gz" } ], "1.9.0": [ { "comment_text": "", "digests": { "md5": "d8f6cc824e75bea142fbdcca53cb448d", "sha256": "611e0f425383e1a92f82e9f907a44657866bfb8369b3f83bac86c63bbfff8614" }, "downloads": -1, "filename": "python_intervals-1.9.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d8f6cc824e75bea142fbdcca53cb448d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 20934, "upload_time": "2019-09-13T08:44:29", "url": "https://files.pythonhosted.org/packages/a7/e4/bdc6d3306159cb0ad12942e072b6557ba706f4017e7b41b83bc16b0f66b4/python_intervals-1.9.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "937e1a2edd642559f3f49b7312fcb7e0", "sha256": "f9010eaa916419f2867e48020536ba417ce43acd2bb8c082d6c7cb511621237c" }, "downloads": -1, "filename": "python-intervals-1.9.0.tar.gz", "has_sig": false, "md5_digest": "937e1a2edd642559f3f49b7312fcb7e0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34493, "upload_time": "2019-09-13T08:44:32", "url": "https://files.pythonhosted.org/packages/32/26/fab298a97df269212245238ac19caeac5960ee60dfd7db85651f0996546a/python-intervals-1.9.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "376cbf3b7ef083404989a3e32e6a4ee3", "sha256": "6d310579752f8e569cd79fd963f7593af494dbefb80368a15bfe2c24ae0a4383" }, "downloads": -1, "filename": "python_intervals-1.10.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "376cbf3b7ef083404989a3e32e6a4ee3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21805, "upload_time": "2019-09-26T07:44:21", "url": "https://files.pythonhosted.org/packages/7f/1c/bfe1e8460ba1d02051b1dc98ed2609c2f91b55632266f13ac0fae0cf9cec/python_intervals-1.10.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8955317ff4e42590c90ba6247b1caaed", "sha256": "0d26746eaed0be78a61dd289bb7a10721b08770bb3e807614835f490d514f2a5" }, "downloads": -1, "filename": "python-intervals-1.10.0.tar.gz", "has_sig": false, "md5_digest": "8955317ff4e42590c90ba6247b1caaed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 40116, "upload_time": "2019-09-26T07:44:24", "url": "https://files.pythonhosted.org/packages/ae/c4/413cd29bfbe8337eeb2c968194f53cbc1424cc48ad60885e1300ef542598/python-intervals-1.10.0.tar.gz" } ] }