class AbstractMSumRing(Parent):
    def __init__(self, *args):
        if len(args) == 1 and isinstance(args[0], AbstractMSumRing):
            self._laurent_polynomial_ring = args[0]._laurent_polynomial_ring
            self._free_module = args[0]._free_module
            self._laurent_polynomial_ring_extra_var = args[0]._laurent_polynomial_ring_extra_var
        else:
            from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
            from sage.modules.free_module import FreeModule
            self._laurent_polynomial_ring = LaurentPolynomialRing(QQ, *args)
            dim = ZZ(self._laurent_polynomial_ring.ngens())
            self._free_module = FreeModule(ZZ, dim)

            # univariate extension of the polynomial ring
            # (needed in several algorithms)
            self._laurent_polynomial_ring_extra_var = self._laurent_polynomial_ring['EXTRA_VAR']

        Parent.__init__(self, category=Rings())

    def ngens(self):
        return self.laurent_polynomial_ring().ngens()

    def free_module(self):
        return self._free_module

    def polynomial_ring(self):
        raise ValueError

    def polynomial_ring_extra_var(self):
        raise ValueError

    def laurent_polynomial_ring(self):
        return self._laurent_polynomial_ring

    def laurent_polynomial_ring_extra_var(self):
        return self._laurent_polynomial_ring_extra_var

    def with_extra_var(self):
        return self['EXTRA_VAR']

    @cached_method
    def zero(self):
        r"""
        EXAMPLES::

            sage: from surface_dynamics.misc.multivariate_generating_series import MultivariateGeneratingSeriesRing

            sage: M = MultivariateGeneratingSeriesRing('x', 2)
            sage: M.zero()
            0
            sage: M.zero().parent() is M
            True
            sage: M.zero().is_zero()
            True
        """
        return self._element_constructor_(QQ.zero())

    @cached_method
    def one(self):
        r"""
        EXAMPLES::

            sage: from surface_dynamics.misc.multivariate_generating_series import MultivariateGeneratingSeriesRing

            sage: M = MultivariateGeneratingSeriesRing('x', 2)
            sage: M.zero()
            0
            sage: M.zero().parent() is M
            True
            sage: M.one().is_one()
            True
        """
        return self._element_constructor_(QQ.one())

    def term(self, num, den):
        r"""
        Return the term ``num / den``.

        INPUT:

        - ``num`` - a Laurent polynomial

        - ``den`` - a list of pairs ``(vector, power)`` or a dictionary
           whose keys are the vectors and the values the powers. The
           vector ``v = (v_0, v_1, \ldots)`` with power ``n`` corresponds
           to the factor `(1 - x_0^{v_0} x_1^{v_1} \ldots x_k^{v_k})^n`.

        EXAMPLES::

            sage: from surface_dynamics.misc.multivariate_generating_series import MultivariateGeneratingSeriesRing

            sage: M = MultivariateGeneratingSeriesRing('x', 3)
            sage: M.term(1, [([1,1,0],1),([1,0,-1],2)])
            (1)/((1 - x0*x2^-1)^2*(1 - x0*x1))
            sage: M.term(1, {(1,1,0): 1, (1,0,-1): 2})
            (1)/((1 - x0*x2^-1)^2*(1 - x0*x1))
        """
        return self.element_class(self, [(den, num)])

    def _element_constructor_(self, arg):
        r"""
        TESTS::

            sage: from surface_dynamics.misc.multivariate_generating_series import MultivariateGeneratingSeriesRing

            sage: M = MultivariateGeneratingSeriesRing('x', 2)
            sage: M(1)
            (1)

            sage: R = M.laurent_polynomial_ring()
            sage: M(R.0)
            (x0)
        """
        num = self._laurent_polynomial_ring(arg)
        return self.element_class(self, [([], num)])