Ejemplo n.º 1
0
def test_infinite():
    infinite_range_arguments_tuples = ((), (10, infinity), (10, infinity, 2), (100, -infinity, -7))

    for infinite_range_arguments_tuple in infinite_range_arguments_tuples:
        cr0 = CuteRange(*infinite_range_arguments_tuple)
        assert type(cr0) == CuteRange
        assert not isinstance(cr0, xrange)
        assert isinstance(cr0, CuteRange)
        assert cr0.length == infinity and len(cr0) == 0
        assert isinstance(cr0[0], int)
        assert cr0[10:].length == cr0[200:].length == infinity
        assert sequence_tools.get_length(cr0[:10]) != infinity != sequence_tools.get_length(cr0[:200])
Ejemplo n.º 2
0
def test_infinite():
    infinite_range_arguments_tuples = ((), (10, infinity), (10, infinity, 2),
                                       (100, -infinity, -7))

    for infinite_range_arguments_tuple in infinite_range_arguments_tuples:
        cr0 = CuteRange(*infinite_range_arguments_tuple)
        assert type(cr0) == CuteRange
        assert not isinstance(cr0, xrange)
        assert isinstance(cr0, CuteRange)
        assert cr0.length == infinity and len(cr0) == 0
        assert isinstance(cr0[0], int)
        assert cr0[10:].length == cr0[200:].length == infinity
        assert sequence_tools.get_length(cr0[:10]) != infinity != \
                                           sequence_tools.get_length(cr0[:200])
Ejemplo n.º 3
0
 def __repr__(self):
     return '<%s: %s>' % (
         type(self).__name__,
         ' * '.join(
             str(sequence_tools.get_length(sequence))
             for sequence in self.sequences),
     )
Ejemplo n.º 4
0
def _enumerate(iterable, reverse_index):
    if reverse_index is False:
        return builtins.enumerate(iterable)
    else:
        from python_toolbox import sequence_tools
        try:
            length = sequence_tools.get_length(iterable)
        except AttributeError:
            iterable = nifty_collections.LazyTuple(iterable)
            length = len(iterable)
        return zip(range(length - 1, -1, -1), iterable)
Ejemplo n.º 5
0
def _enumerate(iterable, reverse_index):
    if reverse_index is False:
        return __builtin__.enumerate(iterable)
    else:
        from python_toolbox import sequence_tools
        try:
            length = sequence_tools.get_length(iterable)
        except AttributeError:
            iterable = nifty_collections.LazyTuple(iterable)
            length = len(iterable)
        return itertools.izip(range(length - 1, -1, -1), iterable)
Ejemplo n.º 6
0
 def accumulated_lengths(self):
     '''
     A sequence of the accumulated length as every sequence is added.
     
     For example, if this chain space has sequences with lengths of 10, 100
     and 1000, this would be `[0, 10, 110, 1110]`.
     '''
     total = 0
     yield 0
     for sequence in self.sequences:
         total += sequence_tools.get_length(sequence)
         yield total
Ejemplo n.º 7
0
 def accumulated_lengths(self):
     '''
     A sequence of the accumulated length as every sequence is added.
     
     For example, if this chain space has sequences with lengths of 10, 100
     and 1000, this would be `[0, 10, 110, 1110]`.
     '''
     total = 0
     yield 0
     for sequence in self.sequences:
         total += sequence_tools.get_length(sequence)
         yield total
Ejemplo n.º 8
0
class MapSpace(sequence_tools.CuteSequenceMixin, collections.Sequence):
    '''
    A space of a function applied to a sequence.
    
    This is similar to Python's builtin `map`, except that it behaves like a
    sequence rather than an iterable. (Though it's also iterable.) You can
    access any item by its index number.
    
    Example:
    
        >>> map_space = MapSpace(lambda x: x ** 2, range(7))
        >>> map_space
        MapSpace(<function <lambda> at 0x00000000030C1510>, range(0, 7))
        >>> len(map_space)
        7
        >>> map_space[3]
        9
        >>> tuple(map_space)
        (0, 1, 4, 9, 16, 25, 36)
    
    '''
    def __init__(self, function, sequence):

        self.function = function
        self.sequence = sequence_tools.ensure_iterable_is_immutable_sequence(
            sequence, default_type=nifty_collections.LazyTuple)

    length = caching.CachedProperty(
        lambda self: sequence_tools.get_length(self.sequence))

    def __repr__(self):
        return '%s(%s, %s)' % (type(self).__name__, self.function,
                               self.sequence)

    def __getitem__(self, i):
        if isinstance(i, slice):
            return type(self)(self.function, self.sequence[i])
        assert isinstance(i, int)
        return self.function(self.sequence[i])  # Propagating `IndexError`.

    def __iter__(self):
        for item in self.sequence:
            yield self.function(item)

    _reduced = property(lambda self:
                        (type(self), self.function, self.sequence))

    __eq__ = lambda self, other: (isinstance(other, MapSpace) and self._reduced
                                  == other._reduced)
    __hash__ = lambda self: hash(self._reduced)

    __bool__ = lambda self: bool(self.sequence)
