Repository: sympy/sympy
File: sympy/combinatorics/free_groups.py (Python)

Code before:

    def _array_form_from_letter_form(self, letter_form):
        if not letter_form:
            return ()
        array_form = []
        for letter in letter_form:
            if letter.is_Symbol:
                array_form.append((letter, 1))
            else:
                array_form.append((-letter, -1))
        return _reduce_array_form(array_form)

    def __new__(cls, init=(), *, reduced=False, boundary_index=None):
        if not init:
            return tuple.__new__(cls, ())
        if boundary_index is not None:
            init = _reduce_array_form(init, boundary_index=boundary_index)
            return tuple.__new__(cls, init)
        if reduced:
            return tuple.__new__(cls, tuple(init))
        return tuple.__new__(cls, _reduce_array_form(init))

    def new(self, init):
        if isinstance(init, FreeGroupElement):
            return self.__class__(init, reduced=True)
        return self.__class__(init)

    _hash = None

    def __hash__(self):
        _hash = self._hash
        if _hash is None:
            self._hash = _hash = hash((self.group, frozenset(tuple(self))))
        return _hash

    def copy(self):
        return self.new(self)

    @property
    def is_identity(self):
        return not self.array_form

    @property
    def array_form(self):
        """
        SymPy provides two different internal kinds of representation
        of associative words. The first one is called the `array_form`
        which is a tuple containing `tuples` as its elements, where the
        size of each tuple is two. At the first position the tuple
        contains the `symbol-generator`, while at the second position
        of tuple contains the exponent of that generator at the position.

Review comment (suggestion):
I would suggest here to make this two separate methods like `new` and `_new` where the private constructor `_new` assumes that its input is already reduced.

Code after:

    def _array_form_from_letter_form(self, letter_form):
        if not letter_form:
            return ()
        array_form = []
        for letter in letter_form:
            if letter.is_Symbol:
                array_form.append((letter, 1))
            else:
                array_form.append((-letter, -1))
        return _reduce_array_form(array_form)

    def __new__(cls, init=()):
        if isinstance(init, FreeGroupElement):
            return cls._new(tuple(init))
        return cls._new(_reduce_array_form(init))

    @classmethod
    def _new(cls, array_form=()):
        return tuple.__new__(cls, array_form)

    @classmethod
    def _new_reduce_at_boundary(cls, array_form, boundary_index):
        array_form = _reduce_array_form(array_form,
                                        boundary_index=boundary_index)
        return cls._new(array_form)

    _hash = None

    def __hash__(self):
        _hash = self._hash
        if _hash is None:
            self._hash = _hash = hash((self.group, frozenset(tuple(self))))
        return _hash

    def copy(self):
        return self._new(tuple(self))

    @property
    def is_identity(self):
        return not self.array_form

    @property
    def array_form(self):
        """
        SymPy provides two different internal kinds of representation
        of associative words. The first one is called the `array_form`
        which is a tuple containing `tuples` as its elements, where the
        size of each tuple is two. At the first position the tuple
        contains the `symbol-generator`, while at the second position
        of tuple contains the exponent of that generator at the position.