"""Utilities for working with lists and sequences."""

class LazyList(list):
    def __init__(self, get_data):
        self.data = get_data

def flatten(x):
    """
    Returns a single, flat list which contains all elements retrieved
    from the sequence and all recursively contained sub-sequences
    (iterables).

    Examples:
    >>> [1, 2, [3, 4], (5, 6)]
    [1, 2, [3, 4], (5, 6)]

    From http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks
    """
    result = []
    for el in x:
        if hasattr(el, '__iter__') and not isinstance(el, str):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

def batch_size(items, size):
    """
    Retrieves items in batches of the given size.

    >>> l = range(1, 11)
    >>> batch_size(l, 3)
    [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
    >>> batch_size(l, 5)
    [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
    """
    return [items[i:i+size] for i in range(0, len(items), size)]

def batches(items, number):
    """
    Retrieves items in the given number of batches.

    >>> l = range(1, 11)
    >>> batches(l, 1)
    [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
    >>> batches(l, 2)
    [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
    >>> batches(l, 3)
    [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
    >>> batches(l, 4)
    [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
    >>> batches(l, 5)
    [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

    Initial batches will contain as many items as possible in cases where
    there are not enough items to be distributed evenly.

    >>> batches(l, 6)
    [[1, 2], [3, 4], [5, 6], [7, 8], [9], [10]]
    >>> batches(l, 7)
    [[1, 2], [3, 4], [5, 6], [7], [8], [9], [10]]
    >>> batches(l, 8)
    [[1, 2], [3, 4], [5], [6], [7], [8], [9], [10]]
    >>> batches(l, 9)
    [[1, 2], [3], [4], [5], [6], [7], [8], [9], [10]]
    >>> batches(l, 10)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]

    If there are more batches than items, empty batches will be appended
    to the batch list.

    >>> batches(l, 11)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], []]
    >>> batches(l, 12)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [], []]
    """
    div, mod= divmod(len(items), number)
    if div > 1:
        if mod:
            div += 1
        return batch_size(items, div)
    else:
        if not div:
            return [[item] for item in items] + [[]] * (number - mod)
        elif div == 1 and not mod:
            return [[item] for item in items]
        else:
            # mod now tells you how many lists of 2 you can fit in
            return ([items[i*2:(i*2)+2] for i in range(0, mod)] +
                    [[item] for item in items[mod*2:]])