Ejemplo n.º 9
0
 def apply(self, sequence, result_type=None):
     '''
     Apply the perm to a sequence, choosing items from it.
     
     This can also be used as `sequence * perm`. Example:
     
     >>> perm = PermSpace(5)[10]
     >>> perm
     <Perm: (0, 2, 4, 1, 3)>
     >>> perm.apply('growl')
     'golrw'
     >>> 'growl' * perm
     'golrw'
     
     Specify `result_type` to determine the type of the result returned. If
     `result_type=None`, will use `tuple`, except when `other` is a `str` or
     `Perm`, in which case that same type would be used.
     '''
     sequence = \
          sequence_tools.ensure_iterable_is_immutable_sequence(sequence)
     if sequence_tools.get_length(sequence) < \
                                            sequence_tools.get_length(self):
         raise Exception("Can't apply permutation on sequence of "
                         "shorter length.")
     
     permed_generator = (sequence[i] for i in self)
     if result_type is not None:
         if result_type is str:
             return ''.join(permed_generator)
         else:
             return result_type(permed_generator)
     elif isinstance(sequence, Perm):
         return type(self)(permed_generator,
                           sequence.nominal_perm_space)
     elif isinstance(sequence, str):
         return ''.join(permed_generator)
     else:
         return tuple(permed_generator)
Ejemplo n.º 10
0
 def apply(self, sequence, result_type=None):
     '''
     Apply the perm to a sequence, choosing items from it.
     
     This can also be used as `sequence * perm`. Example:
     
         >>> perm = PermSpace(5)[10]
         >>> perm
         <Perm: (0, 2, 4, 1, 3)>
         >>> perm.apply('growl')
         'golrw'
         >>> 'growl' * perm
         'golrw'
     
     Specify `result_type` to determine the type of the result returned. If
     `result_type=None`, will use `tuple`, except when `other` is a `str` or
     `Perm`, in which case that same type would be used.
     '''
     sequence = \
          sequence_tools.ensure_iterable_is_immutable_sequence(sequence)
     if sequence_tools.get_length(sequence) < \
                                            sequence_tools.get_length(self):
         raise Exception("Can't apply permutation on sequence of "
                         "shorter length.")
     
     permed_generator = (sequence[i] for i in self)
     if result_type is not None:
         if result_type is str:
             return ''.join(permed_generator)
         else:
             return result_type(permed_generator)
     elif isinstance(sequence, Perm):
         return type(self)(permed_generator,
                           sequence.nominal_perm_space)
     elif isinstance(sequence, str):
         return ''.join(permed_generator)
     else:
         return tuple(permed_generator)
Ejemplo n.º 11
0
 def index(self, given_sequence):
     '''Get the index number of `given_sequence` in this product space.'''
     if not isinstance(given_sequence, collections.Sequence) or \
                             not len(given_sequence) == len(self.sequences):
         raise ValueError
     
     current_radix = 1
     wip_index = 0
         
     for item, sequence in reversed(tuple(zip(given_sequence,
                                              self.sequences))):
         wip_index += current_radix * sequence.index(item)
         # (Propagating `ValueError`.)
         current_radix *= sequence_tools.get_length(sequence)
         
     return wip_index
Ejemplo n.º 12
0
    def index(self, given_sequence):
        '''Get the index number of `given_sequence` in this product space.'''
        if not isinstance(given_sequence, collections.Sequence) or \
                                not len(given_sequence) == len(self.sequences):
            raise ValueError

        current_radix = 1
        wip_index = 0

        for item, sequence in reversed(
                tuple(zip(given_sequence, self.sequences))):
            wip_index += current_radix * sequence.index(item)
            # (Propagating `ValueError`.)
            current_radix *= sequence_tools.get_length(sequence)

        return wip_index
Ejemplo n.º 13
0
 def __bool__(self):
     from python_toolbox import sequence_tools
     return bool(sequence_tools.get_length(self))
