r"""
Free abelian monoids

AUTHORS:

- David Kohel (2005-09)

Sage supports free abelian monoids on any prescribed finite number
`n\geq 0` of generators. Use the
``FreeAbelianMonoid`` function to create a free abelian
monoid, and the ``gen`` and ``gens``
functions to obtain the corresponding generators. You can print the
generators as arbitrary strings using the optional
``names`` argument to the
``FreeAbelianMonoid`` function.

EXAMPLE 1: It is possible to create an abelian monoid in zero or
more variables; the syntax T(1) creates the monoid identity
element even in the rank zero case.

::

    sage: T = FreeAbelianMonoid(0, '')
    sage: T
    Free abelian monoid on 0 generators ()
    sage: T.gens()
    ()
    sage: T(1)
    1

EXAMPLE 2: A free abelian monoid uses a multiplicative
representation of elements, but the underlying representation is
lists of integer exponents.

::

    sage: F = FreeAbelianMonoid(5,names='a,b,c,d,e')
    sage: (a,b,c,d,e) = F.gens()
    sage: a*b^2*e*d
    a*b^2*d*e
    sage: x = b^2*e*d*a^7
    sage: x
    a^7*b^2*d*e
    sage: x.list()
    [7, 2, 0, 1, 1]
"""

#*****************************************************************************
#  Copyright (C) 2005 David Kohel <kohel@maths.usyd.edu>
#
#  Distributed under the terms of the GNU General Public License (GPL):
#
#                  http://www.gnu.org/licenses/
#*****************************************************************************


from sage.structure.parent_gens import ParentWithGens, normalize_names
from free_abelian_monoid_element import FreeAbelianMonoidElement
from sage.rings.integer import Integer
from sage.rings.all import ZZ

from sage.structure.factory import UniqueFactory
from sage.misc.decorators import rename_keyword

class FreeAbelianMonoidFactory(UniqueFactory):
    """
    Create the free abelian monoid in `n` generators.

    INPUT:


    -  ``n`` - integer

    -  ``names`` - names of generators


    OUTPUT: free abelian monoid

    EXAMPLES::

        sage: FreeAbelianMonoid(0, '')
        Free abelian monoid on 0 generators ()
        sage: F = FreeAbelianMonoid(5,names = list("abcde"))
        sage: F
        Free abelian monoid on 5 generators (a, b, c, d, e)
        sage: F(1)
        1
        sage: (a, b, c, d, e) = F.gens()
        sage: mul([ a, b, a, c, b, d, c, d ], F(1))
        a^2*b^2*c^2*d^2
        sage: a**2 * b**3 * a**2 * b**4
        a^4*b^7

    ::

        sage: loads(dumps(F)) is F
        True
    """
    def create_key(self, n, names):
        n = int(n)
        names = normalize_names(n, names)
        return (n, names)
    def create_object(self, version, key):
        return FreeAbelianMonoid_class(*key)

FreeAbelianMonoid_factory = FreeAbelianMonoidFactory("sage.monoids.free_abelian_monoid.FreeAbelianMonoid_factory")

@rename_keyword(deprecation=15289, n="index_set")
def FreeAbelianMonoid(index_set=None, names=None, **kwds):
    """
    Return a free abelian monoid on `n` generators or with the generators
    indexed by a set `I`.

    We construct free abelian monoids by specifing either:

    - the number of generators and/or the names of the generators
    - the indexing set for the generators (this ignores the other two inputs)

    INPUT:

    - ``index_set`` -- an indexing set for the generators; if an integer,
      then this becomes `\{0, 1, \ldots, n-1\}`

    -  ``names`` -- names of generators

    OUTPUT:

    A free abelian monoid.

    EXAMPLES::

        sage: F.<a,b,c,d,e> = FreeAbelianMonoid(); F
        Free abelian monoid on 5 generators (a, b, c, d, e)
        sage: FreeAbelianMonoid(index_set=ZZ)
        Free abelian monoid indexed by Integer Ring
    """
    if isinstance(index_set, str): # Swap args (this works if names is None as well)
        names, index_set = index_set, names

    if index_set is None and names is not None:
        if isinstance(names, str):
            index_set = names.count(',')
        else:
            index_set = len(names)

    if index_set not in ZZ:
        if names is not None:
            names = normalize_names(len(names), names)
        from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid
        return IndexedFreeAbelianMonoid(index_set, names=names, **kwds)

    if names is None:
        raise ValueError("names must be specified")
    return FreeAbelianMonoid_factory(index_set, names)

