Tests of miscellaneous functions from ``plib.stdlib``.

    >>> from plib.stdlib.mathlib import gcd, lcm

The ``gcd`` and ``lcm`` functions use Euclid's algorithm to return the
greatest common divisor or least common multiple of two numbers. Note
that behavior is undefined for non-integer inputs.

    >>> gcd(30, 24), gcd(24, 30), gcd(-30, -24), gcd(-24, -30), gcd(-24, 30), gcd(30, -24), gcd(-30, 24), gcd(24, -30)
    (6, 6, -6, -6, -6, 6, -6, 6)
    >>> gcd(0, 5), gcd(5, 0), gcd(0, -5), gcd(-5, 0)
    (5, 5, -5, -5)
    >>> gcd(0, 0)
    0
    >>> lcm(2, 5), lcm(5, 2), lcm(-2, -5), lcm(-5, -2), lcm(-5, 2), lcm(2, -5), lcm(-2, 5), lcm(5, -2)
    (10, 10, -10, -10, 10, -10, 10, -10)
    >>> lcm(6, 4), lcm(4, 6), lcm(-6, -4), lcm(-4, -6), lcm(-4, 6), lcm(6, -4), lcm(-6, 4), lcm(4, -6)
    (12, 12, -12, -12, 12, -12, 12, -12)
    >>> lcm(0, 5), lcm(5, 0), lcm(0, -5), lcm(-5, 0)
    (0, 0, 0, 0)
    >>> lcm(0, 0)
    0

    >>> from plib.stdlib.coll import inrange, normalize, slice_len

The ``inrange`` function forces its argument to be within the low and
high limits given, and returns the result. Its behavior with anything
other than integer arguments is undefined (no type checking is done).

    >>> inrange(0, 0, 2)
    0
    >>> inrange(1, 0, 2)
    1
    >>> inrange(2, 0, 2)
    2
    >>> inrange(-1, 0, 2)
    0
    >>> inrange(3, 0, 2)
    2
    >>> inrange(0, 2, 0)
    0
    >>> inrange(1, 2, 0)
    1
    >>> inrange(2, 2, 0)
    2
    >>> inrange(-1, 2, 0)
    0
    >>> inrange(3, 2, 0)
    2

The ``normalize`` function returns its second argument "normalized"
as if it were an index into a sequence of length given by its first
argument. "Normalized" means negative indexes are interpreted relative
to the end of the sequence. Indexes that are out of range after
normalization throw IndexError.

    >>> normalize(6, 0)
    0
    >>> normalize(6, 5)
    5
    >>> normalize(6, -1)
    5
    >>> normalize(6, -6)
    0
    >>> normalize(6, 6)
    Traceback (most recent call last):
    ...
    IndexError: sequence index out of range
    >>> normalize(6, -7)
    Traceback (most recent call last):
    ...
    IndexError: sequence index out of range

The ``slice_len`` argument returns the number of index values that are
"covered" by the given slice. Its behavior is undefined for anything
except slice arguments (no type checking is done). The only exception
it will throw is ValueError if the slice step is zero (to match the
semantics of the builtin containers).

    >>> slice_len(slice(1))
    1
    >>> slice_len(slice(2))
    2
    >>> slice_len(slice(1, 2))
    1
    >>> slice_len(slice(2, 4))
    2
    >>> slice_len(slice(0, 1, 1))
    1
    >>> slice_len(slice(0, 2, 1))
    2
    >>> slice_len(slice(0, 2, 2))
    1
    >>> slice_len(slice(1, 0, -1))
    1
    >>> slice_len(slice(2, 0, -1))
    2
    >>> slice_len(slice(2, 0, -2))
    1
    >>> slice_len(slice(0))
    0
    >>> slice_len(slice(0, 0))
    0
    >>> slice_len(slice(0, 0, 0))
    Traceback (most recent call last):
    ...
    ValueError: Slice step cannot be zero.

We need a slight hack to test that ``slice_len`` returns None when
given a slice whose length cannot be determined (because there is
no count argument to tell it how long the sequence is that it is
indexing into).

    >>> class SliceHack(object):
    ...     def __getitem__(self, index):
    ...         if slice_len(index) is None:
    ...             return "Indefinite slice!"
    ...
    >>> SliceHack()[:]
    'Indefinite slice!'
    >>> SliceHack()[:-1]
    'Indefinite slice!'
    >>> SliceHack()[-1:]
    'Indefinite slice!'
    >>> SliceHack()[-1:-1]
    'Indefinite slice!'

    >>> from plib.stdlib.strings import strtobool, strtodate

The ``strtobool`` function provides a quick and dirty way to convert
the strings ``'False'`` and ``'True'`` back into their boolean
equivalents. It returns ``None`` for any other string value.

    >>> for b in (True, False, None):
    ...     print(strtobool(str(b)) is b)
    ... 
    True
    True
    True

The ``strtodate`` function provides a quick and dirty way to convert
strings in canonical date format back into date objects. Its behavior
for strings not correctly formatted is undefined.

    >>> s = '2001-05-30'
    >>> str(strtodate(s)) == s
    True