Ejemplo n.º 14
0
    def __getitem__(self, i):
        if isinstance(i, (slice, sequence_tools.CanonicalSlice)):
            canonical_slice = sequence_tools.CanonicalSlice(
                i, self.length, offset=self.canonical_slice.start)
            return PermSpace(self.sequence,
                             domain=self.domain,
                             n_elements=self.n_elements,
                             fixed_map=self.fixed_map,
                             degrees=self.degrees,
                             is_combination=self.is_combination,
                             slice_=canonical_slice,
                             perm_type=self.perm_type)

        assert isinstance(i, numbers.Integral)
        if i <= -1:
            i += self.length

        if not (0 <= i < self.length):
            raise IndexError
        elif self.is_sliced:
            return self.unsliced[i + self.canonical_slice.start]
        elif self.is_dapplied:
            return self.perm_type(self.undapplied[i], perm_space=self)

        #######################################################################
        elif self.is_degreed:
            if self.is_rapplied:
                assert not self.is_recurrent and \
                       not self.is_partial and not self.is_combination and \
                       not self.is_dapplied and not self.is_sliced
                return self.perm_type(map(self.sequence.__getitem__,
                                          self.unrapplied[i]),
                                      perm_space=self)


            assert not self.is_rapplied and not self.is_recurrent and \
                   not self.is_partial and not self.is_combination and \
                   not self.is_dapplied and not self.is_sliced
            # If that wasn't an example of asserting one's dominance, I don't
            # know what is.

            available_values = list(self.free_values)
            wip_perm_sequence_dict = dict(self.fixed_map)
            wip_n_cycles_in_fixed_items = \
                                    self._n_cycles_in_fixed_items_of_just_fixed
            wip_i = i
            for j in self.sequence:
                if j in wip_perm_sequence_dict:
                    continue
                for unused_value in available_values:
                    candidate_perm_sequence_dict = dict(wip_perm_sequence_dict)
                    candidate_perm_sequence_dict[j] = unused_value

                    ### Checking whether we closed a cycle: ###################
                    #                                                         #
                    if j == unused_value:
                        closed_cycle = True
                    else:
                        current = j
                        while True:
                            current = candidate_perm_sequence_dict[current]
                            if current == j:
                                closed_cycle = True
                                break
                            elif current not in candidate_perm_sequence_dict:
                                closed_cycle = False
                                break
                    #                                                         #
                    ### Finished checking whether we closed a cycle. ##########

                    candidate_n_cycles_in_fixed_items = \
                                     wip_n_cycles_in_fixed_items + closed_cycle

                    candidate_fixed_perm_space_length = sum(
                        math_tools.abs_stirling(
                            self.sequence_length -
                            len(candidate_perm_sequence_dict),
                            self.sequence_length - degree -
                            candidate_n_cycles_in_fixed_items)
                        for degree in self.degrees)

                    if wip_i < candidate_fixed_perm_space_length:
                        available_values.remove(unused_value)
                        wip_perm_sequence_dict[j] = unused_value
                        wip_n_cycles_in_fixed_items = \
                                              candidate_n_cycles_in_fixed_items

                        break
                    wip_i -= candidate_fixed_perm_space_length
                else:
                    raise RuntimeError
            assert wip_i == 0
            return self.perm_type(
                (wip_perm_sequence_dict[k] for k in self.domain), self)

        #######################################################################
        elif self.is_recurrent:
            assert not self.is_dapplied and not self.is_degreed and \
                                                             not self.is_sliced
            available_values = list(self.sequence)
            reserved_values = nifty_collections.Bag(self.fixed_map.values())
            wip_perm_sequence_dict = dict(self.fixed_map)
            wip_i = i
            shit_set = set()
            for j in range(self.n_elements):
                if j in self.fixed_map:
                    available_values.remove(self.fixed_map[j])
                    reserved_values[self.fixed_map[j]] -= 1
                    continue
                unused_values = [
                    item for item in
                    nifty_collections.OrderedBag(available_values) -
                    reserved_values if item not in shit_set
                ]
                for unused_value in unused_values:
                    wip_perm_sequence_dict[j] = unused_value

                    candidate_sub_perm_space = \
                                             PermSpace._create_with_cut_prefix(
                        self.sequence,
                        n_elements=self.n_elements,
                        fixed_map=wip_perm_sequence_dict,
                        is_combination=self.is_combination,
                        shit_set=shit_set, perm_type=self.perm_type
                    )

                    if wip_i < candidate_sub_perm_space.length:
                        available_values.remove(unused_value)
                        break
                    else:
                        wip_i -= candidate_sub_perm_space.length
                        if self.is_combination:
                            shit_set.add(wip_perm_sequence_dict[j])
                        del wip_perm_sequence_dict[j]
                else:
                    raise RuntimeError
            assert wip_i == 0
            return self.perm_type(
                dict_tools.get_tuple(wip_perm_sequence_dict, self.domain),
                self)

        #######################################################################
        elif self.is_fixed:
            free_values_perm = self._free_values_unsliced_perm_space[i]
            free_values_perm_iterator = iter(free_values_perm)
            return self.perm_type(
                tuple((self._undapplied_fixed_map[m] if (
                    m in self.fixed_indices
                ) else next(free_values_perm_iterator))
                      for m in self.indices), self)

        #######################################################################
        elif self.is_combination:
            wip_number = self.length - 1 - i
            wip_perm_sequence = []
            for i in range(self.n_elements, 0, -1):
                for j in range(self.sequence_length, i - 2, -1):
                    candidate = math_tools.binomial(j, i)
                    if candidate <= wip_number:
                        wip_perm_sequence.append(self.sequence[-(j + 1)])
                        wip_number -= candidate
                        break
                else:
                    raise RuntimeError
            result = tuple(wip_perm_sequence)
            assert len(result) == self.n_elements
            return self.perm_type(result, self)

        #######################################################################
        else:
            factoradic_number = math_tools.to_factoradic(
                i * math.factorial(self.n_unused_elements),
                n_digits_pad=self.sequence_length)
            if self.is_partial:
                factoradic_number = factoradic_number[:-self.n_unused_elements]
            unused_numbers = list(self.sequence)
            result = tuple(
                unused_numbers.pop(factoradic_digit)
                for factoradic_digit in factoradic_number)
            assert sequence_tools.get_length(result) == self.n_elements
            return self.perm_type(result, self)
