r"""
Examples of magmas
"""
# ****************************************************************************
#       Copyright (C) 2020 Markus Wageringel <markus.wageringel+gh@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#                  https://www.gnu.org/licenses/
# ****************************************************************************

from sage.misc.cachefunc import cached_method
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.element_wrapper import ElementWrapper
from sage.categories.all import Magmas
from sage.sets.family import Family


class FreeMagma(UniqueRepresentation, Parent):
    r"""
    An example of magma.

    The purpose of this class is to provide a minimal template for
    implementing a magma.

    EXAMPLES::

        sage: M = Magmas().example(); M
        An example of a magma: the free magma generated by ('a', 'b', 'c', 'd')

    This is the free magma generated by::

        sage: M.magma_generators()
        Family ('a', 'b', 'c', 'd')
        sage: a, b, c, d = M.magma_generators()

    and with a non-associative product given by::

        sage: a * (b * c) * (d * a * b)
        '((a*(b*c))*((d*a)*b))'
        sage: a * (b * c) == (a * b) * c
        False

    TESTS::

        sage: TestSuite(M).run()
    """

    def __init__(self, alphabet=('a', 'b', 'c', 'd')):
        r"""
        The free magma.

        INPUT:

        - ``alphabet`` -- a tuple of strings; the generators of the magma

        EXAMPLES::

            sage: from sage.categories.examples.magmas import FreeMagma
            sage: F = FreeMagma(('a', 'b', 'c')); F
            An example of a magma: the free magma generated by ('a', 'b', 'c')

        TESTS::

            sage: F == loads(dumps(F))
            True
        """
        if any('(' in x or ')' in x or '*' in x for x in alphabet):
            raise ValueError("alphabet must not contain characters "
                             "'(', ')' or '*'")
        self.alphabet = alphabet
        Parent.__init__(self, category=Magmas().FinitelyGenerated())

    def _repr_(self):
        r"""
        EXAMPLES::

            sage: from sage.categories.examples.magmas import FreeMagma
            sage: FreeMagma(('a', 'b', 'c'))._repr_()
            "An example of a magma: the free magma generated by ('a', 'b', 'c')"
        """
        return ("An example of a magma: the free magma generated by %s"
                % (self.alphabet,))

    def product(self, x, y):
        r"""
        Return the product of ``x`` and ``y`` in the magma, as per
        :meth:`Magmas.ParentMethods.product`.

        EXAMPLES::

            sage: F = Magmas().example()
            sage: F('a') * F.an_element()
            '(a*(((a*b)*c)*d))'
        """
        assert x in self
        assert y in self
        return self("(%s*%s)" % (x.value, y.value))

    @cached_method
    def magma_generators(self):
        r"""
        Return the generators of the magma.

        EXAMPLES::

            sage: F = Magmas().example()
            sage: F.magma_generators()
            Family ('a', 'b', 'c', 'd')
        """
        return Family([self(i) for i in self.alphabet])

    def an_element(self):
        r"""
        Return an element of the magma.

        EXAMPLES::

            sage: F = Magmas().example()
            sage: F.an_element()
            '(((a*b)*c)*d)'
        """
        gens = self.magma_generators()
        x = gens.first()
        for y in gens.iterator_range(1):
            x *= y
        return x

    def _element_constructor_(self, x):
        r"""
        Construct an element of this magma from the data ``x``.

        INPUT:

        - ``x`` -- a string

        EXAMPLES::

            sage: F = Magmas().example(); F
            An example of a magma: the free magma generated by ('a', 'b', 'c', 'd')
            sage: F._element_constructor_('a')
            'a'
            sage: F._element_constructor_('(a*(b*c))')
            '(a*(b*c))'
        """
        return self.element_class(self, x)

    class Element(ElementWrapper):
        r"""
        The class for elements of the free magma.
        """
        wrapped_class = str


Example = FreeMagma
