def __getitem__(self, i):
        """
        Index or slice an ETS

        :param i: the index or slince
        :type i: int or slice
        :return: Elementary transform (sequence)
        :rtype: ETS

        Example:

        .. runblock:: pycon

            >>> from roboticstoolbox import ETS
            >>> e = ETS.rz() * ETS.tx(1) * ETS.rz() * ETS.tx(1)
            >>> e[0]
            >>> e[1]
            >>> e[1:3]

        """
        item = ETS()
        data = self.data[i]  # can be [2] or slice, eg. [3:5]
        # ensure that data is always a list
        if isinstance(data, list):
            item.data = data
        else:
            item.data = [data]
        return item
    def pop(self, i=-1):
        """
        Pop value

        :param i: item in the list to pop, default is last
        :type i: int
        :return: the popped value
        :rtype: instance of same type
        :raises IndexError: if there are no values to pop

        Removes a value from the value list and returns it.  The original
        instance is modified.

        Example:

        .. runblock:: pycon

            >>> from roboticstoolbox import ETS
            >>> e = ETS.rz() * ETS.tx(1) * ETS.rz() * ETS.tx(1)
            >>> tail = e.pop()
            >>> tail
            >>> e
        """
        item = ETS()
        item.data = [super().pop(i)]
        return item
    def __mul__(self, rest):
        """
        Overloaded ``*`` operator

        :return: [description]
        :rtype: [type]

        Example:

        .. runblock:: pycon

            >>> from roboticstoolbox import ETS
            >>> e1 = ETS.rz()
            >>> len(e1)
            >>> e2= ETS.tx(2)
            >>> len(e2)
            >>> e = e1 * e2
            >>> len(e)

        .. note:: The ``*`` operator implies composition, but actually the
            result is a new ETS instance that contains the concatenation of
            the left and right operands in an internal list. In this example
            we see the length of the product is 2.
        """
        prod = ETS()
        prod.data = self.data + rest.data
        return prod
    def inv(self):
        r"""
        Inverse of ETS

        :return: [description]
        :rtype: ETS instance

        The inverse of a given ETS.  It is computed as the inverse of the
        individual ETs in the reverse order.

        .. math::

            (\mathbf{E}_0, \mathbf{E}_1 \cdots \mathbf{E}_{n-1} )^{-1} = (\mathbf{E}_{n-1}^{-1}, \mathbf{E}_{n-2}^{-1} \cdots \mathbf{E}_0^{-1}{n-1} )

        Example:

        .. runblock:: pycon

            >>> from roboticstoolbox import ETS
            >>> e = ETS.rz(j=2) * ETS.tx(1) * ETS.rx(j=3,flip=True) * ETS.tx(1)
            >>> print(e)
            >>> print(e.inv())
            >>> q = [1,2,3,4]
            >>> print(e.eval(q) * e.inv().eval(q))

        .. note:: It is essential to use explicit joint indices to account for
            the reversed order of the transforms.
        """  # noqa

        inv = ETS()
        for ns in reversed(self.data):
            # get the namespace from the list

            # clone it, and invert the elements to create an inverse
            nsi = copy.copy(ns)
            if nsi.joint:
                nsi.flip ^= True  # toggle flip status
            elif nsi.axis[0] == 'C':
                nsi.T = trinv(nsi.T)
            elif nsi.eta is not None:
                nsi.T = trinv(nsi.T)
                nsi.eta = -nsi.eta
            et = ETS()  # create a new ETS instance
            et.data = [nsi]  # set it's data from the dict
            inv *= et
        return inv
    def inv(self):
        r"""
        Inverse of ETS

        :return: [description]
        :rtype: ETS instance

        The inverse of a given ETS.  It is computed as the inverse of the
        individual ETs in the reverse order.

        .. math::

            (\mathbf{E}_0, \mathbf{E}_1 \cdots \mathbf{E}_{n-1} )^{-1} = (\mathbf{E}_{n-1}^{-1}, \mathbf{E}_{n-2}^{-1} \cdots \mathbf{E}_0^{-1}{n-1} )

        Example:

        .. runblock:: pycon

            >>> from roboticstoolbox import ETS
            >>> e = ETS.rz(j=2) * ETS.tx(1) * ETS.rx(j=3,flip=True) * ETS.tx(1)
            >>> print(e)
            >>> print(e.inv())
            >>> q = [1,2,3,4]
            >>> print(e.eval(q) * e.inv().eval(q))

        .. note:: It is essential to use explicit joint indices to account for
            the reversed order of the transforms.
        """

        inv = ETS()
        for e in reversed(self.data):
            # get the named tuple from the list, and convert to a dict
            etdict = e._asdict()

            # update the dict to make this an inverse
            if etdict['joint']:
                etdict['flip'] ^= True  # toggle flip status
            elif etdict['axis'][0] == 'C':
                etdict['T'] = trinv(etdict['T'])
            elif etdict['eta'] is not None:
                etdict['T'] = trinv(etdict['T'])
                etdict['eta'] = -etdict['eta']
            et = ETS()  # create a new ETS instance
            et.data = [self.ets_tuple(**etdict)]  # set it's data from the dict
            inv *= et
        return inv
 def __imul__(self, rest):
     prod = ETS()
     prod.data = self.data + rest.data
     return prod