Ejemplo n.º 15
0
 def __repr__(self):
     return '<%s: %s>' % (
         type(self).__name__,
         ' * '.join(str(sequence_tools.get_length(sequence))
                    for sequence in self.sequences),
     )
Ejemplo n.º 16
0
 def __bool__(self):
     from python_toolbox import sequence_tools
     return bool(sequence_tools.get_length(self))
Ejemplo n.º 17
0
    def index(self, perm):
        '''Get the index number of permutation `perm` in this space.'''
        if not isinstance(perm, collections.Iterable):
            raise ValueError
        
        try:
            perm = sequence_tools.ensure_iterable_is_immutable_sequence(
                perm,
                allow_unordered=False
            )
        except sequence_tools.UnorderedIterableException:
            raise ValueError('An unordered iterable is never contained in a '
                             '`PermSpace`. Try an ordered one.')
        
        perm_set = set(perm) if not isinstance(perm, UnrecurrentedPerm) \
                                                  else set(perm._perm_sequence)
        if not (perm_set <= set(self.sequence)):
            raise ValueError
        
        if sequence_tools.get_length(perm) != self.n_elements:
            raise ValueError
        
        if not isinstance(perm, self.perm_type):
            perm = self.perm_type(perm, self)
            
        if self.sequence != perm.nominal_perm_space.sequence:
            # (This also covers `self.rapplied != perm.rapplied`)
            raise ValueError
        if self.domain != perm.domain:
            # (This also covers `self.dapplied != perm.dapplied`)
            raise ValueError
        if self.is_degreed and (perm.degree not in self.degrees):
            raise ValueError
        
        # At this point we know the permutation contains the correct items, and
        # has the correct degree.
        if perm.is_dapplied: return self.undapplied.index(perm.undapplied)
        
        #######################################################################
        elif self.is_degreed:
            if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied)
            wip_perm_number = 0
            wip_perm_sequence_dict = dict(self.fixed_map)
            unused_values = list(self.free_values)
            for i, value in enumerate(perm._perm_sequence):
                if i in self.fixed_indices:
                    continue
                unused_values.remove(value)
                lower_values = [j for j in unused_values if j < value]
                for lower_value in lower_values:
                    temp_fixed_map = dict(wip_perm_sequence_dict)
                    temp_fixed_map[i] = lower_value
                    wip_perm_number += PermSpace(
                        self.sequence_length, degrees=self.degrees,
                        fixed_map=temp_fixed_map
                    ).length
                    
                wip_perm_sequence_dict[self.domain[i]] = value
                
            perm_number = wip_perm_number
            
        #######################################################################
        elif self.is_recurrent:
            assert not self.is_degreed and not self.is_dapplied

            wip_perm_number = 0
            unused_values = list(self.sequence)
            reserved_values = list(self.fixed_map.values())
            perm_sequence_list = list(perm._perm_sequence)
            shit_set = set()
            for i, value in enumerate(perm._perm_sequence):
                if i in self.fixed_map:
                    if self.fixed_map[i] == value:
                        unused_values.remove(value)
                        reserved_values.remove(value)
                        continue
                    else:
                        raise ValueError
                lower_values = [
                    thing for thing in
                    nifty_collections.OrderedSet(unused_values) if
                    (thing not in reserved_values or unused_values.count(thing)
                     > reserved_values.count(thing)) and 
                    unused_values.index(thing) < unused_values.index(value) and
                                                          thing not in shit_set
                ]
                unused_values.remove(value)
                for lower_value in lower_values:
                    temp_fixed_map = dict(
                            enumerate(perm_sequence_list[:i] + [lower_value])
                    )
                    temp_fixed_map.update(self.fixed_map)
                    
                    candidate_sub_perm_space = \
                                             PermSpace._create_with_cut_prefix(
                        self.sequence,
                        n_elements=self.n_elements,
                        fixed_map=temp_fixed_map,
                        is_combination=self.is_combination,
                        shit_set=shit_set, perm_type=self.perm_type
                    )
                    
                    wip_perm_number += candidate_sub_perm_space.length
                    if self.is_combination:
                        shit_set.add(lower_value)
                    
                
            perm_number = wip_perm_number
            
        #######################################################################
        elif self.is_fixed:
            assert not self.is_degreed and not self.is_recurrent
            free_values_perm_sequence = []
            for i, perm_item in zip(self.domain, perm._perm_sequence):
                if i in self.fixed_map:
                    if self.fixed_map[i] != perm_item:
                        raise ValueError
                else:
                    free_values_perm_sequence.append(perm_item)
            
            # At this point we know all the items that should be fixed are
            # fixed.
            
            perm_number = self._free_values_unsliced_perm_space.index(
                free_values_perm_sequence
            )
            
            
        #######################################################################
        elif self.is_combination:
            if perm.is_rapplied:
                return self.unrapplied.index(perm.unrapplied)
            
            assert not self.is_rapplied and not self.is_recurrent and \
                   not self.is_dapplied and not self.is_fixed and \
                                                            not self.is_degreed
            
            if not cute_iter_tools.is_sorted(perm._perm_sequence):
                raise ValueError
            
            processed_perm_sequence = tuple(
                self.sequence_length - 1 -
                                     item for item in perm._perm_sequence[::-1]
            )
            perm_number = self.unsliced.length - 1 - sum(
                (math_tools.binomial(item, i) for i, item in
                                  enumerate(processed_perm_sequence, start=1)),
                0
            )
              
        #######################################################################
        else:
            factoradic_number = []
            unused_values = list(self.sequence)
            for i, value in enumerate(perm._perm_sequence):
                index_of_current_number = unused_values.index(value)
                factoradic_number.append(index_of_current_number)
                unused_values.remove(value)
            perm_number = math_tools.from_factoradic(
                factoradic_number +
                [0] * self.n_unused_elements
            ) // math.factorial(self.n_unused_elements)
            
            
        #######################################################################
        
        if perm_number not in self.canonical_slice:
            raise ValueError
            
        return perm_number - self.canonical_slice.start
