def test_short_iterables():
    assert tuple(iterate_overlapping_subsequences([1])) == ()
    assert tuple(iterate_overlapping_subsequences([1], length=7)) == ()
        
    
            
            
    
            
            
    
def test_short_iterables():
    assert tuple(iterate_overlapping_subsequences([1])) == ()
    assert tuple(iterate_overlapping_subsequences([1], length=7)) == ()
        
    
            
            
    
            
            
    
def test_various_lengths():
    assert tuple(iterate_overlapping_subsequences(xrange(7), length=3)) == \
                        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6))
    assert tuple(iterate_overlapping_subsequences(xrange(7), length=4)) == \
                       ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6))
    assert tuple(iterate_overlapping_subsequences(xrange(7), length=5)) == \
                            ((0, 1, 2, 3, 4), (1, 2, 3, 4, 5), (2, 3, 4, 5, 6))
    
    assert tuple(iterate_overlapping_subsequences(xrange(7), length=4,
            wrap_around=True)) == ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5),
            (3, 4, 5, 6), (4, 5, 6, 0), (5, 6, 0, 1), (6, 0, 1, 2))
    assert tuple(iterate_overlapping_subsequences(xrange(7), length=5,
            wrap_around=True)) == ((0, 1, 2, 3, 4), (1, 2, 3, 4, 5),
            (2, 3, 4, 5, 6), (3, 4, 5, 6, 0), (4, 5, 6, 0, 1), (5, 6, 0, 1, 2),
            (6, 0, 1, 2, 3))
def test_various_lengths():
    assert tuple(iterate_overlapping_subsequences(range(7), length=3)) == \
                        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6))
    assert tuple(iterate_overlapping_subsequences(range(7), length=4)) == \
                       ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6))
    assert tuple(iterate_overlapping_subsequences(range(7), length=5)) == \
                            ((0, 1, 2, 3, 4), (1, 2, 3, 4, 5), (2, 3, 4, 5, 6))
    
    assert tuple(iterate_overlapping_subsequences(range(7), length=4,
            wrap_around=True)) == ((0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5),
            (3, 4, 5, 6), (4, 5, 6, 0), (5, 6, 0, 1), (6, 0, 1, 2))
    assert tuple(iterate_overlapping_subsequences(range(7), length=5,
            wrap_around=True)) == ((0, 1, 2, 3, 4), (1, 2, 3, 4, 5),
            (2, 3, 4, 5, 6), (3, 4, 5, 6, 0), (4, 5, 6, 0, 1), (5, 6, 0, 1, 2),
            (6, 0, 1, 2, 3))
Example #5
0
def divide_to_slices(sequence, n_slices):
    '''
    Divide a sequence to slices.
    
    Example:
    
        >>> divide_to_slices(range(10), 3)
        [range(0, 4), range(4, 7), range(7, 10)]
        
    '''
    from python_toolbox import cute_iter_tools

    assert isinstance(n_slices, numbers.Integral)
    assert n_slices >= 1

    sequence_length = get_length(sequence)
    base_slice_length, remainder = divmod(sequence_length, n_slices)
    indices = [0]
    for i in range(n_slices):
        indices.append(indices[-1] + base_slice_length + (remainder > i))
    assert len(indices) == n_slices + 1
    assert indices[0] == 0
    assert indices[-1] == sequence_length
    return [
        sequence[x:y]
        for x, y in cute_iter_tools.iterate_overlapping_subsequences(indices)
    ]
def test_garbage_collection():
    
    garbage_collected = set()

    class GarbageNoter(object):
        def __init__(self, n):
            assert isinstance(n, int)
            self.n = n
        def __del__(self):
            garbage_collected.add(self.n)
            
    iterable = (GarbageNoter(i) for i in xrange(7))
    
    consecutive_subsequences_iterator = \
                           iterate_overlapping_subsequences(iterable, length=3)
    
    def assert_garbage_collected(indexes):
        gc_tools.collect()
        assert set(indexes) == garbage_collected
        
    assert_garbage_collected(())
    next(consecutive_subsequences_iterator)
    assert_garbage_collected(())
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0,))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2, 3))
    with cute_testing.RaiseAssertor(StopIteration):
        next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2, 3, 4, 5, 6))
