예제 #1
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))
예제 #2
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))
예제 #3
0
def from_factoradic(factoradic_number):
    '''
    Convert a factoradic representation to the number it's representing.
    
    Read about factoradic numbers here:
    https://en.wikipedia.org/wiki/Factorial_number_system
    
    Example:
    
        >>> from_factoradic((4, 0, 2, 0, 0))
        100
    
    '''
    from python_toolbox import sequence_tools
    assert isinstance(factoradic_number, collections.Iterable)
    factoradic_number = \
              sequence_tools.ensure_iterable_is_sequence(factoradic_number)
    number = 0
    for i, value in enumerate(reversed(factoradic_number)):
        assert 0 <= value <= i
        number += value * math.factorial(i)
    return number
예제 #4
0
def from_factoradic(factoradic_number):
    '''
    Convert a factoradic representation to the number it's representing.
    
    Read about factoradic numbers here:
    https://en.wikipedia.org/wiki/Factorial_number_system
    
    Example:
    
        >>> from_factoradic((4, 0, 2, 0, 0))
        100
    
    '''
    from python_toolbox import sequence_tools
    assert isinstance(factoradic_number, collections.Iterable)
    factoradic_number = \
              sequence_tools.ensure_iterable_is_sequence(factoradic_number)
    number = 0
    for i, value in enumerate(reversed(factoradic_number)):
        assert 0 <= value <= i
        number += value * math.factorial(i)
    return number