Ejemplo n.º 18
0
    def __init__(self, slice_, iterable_or_length=None, offset=0):
        from python_toolbox import sequence_tools
        from python_toolbox import cute_iter_tools
        
        if isinstance(slice_, CanonicalSlice):
            slice_ = slice(slice_.start, slice_.stop, slice_.step)
        assert isinstance(slice_, slice)
        self.given_slice = slice_
        if iterable_or_length is not None:
            if isinstance(iterable_or_length,
                          math_tools.PossiblyInfiniteIntegral):
                self.length = iterable_or_length
            elif isinstance(iterable_or_length, collections.Sequence):
                self.length = sequence_tools.get_length(iterable_or_length)
            else:
                assert isinstance(iterable_or_length, collections.Iterable)
                self.length = cute_iter_tools.get_length(iterable_or_length)
        else:
            self.length = None
            
        self.offset = offset
            
        ### Parsing `step`: ###################################################
        #                                                                     #
        assert slice_.step != 0
        if slice_.step is None:
            self.step = 1
        else:
            self.step = slice_.step
        #                                                                     #
        ### Finished parsing `step`. ##########################################

            
        ### Parsing `start`: #################################################
        #                                                                    #
        if slice_.start is None:
            if self.step > 0:
                self.start = 0 + self.offset
            else:
                assert self.step < 0
                self.start = (self.length + self.offset) if \
                                        (self.length is not None) else infinity
        else: # s.start is not None
            if self.length is not None:
                if slice_.start < 0:
                    self.start = \
                               max(slice_.start + self.length, 0) + self.offset
                else:
                    self.start = min(slice_.start, self.length) + self.offset
            else: 
                self.start = slice_.start + self.offset
        #                                                                     #
        ### Finished parsing `start`. #########################################
        
        ### Parsing `stop`: ###################################################
        #                                                                     #
        if slice_.stop is None:
            if self.step > 0:
                self.stop = (self.length + self.offset) if \
                                        (self.length is not None) else infinity
            else:
                assert self.step < 0
                self.stop = -infinity 
            
        else: # slice_.stop is not None
            if self.length is not None:
                if slice_.stop < 0:
                    self.stop = max(slice_.stop + self.length, 0) + self.offset
                else: # slice_.stop >= 0
                    self.stop = min(slice_.stop, self.length) + self.offset
            else: 
                self.stop = slice_.stop + self.offset 
        #                                                                     #
        ### Finished parsing `stop`. ##########################################
            
        if (self.step > 0 and self.start >= self.stop >= 0) or \
           (self.step < 0 and self.stop >= self.start):
            # We have a case of an empty slice.
            self.start = self.stop = 0
        
            
        self.slice_ = slice(*((item if item not in math_tools.infinities
                               else None) for item in self))
            
        ### Doing sanity checks: ##############################################
        #                                                                     #
        if self.length:
            if self.step > 0:
                assert 0 <= self.start <= \
                                         self.stop <= self.length + self.offset
            else:
                assert self.step < 0
                assert 0 <= self.stop <= \
                                        self.start <= self.length + self.offset
Ejemplo n.º 19
0
    def __init__(self, slice_, iterable_or_length=None, offset=0):
        from python_toolbox import sequence_tools
        from python_toolbox import cute_iter_tools

        if isinstance(slice_, CanonicalSlice):
            slice_ = slice(slice_.start, slice_.stop, slice_.step)
        assert isinstance(slice_, slice)
        self.given_slice = slice_
        if iterable_or_length is not None:
            if isinstance(iterable_or_length,
                          math_tools.PossiblyInfiniteIntegral):
                self.length = iterable_or_length
            elif isinstance(iterable_or_length, collections.Sequence):
                self.length = sequence_tools.get_length(iterable_or_length)
            else:
                assert isinstance(iterable_or_length, collections.Iterable)
                self.length = cute_iter_tools.get_length(iterable_or_length)
        else:
            self.length = None

        self.offset = offset

        ### Parsing `step`: ###################################################
        #                                                                     #
        assert slice_.step != 0
        if slice_.step is None:
            self.step = 1
        else:
            self.step = slice_.step
        #                                                                     #
        ### Finished parsing `step`. ##########################################

        ### Parsing `start`: #################################################
        #                                                                    #
        if slice_.start is None:
            if self.step > 0:
                self.start = 0 + self.offset
            else:
                assert self.step < 0
                self.start = (self.length + self.offset) if \
                                        (self.length is not None) else infinity
        else:  # s.start is not None
            if self.length is not None:
                if slice_.start < 0:
                    self.start = \
                               max(slice_.start + self.length, 0) + self.offset
                else:
                    self.start = min(slice_.start, self.length) + self.offset
            else:
                self.start = slice_.start + self.offset
        #                                                                     #
        ### Finished parsing `start`. #########################################

        ### Parsing `stop`: ###################################################
        #                                                                     #
        if slice_.stop is None:
            if self.step > 0:
                self.stop = (self.length + self.offset) if \
                                        (self.length is not None) else infinity
            else:
                assert self.step < 0
                self.stop = -infinity

        else:  # slice_.stop is not None
            if self.length is not None:
                if slice_.stop < 0:
                    self.stop = max(slice_.stop + self.length, 0) + self.offset
                else:  # slice_.stop >= 0
                    self.stop = min(slice_.stop, self.length) + self.offset
            else:
                self.stop = slice_.stop + self.offset
        #                                                                     #
        ### Finished parsing `stop`. ##########################################

        if (self.step > 0 and self.start >= self.stop >= 0) or \
           (self.step < 0 and self.stop >= self.start):
            # We have a case of an empty slice.
            self.start = self.stop = 0

        self.slice_ = slice(
            *((item if item not in math_tools.infinities else None)
              for item in self))

        ### Doing sanity checks: ##############################################
        #                                                                     #
        if self.length:
            if self.step > 0:
                assert 0 <= self.start <= \
                                         self.stop <= self.length + self.offset
            else:
                assert self.step < 0
                assert 0 <= self.stop <= \
                                        self.start <= self.length + self.offset