Example #7
0
def divide_to_slices(sequence, n_slices):
    '''
    Divide a sequence to slices.
    
    Example:
    
        >>> divide_to_slices(range(10), 3)
        [range(0, 4), range(4, 7), range(7, 10)]
        
    '''
    from python_toolbox import cute_iter_tools
    
    assert isinstance(n_slices, numbers.Integral)
    assert n_slices >= 1
    
    sequence_length = get_length(sequence)
    base_slice_length, remainder = divmod(sequence_length, n_slices)
    indices = [0]
    for i in range(n_slices):
        indices.append(indices[-1] + base_slice_length + (remainder > i))
    assert len(indices) == n_slices + 1
    assert indices[0] == 0
    assert indices[-1] == sequence_length
    return [sequence[x:y] for x, y in
                     cute_iter_tools.iterate_overlapping_subsequences(indices)]

    
Example #8
0
def test_garbage_collection():

    garbage_collected = set()

    class GarbageNoter(object):
        def __init__(self, n):
            assert isinstance(n, int)
            self.n = n

        def __del__(self):
            garbage_collected.add(self.n)

    iterable = (GarbageNoter(i) for i in xrange(7))

    consecutive_subsequences_iterator = \
                           iterate_overlapping_subsequences(iterable, length=3)

    def assert_garbage_collected(indexes):
        gc_tools.collect()
        assert set(indexes) == garbage_collected

    assert_garbage_collected(())
    next(consecutive_subsequences_iterator)
    assert_garbage_collected(())
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, ))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2))
    next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2, 3))
    with cute_testing.RaiseAssertor(StopIteration):
        next(consecutive_subsequences_iterator)
    assert_garbage_collected((0, 1, 2, 3, 4, 5, 6))
Example #9
0
def all_equal(iterable, exhaustive=False):
    '''
    Return whether all elements in the iterable are equal to each other.
    
    If `exhaustive` is set to `False`, it's assumed that the equality relation
    is transitive, therefore not every member is tested against every other
    member. So in a list of size `n`, `n-1` equality checks will be made.
    
    If `exhaustive` is set to `True`, every member will be checked against
    every other member. So in a list of size `n`, `(n*(n-1))/2` equality checks
    will be made.
    '''
    # todo: Maybe I should simply check if `len(set(iterable)) == 1`? Will not
    # work for unhashables.
    
    if exhaustive is True:
        items = tuple(iterable)
        if len(items) <= 1:
            return True
        from python_toolbox import combi
        pairs = tuple(
            items * comb for comb in combi.CombSpace(len(items), 2)
        )
        # Can't feed the items directly to `CombSpace` because they might not
        # be hashable.
    else: # exhaustive is False
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)
        
    return all(a == b for (a, b) in pairs)
Example #10
0
def all_equal(iterable, exhaustive=False):
    '''
    Return whether all elements in the iterable are equal to each other.
    
    If `exhaustive` is set to `False`, it's assumed that the equality relation
    is transitive, therefore not every member is tested against every other
    member. So in a list of size `n`, `n-1` equality checks will be made.
    
    If `exhaustive` is set to `True`, every member will be checked against
    every other member. So in a list of size `n`, `(n*(n-1))/2` equality checks
    will be made.
    '''
    # todo: Maybe I should simply check if `len(set(iterable)) == 1`? Will not
    # work for unhashables.

    if exhaustive is True:
        items = tuple(iterable)
        if len(items) <= 1:
            return True
        from python_toolbox import combi
        pairs = tuple(items * comb for comb in combi.CombSpace(len(items), 2))
        # Can't feed the items directly to `CombSpace` because they might not
        # be hashable.
    else:  # exhaustive is False
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)

    return all(a == b for (a, b) in pairs)
def test_lazy_tuple():
    lazy_tuple = \
          iterate_overlapping_subsequences(range(7), length=3, lazy_tuple=True)
    assert isinstance(lazy_tuple, nifty_collections.LazyTuple)
    assert not lazy_tuple.collected_data
    
    assert lazy_tuple == \
                        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6))
Example #12
0
def test_lazy_tuple():
    lazy_tuple = \
          iterate_overlapping_subsequences(range(7), length=3, lazy_tuple=True)
    assert isinstance(lazy_tuple, nifty_collections.LazyTuple)
    assert not lazy_tuple.collected_data

    assert lazy_tuple == \
                        ((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6))
Example #13
0
def all_equivalent(iterable,
                   relation=operator.eq,
                   *,
                   assume_reflexive=True,
                   assume_symmetric=True,
                   assume_transitive=True):
    '''
    Return whether all elements in the iterable are equivalent to each other.

    By default "equivalent" means they're all equal to each other in Python.
    You can set a different relation to the `relation` argument, as a function
    that accepts two arguments and returns whether they're equivalent or not.
    You can use this, for example, to test if all items are NOT equal by
    passing in `relation=operator.ne`. You can also define any custom relation
    you want: `relation=(lambda x, y: x % 7 == y % 7)`.

    By default, we assume that the relation we're using is an equivalence
    relation (see http://en.wikipedia.org/wiki/Equivalence_relation for
    definition.) This means that we assume the relation is reflexive, symmetric
    and transitive, so we can do less checks on the elements to save time. You
    can use `assume_reflexive=False`, `assume_symmetric=False` and
    `assume_transitive=False` to break any of these assumptions and make this
    function do more checks that the equivalence holds between any pair of
    items from the iterable. (The more assumptions you ask to break, the more
    checks this function does before it concludes that the relation holds
    between all items.)
    '''
    from python_toolbox import sequence_tools

    if not assume_transitive or not assume_reflexive:
        iterable = sequence_tools.ensure_iterable_is_sequence(iterable)

    if assume_transitive:
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)
    else:
        from python_toolbox import combi
        pairs = tuple(iterable * comb
                      for comb in combi.CombSpace(len(iterable), 2))
        # Can't feed the items directly to `CombSpace` because they might not
        # be hashable.

    if not assume_symmetric:
        pairs = itertools.chain(
            *itertools.starmap(lambda x, y: ((x, y), (y, x)), pairs))

    if not assume_reflexive:
        pairs = itertools.chain(pairs, zip(iterable, iterable))

    return all(itertools.starmap(relation, pairs))
Example #14
0
def find_clear_place_on_circle(circle_points, circle_size=1):
    '''
    Find the point on a circle that's the farthest away from other points.

    Given an interval `(0, circle_size)` and a bunch of points in it, find a
    place for a new point that is as far away from the other points as
    possible. (Since this is a circle, there's wraparound, e.g. the end of the
    interval connects to the start.)
    '''

    from python_toolbox import cute_iter_tools

    # Before starting, taking care of two edge cases:
    if not circle_points:
        # Edge case: No points at all
        return circle_size / 2
    if len(circle_points) == 1:
        # Edge case: Only one point
        return (circle_points[0] + circle_size / 2) % circle_size

    sorted_circle_points = sorted(circle_points)
    last_point = sorted_circle_points[-1]
    if last_point >= circle_size:
        raise Exception(f"One of the points ({last_point}) is bigger than the "
                        f"circle size {circle_size}.")
    clear_space = {}

    for first_point, second_point in \
        cute_iter_tools.iterate_overlapping_subsequences(sorted_circle_points,
                                                     wrap_around=True):

        clear_space[first_point] = second_point - first_point

    # That's the only one that might be negative, so we ensure it's positive:
    clear_space[last_point] %= circle_size

    maximum_clear_space = max(clear_space.values())

    winners = [
        key for (key, value) in clear_space.items()
        if value == maximum_clear_space
    ]

    winner = winners[0]

    result = (winner + (maximum_clear_space / 2)) % circle_size

    return result