def is_FreeAbelianMonoid(x):
    """
    Return True if `x` is a free abelian monoid.

    EXAMPLES::

        sage: from sage.monoids.free_abelian_monoid import is_FreeAbelianMonoid
        sage: is_FreeAbelianMonoid(5)
        False
        sage: is_FreeAbelianMonoid(FreeAbelianMonoid(7,'a'))
        True
        sage: is_FreeAbelianMonoid(FreeMonoid(7,'a'))
        False
        sage: is_FreeAbelianMonoid(FreeMonoid(0,''))
        False
    """
    return isinstance(x, FreeAbelianMonoid_class)

class FreeAbelianMonoid_class(ParentWithGens):
    """
    Free abelian monoid on `n` generators.
    """
    Element = FreeAbelianMonoidElement

    def __init__(self, n, names):
        if not isinstance(n, (int, long, Integer)):
            raise TypeError("n (=%s) must be an integer."%n)
        if n < 0:
            raise ValueError("n (=%s) must be nonnegative."%n)
        self.__ngens = int(n)
        assert not names is None
        self._assign_names(names)

    def __repr__(self):
        n = self.__ngens
        return "Free abelian monoid on %s generators %s"%(n,self.gens())

    def __call__(self, x):
        """
        Create an element of this abelian monoid from `x`.

        EXAMPLES::

            sage: F = FreeAbelianMonoid(10,'x')
            sage: F(F.gen(2))
            x2
            sage: F(1)
            1
        """
        if isinstance(x, FreeAbelianMonoidElement) and x.parent() == self:
            return x
        return FreeAbelianMonoidElement(self, x)


    def __contains__(self, x):
        """
        Return True if `x` is an element of this abelian monoid.

        EXAMPLES::

            sage: F = FreeAbelianMonoid(10,'b')
            sage: F.gen(2)*F.gen(3) in F
            True

        Note that a monoid on `9` generators is not considered a
        submonoid of one on `10` generators.

        ::

            sage: FreeAbelianMonoid(9,'c').gen(2) in F
            False

        However, multiple calls to the monoid constructor do *not* return
        multiple distinct monoids.

        ::

            sage: FreeAbelianMonoid(10,'b').gen(2) in F
            True
        """
        return isinstance(x, FreeAbelianMonoidElement) and x.parent() == self

    def gen(self, i=0):
        """
        The `i`-th generator of the abelian monoid.

        EXAMPLES::

            sage: F = FreeAbelianMonoid(5,'a')
            sage: F.gen(0)
            a0
            sage: F.gen(2)
            a2
        """
        n = self.__ngens
        if i < 0 or not i < n:
            raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1))
        x = [ 0 for j in range(n) ]
        x[int(i)] = 1
        return self.Element(self,x)

    def ngens(self):
        """
        The number of free generators of the abelian monoid.

        EXAMPLES::

            sage: F = FreeAbelianMonoid(3000, 'a')
            sage: F.ngens()
            3000
        """
        return self.__ngens

    def cardinality(self):
        r"""
        Return the cardinality of ``self``, which is `\infty`.

        EXAMPLES::

            sage: F = FreeAbelianMonoid(3000, 'a')
            sage: F.cardinality()
            +Infinity
        """
        if self.__ngens == 0:
            from sage.rings.all import ZZ
            return ZZ.one()
        from sage.rings.infinity import infinity
        return infinity