Ejemplo n.º 20
0
    def index(self, perm):
        '''Get the index number of permutation `perm` in this space.'''
        if not isinstance(perm, collections.abc.Iterable):
            raise ValueError

        perm = sequence_tools.ensure_iterable_is_immutable_sequence(perm)

        perm_set = set(perm) if not isinstance(perm, UnrecurrentedPerm) \
                                                  else set(perm._perm_sequence)
        if not (perm_set <= set(self.sequence)):
            raise ValueError

        if sequence_tools.get_length(perm) != self.n_elements:
            raise ValueError

        if not isinstance(perm, self.perm_type):
            perm = self.perm_type(perm, self)

        if self.sequence != perm.nominal_perm_space.sequence:
            # (This also covers `self.rapplied != perm.rapplied`)
            raise ValueError
        if self.domain != perm.domain:
            # (This also covers `self.dapplied != perm.dapplied`)
            raise ValueError
        if self.is_degreed and (perm.degree not in self.degrees):
            raise ValueError

        # At this point we know the permutation contains the correct items, and
        # has the correct degree.
        if perm.is_dapplied:
            return self.undapplied.index(perm.undapplied)

            #######################################################################
        elif self.is_degreed:
            if perm.is_rapplied: return self.unrapplied.index(perm.unrapplied)
            wip_perm_number = 0
            wip_perm_sequence_dict = dict(self.fixed_map)
            unused_values = list(self.free_values)
            for i, value in enumerate(perm._perm_sequence):
                if i in self.fixed_indices:
                    continue
                unused_values.remove(value)
                lower_values = [j for j in unused_values if j < value]
                for lower_value in lower_values:
                    temp_fixed_map = dict(wip_perm_sequence_dict)
                    temp_fixed_map[i] = lower_value
                    wip_perm_number += PermSpace(
                        self.sequence_length,
                        degrees=self.degrees,
                        fixed_map=temp_fixed_map).length

                wip_perm_sequence_dict[self.domain[i]] = value

            perm_number = wip_perm_number

        #######################################################################
        elif self.is_recurrent:
            assert not self.is_degreed and not self.is_dapplied

            wip_perm_number = 0
            unused_values = list(self.sequence)
            reserved_values = list(self.fixed_map.values())
            perm_sequence_list = list(perm._perm_sequence)
            shit_set = set()
            for i, value in enumerate(perm._perm_sequence):
                if i in self.fixed_map:
                    if self.fixed_map[i] == value:
                        unused_values.remove(value)
                        reserved_values.remove(value)
                        continue
                    else:
                        raise ValueError
                lower_values = [
                    thing
                    for thing in nifty_collections.OrderedSet(unused_values)
                    if (thing not in reserved_values or unused_values.count(
                        thing) > reserved_values.count(thing))
                    and unused_values.index(thing) < unused_values.index(value)
                    and thing not in shit_set
                ]
                unused_values.remove(value)
                for lower_value in lower_values:
                    temp_fixed_map = dict(
                        enumerate(perm_sequence_list[:i] + [lower_value]))
                    temp_fixed_map.update(self.fixed_map)

                    candidate_sub_perm_space = \
                                             PermSpace._create_with_cut_prefix(
                        self.sequence,
                        n_elements=self.n_elements,
                        fixed_map=temp_fixed_map,
                        is_combination=self.is_combination,
                        shit_set=shit_set, perm_type=self.perm_type
                    )

                    wip_perm_number += candidate_sub_perm_space.length
                    if self.is_combination:
                        shit_set.add(lower_value)

            perm_number = wip_perm_number

        #######################################################################
        elif self.is_fixed:
            assert not self.is_degreed and not self.is_recurrent
            free_values_perm_sequence = []
            for i, perm_item in zip(self.domain, perm._perm_sequence):
                if i in self.fixed_map:
                    if self.fixed_map[i] != perm_item:
                        raise ValueError
                else:
                    free_values_perm_sequence.append(perm_item)

            # At this point we know all the items that should be fixed are
            # fixed.

            perm_number = self._free_values_unsliced_perm_space.index(
                free_values_perm_sequence)

        #######################################################################
        elif self.is_combination:
            if perm.is_rapplied:
                return self.unrapplied.index(perm.unrapplied)

            assert not self.is_rapplied and not self.is_recurrent and \
                   not self.is_dapplied and not self.is_fixed and \
                                                            not self.is_degreed

            if not cute_iter_tools.is_sorted(perm._perm_sequence):
                raise ValueError

            processed_perm_sequence = tuple(
                self.sequence_length - 1 - item
                for item in perm._perm_sequence[::-1])
            perm_number = self.unsliced.length - 1 - sum(
                (math_tools.binomial(item, i)
                 for i, item in enumerate(processed_perm_sequence, start=1)),
                0)

        #######################################################################
        else:
            factoradic_number = []
            unused_values = list(self.sequence)
            for i, value in enumerate(perm._perm_sequence):
                index_of_current_number = unused_values.index(value)
                factoradic_number.append(index_of_current_number)
                unused_values.remove(value)
            perm_number = math_tools.from_factoradic(
                factoradic_number +
                [0] * self.n_unused_elements) // math.factorial(
                    self.n_unused_elements)

        #######################################################################

        if perm_number not in self.canonical_slice:
            raise ValueError

        return perm_number - self.canonical_slice.start
Ejemplo n.º 21
0
    def __getitem__(self, i):
        if isinstance(i, (slice, sequence_tools.CanonicalSlice)):
            canonical_slice = sequence_tools.CanonicalSlice(
                i, self.length, offset=self.canonical_slice.start
            )
            return PermSpace(
                self.sequence, domain=self.domain, n_elements=self.n_elements,
                fixed_map=self.fixed_map, degrees=self.degrees,
                is_combination=self.is_combination, slice_=canonical_slice,
                perm_type=self.perm_type
            )
        
        assert isinstance(i, numbers.Integral)
        if i <= -1:
            i += self.length
            
        if not (0 <= i < self.length):
            raise IndexError
        elif self.is_sliced:
            return self.unsliced[i + self.canonical_slice.start]
        elif self.is_dapplied:
            return self.perm_type(self.undapplied[i], perm_space=self)

        #######################################################################
        elif self.is_degreed:
            if self.is_rapplied:
                assert not self.is_recurrent and \
                       not self.is_partial and not self.is_combination and \
                       not self.is_dapplied and not self.is_sliced
                return self.perm_type(map(self.sequence.__getitem__,
                                          self.unrapplied[i]),
                                      perm_space=self)
                
            
            assert not self.is_rapplied and not self.is_recurrent and \
                   not self.is_partial and not self.is_combination and \
                   not self.is_dapplied and not self.is_sliced
            # If that wasn't an example of asserting one's dominance, I don't
            # know what is.
            
            available_values = list(self.free_values)
            wip_perm_sequence_dict = dict(self.fixed_map)
            wip_n_cycles_in_fixed_items = \
                                    self._n_cycles_in_fixed_items_of_just_fixed
            wip_i = i
            for j in self.sequence:
                if j in wip_perm_sequence_dict:
                    continue
                for unused_value in available_values:
                    candidate_perm_sequence_dict = dict(wip_perm_sequence_dict)
                    candidate_perm_sequence_dict[j] = unused_value
                    
                    ### Checking whether we closed a cycle: ###################
                    #                                                         #
                    if j == unused_value:
                        closed_cycle = True
                    else:
                        current = j
                        while True:
                            current = candidate_perm_sequence_dict[current]
                            if current == j:
                                closed_cycle = True
                                break
                            elif current not in candidate_perm_sequence_dict:
                                closed_cycle = False
                                break
                    #                                                         #
                    ### Finished checking whether we closed a cycle. ##########
                    
                    candidate_n_cycles_in_fixed_items = \
                                     wip_n_cycles_in_fixed_items + closed_cycle
                    
                    candidate_fixed_perm_space_length = sum(
                        math_tools.abs_stirling(
                            self.sequence_length -
                                             len(candidate_perm_sequence_dict),
                            self.sequence_length - degree -
                                              candidate_n_cycles_in_fixed_items
                        ) for degree in self.degrees
                    )
                    
                    
                    if wip_i < candidate_fixed_perm_space_length:
                        available_values.remove(unused_value)
                        wip_perm_sequence_dict[j] = unused_value
                        wip_n_cycles_in_fixed_items = \
                                              candidate_n_cycles_in_fixed_items
                        
                        break
                    wip_i -= candidate_fixed_perm_space_length
                else:
                    raise RuntimeError
            assert wip_i == 0
            return self.perm_type((wip_perm_sequence_dict[k] for k in
                                   self.domain), self)
        
        #######################################################################
        elif self.is_recurrent:
            assert not self.is_dapplied and not self.is_degreed and \
                                                             not self.is_sliced
            available_values = list(self.sequence)
            reserved_values = nifty_collections.Bag(self.fixed_map.values())
            wip_perm_sequence_dict = dict(self.fixed_map)
            wip_i = i
            shit_set = set()
            for j in range(self.n_elements):
                if j in self.fixed_map:
                    available_values.remove(self.fixed_map[j])
                    reserved_values[self.fixed_map[j]] -= 1
                    continue
                unused_values = [
                    item for item in
                    nifty_collections.OrderedBag(available_values) -
                    reserved_values if item not in shit_set
                ]
                for unused_value in unused_values:
                    wip_perm_sequence_dict[j] = unused_value
                    
                    candidate_sub_perm_space = \
                                             PermSpace._create_with_cut_prefix(
                        self.sequence,
                        n_elements=self.n_elements,
                        fixed_map=wip_perm_sequence_dict,
                        is_combination=self.is_combination,
                        shit_set=shit_set, perm_type=self.perm_type
                    )
                    
                    if wip_i < candidate_sub_perm_space.length:
                        available_values.remove(unused_value)
                        break
                    else:
                        wip_i -= candidate_sub_perm_space.length
                        if self.is_combination:
                            shit_set.add(wip_perm_sequence_dict[j])
                        del wip_perm_sequence_dict[j]
                else:
                    raise RuntimeError
            assert wip_i == 0
            return self.perm_type(
                dict_tools.get_tuple(wip_perm_sequence_dict, self.domain),
                self
            )
        
        #######################################################################
        elif self.is_fixed:
            free_values_perm = self._free_values_unsliced_perm_space[i]
            free_values_perm_iterator = iter(free_values_perm)
            return self.perm_type(
                tuple(
                    (self._undapplied_fixed_map[m] if
                     (m in self.fixed_indices) else
                     next(free_values_perm_iterator))
                                       for m in range(self.sequence_length)
                ),
                self
            )
        
        #######################################################################
        elif self.is_combination:
            wip_number = self.length - 1 - i
            wip_perm_sequence = []
            for i in range(self.n_elements, 0, -1):
                for j in range(self.sequence_length, i - 2, -1):
                    candidate = math_tools.binomial(j, i)
                    if candidate <= wip_number:
                        wip_perm_sequence.append(
                            self.sequence[-(j+1)]
                        )
                        wip_number -= candidate
                        break
                else:
                    raise RuntimeError
            result = tuple(wip_perm_sequence)
            assert len(result) == self.n_elements
            return self.perm_type(result, self)

        
        #######################################################################
        else:
            factoradic_number = math_tools.to_factoradic(
                i * math.factorial(
                     self.n_unused_elements),
                n_digits_pad=self.sequence_length
            )
            if self.is_partial:
                factoradic_number = factoradic_number[:-self.n_unused_elements]
            unused_numbers = list(self.sequence)
            result = tuple(unused_numbers.pop(factoradic_digit) for
                                         factoradic_digit in factoradic_number)
            assert sequence_tools.get_length(result) == self.n_elements
            return self.perm_type(result, self)