Example #15
0
def all_equivalent(iterable, relation=operator.eq, assume_reflexive=True,
                   assume_symmetric=True, assume_transitive=True):
    '''
    Return whether all elements in the iterable are equivalent to each other.
    
    By default "equivalent" means they're all equal to each other in Python.
    You can set a different relation to the `relation` argument, as a function
    that accepts two arguments and returns whether they're equivalent or not.
    You can use this, for example, to test if all items are NOT equal by
    passing in `relation=operator.ne`. You can also define any custom relation
    you want: `relation=(lambda x, y: x % 7 == y % 7)`.
    
    By default, we assume that the relation we're using is an equivalence
    relation (see http://en.wikipedia.org/wiki/Equivalence_relation for
    definition.) This means that we assume the relation is reflexive, symmetric
    and transitive, so we can do less checks on the elements to save time. You
    can use `assume_reflexive=False`, `assume_symmetric=False` and
    `assume_transitive=False` to break any of these assumptions and make this
    function do more checks that the equivalence holds between any pair of
    items from the iterable. (The more assumptions you ask to break, the more
    checks this function does before it concludes that the relation holds
    between all items.)
    '''
    from python_toolbox import sequence_tools
    
    if not assume_transitive or not assume_reflexive:
        iterable = sequence_tools.ensure_iterable_is_sequence(iterable)
        
    if assume_transitive:
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)
    else:
        from python_toolbox import combi
        pairs = tuple(
            iterable * comb for comb in combi.CombSpace(len(iterable), 2)
        )
        # Can't feed the items directly to `CombSpace` because they might not
        # be hashable.
        
    if not assume_symmetric:
        pairs = itertools.chain(
            *itertools.starmap(lambda x, y: ((x, y), (y, x)), pairs)
        )
    
    if not assume_reflexive:
        pairs = itertools.chain(pairs,
                                zip(iterable, iterable))
        
    return all(itertools.starmap(relation, pairs))
Example #16
0
def find_clear_place_on_circle(circle_points, circle_size=1):
    '''
    Find the point on a circle that's the farthest away from other points.
    
    Given an interval `(0, circle_size)` and a bunch of points in it, find a
    place for a new point that is as far away from the other points as
    possible. (Since this is a circle, there's wraparound, e.g. the end of the
    interval connects to the start.)
    '''

    from python_toolbox import cute_iter_tools

    # Before starting, taking care of two edge cases:
    if not circle_points:
        # Edge case: No points at all
        return circle_size / 2
    if len(circle_points) == 1:
        # Edge case: Only one point
        return (circle_points[0] + circle_size / 2) % circle_size
    
    sorted_circle_points = sorted(circle_points)
    last_point = sorted_circle_points[-1]
    if last_point >= circle_size:
        raise Exception("One of the points (%s) is bigger than the circle "
                        "size %s." % (last_point, circle_size))
    clear_space = {}
    
    for first_point, second_point in \
        cute_iter_tools.iterate_overlapping_subsequences(sorted_circle_points,
                                                     wrap_around=True):
        
        clear_space[first_point] = second_point - first_point
        
    # That's the only one that might be negative, so we ensure it's positive:
    clear_space[last_point] %= circle_size
    
    maximum_clear_space = max(clear_space.values())
    
    winners = [key for (key, value) in clear_space.items()
               if value == maximum_clear_space]
    
    winner = winners[0]
    
    result = (winner + (maximum_clear_space / 2)) % circle_size
    
    return result
Example #17
0
def all_equal(iterable, exhaustive=False):
    '''
    Return whether all elements in the iterable are equal to each other.
    
    If `exhaustive` is set to `False`, it's assumed that the equality relation
    is transitive, therefore not every member is tested against every other
    member. So in a list of size `n`, `n-1` equality checks will be made.
    
    If `exhaustive` is set to `True`, every member will be checked against
    every other member. So in a list of size `n`, `(n*(n-1))/2` equality checks
    will be made.
    '''
    # todo: Maybe I should simply check if `len(set(iterable)) == 1`? Will not
    # work for unhashables.
    
    if exhaustive is True:
        pairs = sequence_tools.combinations(list(iterable), 2)
    else: # exhaustive is False
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)
        
    return all(a==b for (a, b) in pairs)
Example #18
0
def all_equal(iterable, exhaustive=False):
    '''
    Return whether all elements in the iterable are equal to each other.
    
    If `exhaustive` is set to `False`, it's assumed that the equality relation
    is transitive, therefore not every member is tested against every other
    member. So in a list of size `n`, `n-1` equality checks will be made.
    
    If `exhaustive` is set to `True`, every member will be checked against
    every other member. So in a list of size `n`, `(n*(n-1))/2` equality checks
    will be made.
    '''
    # todo: Maybe I should simply check if `len(set(iterable)) == 1`? Will not
    # work for unhashables.

    if exhaustive is True:
        pairs = sequence_tools.combinations(list(iterable), 2)
    else:  # exhaustive is False
        pairs = cute_iter_tools.iterate_overlapping_subsequences(iterable)

    return all(a == b for (a, b) in pairs)
Example #19
0
def test_length_2():

    # `iterate_overlapping_subsequences` returns an iterator, not a sequence:
    assert not isinstance(iterate_overlapping_subsequences(list(range(4))),
                          collections.Sequence)

    assert tuple(iterate_overlapping_subsequences(range(4))) == \
           tuple(iterate_overlapping_subsequences(xrange(4))) == \
           ((0, 1), (1, 2), (2, 3))

    assert tuple(iterate_overlapping_subsequences(range(4),
                                                  wrap_around=True)) == \
           tuple(iterate_overlapping_subsequences(xrange(4),
                                                  wrap_around=True)) ==\
           ((0, 1), (1, 2), (2, 3), (3, 0))

    assert tuple(iterate_overlapping_subsequences('meow')) == \
           (('m', 'e'), ('e', 'o'), ('o', 'w'))
def test_length_2():

    # `iterate_overlapping_subsequences` returns an iterator, not a sequence:
    assert not sequence_tools.is_sequence(
        iterate_overlapping_subsequences(range(4)))

    assert tuple(iterate_overlapping_subsequences(range(4))) == \
           tuple(iterate_overlapping_subsequences(xrange(4))) == \
           ((0, 1), (1, 2), (2, 3))

    assert tuple(iterate_overlapping_subsequences(range(4),
                                                  wrap_around=True)) == \
           tuple(iterate_overlapping_subsequences(xrange(4),
                                                  wrap_around=True)) ==\
           ((0, 1), (1, 2), (2, 3), (3, 0))

    assert tuple(iterate_overlapping_subsequences('meow')) == \
           (('m', 'e'), ('e', 'o'), ('o', 'w'))
def test_length_2():
    
    # `iterate_overlapping_subsequences` returns an iterator, not a sequence:
    assert not sequence_tools.is_sequence(
        iterate_overlapping_subsequences(range(4))
    )
                                          
    assert tuple(iterate_overlapping_subsequences(range(4))) == \
           tuple(iterate_overlapping_subsequences(xrange(4))) == \
           ((0, 1), (1, 2), (2, 3))
                                          
    assert tuple(iterate_overlapping_subsequences(range(4),
                                                  wrap_around=True)) == \
           tuple(iterate_overlapping_subsequences(xrange(4),
                                                  wrap_around=True)) ==\
           ((0, 1), (1, 2), (2, 3), (3, 0))
                                          
    assert tuple(iterate_overlapping_subsequences('meow')) == \
           (('m', 'e'), ('e', 'o'), ('o', 'w'))
def test_length_2():
    
    # `iterate_overlapping_subsequences` returns an iterator, not a sequence:
    assert not isinstance(
        iterate_overlapping_subsequences(list(range(4))),
        collections.Sequence
    )
                                          
    assert tuple(iterate_overlapping_subsequences(list(range(4)))) == \
           tuple(iterate_overlapping_subsequences(range(4))) == \
           ((0, 1), (1, 2), (2, 3))
                                          
    assert tuple(iterate_overlapping_subsequences(list(range(4)),
                                                  wrap_around=True)) == \
           tuple(iterate_overlapping_subsequences(range(4),
                                                  wrap_around=True)) ==\
           ((0, 1), (1, 2), (2, 3), (3, 0))
                                          
    assert tuple(iterate_overlapping_subsequences('meow')) == \
           (('m', 'e'), ('e', 'o'), ('o', 'w'))
Example #23
0
def test_iterable_too_short():
    with cute_testing.RaiseAssertor(NotImplementedError):
        tuple(iterate_overlapping_subsequences([1], wrap_around=True))
def test_iterable_too_short():
    with cute_testing.RaiseAssertor(NotImplementedError):
        tuple(iterate_overlapping_subsequences([1], wrap_around=True))