===========
Range lists
===========

Range lists describe ordered integer numbers. Sub-sequences of consecutive
numbers may, however, be stored in terms of their first and last members. This
allows representing a large list of integers with possible gaps in a more
memory-efficient manner than by putting them in a Python list.

  >>> from phebe.rangelist import RangeList

Range lists are immutable. They have a technical representation and a friendly
string representation. Upon initialization, pass all numbers and number ranges
in a variable-length argument list.

  >>> RangeList()
  RangeList()
  >>> print RangeList()
  []
  >>> RangeList(1, 3)
  RangeList(1, 3)
  >>> print RangeList(1,2,4)
  [1, 2, 4]

Pass number ranges as pairs of (first member) and (last member + 1).

  >>> RangeList((3,6))
  RangeList((3, 6))
  >>> print RangeList((3,6))
  [3 -- 5]

Even other RangeList instances are allowed as arguments. They will be unpacked
into their separate numbers and number ranges.

  >>> RangeList(RangeList(1, (2, 5), 7), RangeList((9, 11), 15))
  RangeList(1, (2, 5), 7, (9, 11), 15)

You may intermix single numbers, number ranges, and range lists freely.
Adjoint numbers or number ranges will not be joined internally.

  >>> print RangeList((1, 3), (3, 6))
  [1 -- 2, 3 -- 5]
  >>> print RangeList(1, (3, 6), 8, (10, 20))
  [1, 3 -- 5, 8, 10 -- 19]
  >>> print RangeList(1, RangeList((3, 5), 7), (8, 10))
  [1, 3 -- 4, 7, 8 -- 9]

In a boolean context, an empty RangeList evaluates to False, any non-empty
RangeList to True:

  >>> bool(RangeList())
  False
  >>> bool(RangeList(1))
  True

The length of a range list is the number of integers represented by it.

  >>> len(RangeList(1,2,3))
  3
  >>> len(RangeList((1, 5)))
  4
  >>> len(RangeList(3, (5, 9)))
  5

You can ask a range list for its minimum and maximum represented number.

  >>> min(RangeList(1, (3, 7)))
  1
  >>> min(RangeList((3, 7), 10))
  3
  >>> max(RangeList(1, (3, 7)))
  6
  >>> max(RangeList((3, 7), 10))
  10

If you iterate over a range list, it yields all numbers it represents.

  >>> iter(RangeList()) # doctest: +ELLIPSIS
  <generator object at ...>
  >>> list(RangeList())
  []
  >>> list(RangeList(1, 2, 5))
  [1, 2, 5]
  >>> list(RangeList(1, (3, 7)))
  [1, 3, 4, 5, 6]

You can test whether a range list contains a given number.

  >>> 3 in RangeList()
  False
  >>> 3 in RangeList(3, (5, 8))
  True
  >>> 3 in RangeList((3, 7), 10)
  True
  >>> 3 in RangeList(1, (5, 8))
  False


.. Local Variables:
.. mode: rst
.. End:
