###############################################################################
#
#       Copyright (C) 2011 Simon King <simon.king@uni-jena.de>
#  Distributed under the terms of the GNU General Public License (GPL),
#  version 2 or any later version.  The full text of the GPL is available at:
#                  http://www.gnu.org/licenses/
#
###############################################################################

"""
Generic implementation of one- and two-sided ideals of non-commutative rings.

AUTHOR:

- Simon King (2011-03-21), <simon.king@uni-jena.de>, :trac:`7797`.

EXAMPLES::

    sage: MS = MatrixSpace(ZZ,2,2)
    sage: MS*MS([0,1,-2,3])
    Left Ideal
    (
      [ 0  1]
      [-2  3]
    )
     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
    sage: MS([0,1,-2,3])*MS
    Right Ideal
    (
      [ 0  1]
      [-2  3]
    )
     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
    sage: MS*MS([0,1,-2,3])*MS
    Twosided Ideal
    (
      [ 0  1]
      [-2  3]
    )
     of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

See :mod:`~sage.algebras.letterplace.letterplace_ideal` for a more
elaborate implementation in the special case of ideals in free
algebras.

TESTS::

    sage: A = SteenrodAlgebra(2)
    sage: IL = A*[A.1+A.2,A.1^2]; IL
    Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
    sage: TestSuite(IL).run(skip=['_test_category'],verbose=True)
    running ._test_eq() . . . pass
    running ._test_not_implemented_methods() . . . pass
    running ._test_pickling() . . . pass

"""

from sage.rings.ideal_monoid import IdealMonoid_c
from sage.structure.parent import Parent
from sage.categories.monoids import Monoids
from sage.rings.ideal import Ideal_generic
import sage

class IdealMonoid_nc(IdealMonoid_c):
    """
    Base class for the monoid of ideals over a non-commutative ring.

    .. NOTE::

        This class is essentially the same as
        :class:`~sage.rings.ideal_monoid.IdealMonoid_c`,
        but does not complain about non-commutative rings.

    EXAMPLES::

        sage: MS = MatrixSpace(ZZ,2,2)
        sage: MS.ideal_monoid()
        Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

    """
    def __init__(self, R):
        """
        Initialize ``self``.

        INPUT:

        - ``R`` -- A ring.

        TESTS::

            sage: from sage.rings.noncommutative_ideals import IdealMonoid_nc
            sage: MS = MatrixSpace(ZZ,2,2)
            sage: IdealMonoid_nc(MS)
            Monoid of ideals of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

        """
        self._IdealMonoid_c__R = R
        Parent.__init__(self, base = sage.rings.integer_ring.ZZ,
                        category = Monoids())
        self._populate_coercion_lists_()

    def _element_constructor_(self, x):
        r"""
        Create an ideal in this monoid from ``x``.

        INPUT:

        - ``x`` -- An ideal, or a list of elements.

        TESTS::

            sage: A = SteenrodAlgebra(2) # indirect doctest
            sage: IL = A*[A.1+A.2,A.1^2]; IL
            Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
            sage: IR = [A.1+A.2,A.1^2]*A; IR
            Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
            sage: IT = A*[A.1+A.2,A.1^2]*A; IT
            Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
            sage: M = IL.parent()
            sage: M([A.0, 1])
            Twosided Ideal (Sq(1), 1) of mod 2 Steenrod algebra, milnor basis

        ::

            sage: IL == loads(dumps(IL))
            True
            sage: IR == loads(dumps(IR))
            True
            sage: IT == loads(dumps(IT))
            True

        """
        side = "twosided"
        if isinstance(x, Ideal_nc):
            side = x.side()
            x = x.gens()
        elif isinstance(x, Ideal_generic):
            x = x.gens()
        y = self._IdealMonoid_c__R.ideal(x,side=side)
        y._set_parent(self)
        return y

class Ideal_nc(Ideal_generic):
    """
    Generic non-commutative ideal.

    All fancy stuff such as the computation of Groebner bases must be
    implemented in sub-classes. See :class:`~sage.algebras.letterplace.letterplace_ideal.LetterplaceIdeal`
    for an example.

    EXAMPLES::

        sage: MS = MatrixSpace(QQ,2,2)
        sage: I = MS*[MS.1,MS.2]; I
        Left Ideal
        (
          [0 1]
          [0 0],
        <BLANKLINE>
          [0 0]
          [1 0]
        )
         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
        sage: [MS.1,MS.2]*MS
        Right Ideal
        (
          [0 1]
          [0 0],
        <BLANKLINE>
          [0 0]
          [1 0]
        )
         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
        sage: MS*[MS.1,MS.2]*MS
        Twosided Ideal
        (
          [0 1]
          [0 0],
        <BLANKLINE>
          [0 0]
          [1 0]
        )
         of Full MatrixSpace of 2 by 2 dense matrices over Rational Field

    """
    def __init__(self, ring, gens, coerce=True, side = "twosided"):
        """
        Initialize ``self``.

        INPUT:

        - ``ring`` -- A ring.

        - ``gens`` -- A list or tuple of elements.

        - ``coerce`` (optional bool, default ``True``): First coerce the given
          list of elements into the given ring.

        - ``side`` (option string, default ``"twosided"``): Must be ``"left"``,
          ``"right"`` or ``"twosided"``. Determines whether the ideal is a
          left, right or twosided ideal.

        TESTS::

            sage: MS = MatrixSpace(ZZ,2,2)
            sage: from sage.rings.noncommutative_ideals import Ideal_nc
            sage: Ideal_nc(MS,[MS.1,MS.2], side='left')
            Left Ideal
            (
              [0 1]
              [0 0],
            <BLANKLINE>
              [0 0]
              [1 0]
            )
             of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
            sage: Ideal_nc(MS,[MS.1,MS.2], side='right')
            Right Ideal
            (
              [0 1]
              [0 0],
            <BLANKLINE>
              [0 0]
              [1 0]
            )
             of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

        """
        if side not in ['left','right','twosided']:
            raise ValueError, "Ideals are left, right or twosided, but not %s"%side
        self.__side = side
        Ideal_generic.__init__(self, ring, gens, coerce=coerce)

    def __repr__(self):
        """
        Return a string representation of ``self``.

        TESTS::

            sage: A = SteenrodAlgebra(2)
            sage: A*[A.1+A.2,A.1^2]      # indirect doctest
            Left Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
            sage: [A.1+A.2,A.1^2]*A
            Right Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis
            sage: A*[A.1+A.2,A.1^2]*A
            Twosided Ideal (Sq(2) + Sq(4), Sq(1,1)) of mod 2 Steenrod algebra, milnor basis

        """
        return "%s Ideal %s of %s"%(self.__side.capitalize(),self._repr_short(),self.ring())

    def __cmp__(self, right):
        """
        Ideals of different sidedness do not compare equal. Apart from
        that, the generators are compared.

        EXAMPLES::

             sage: A = SteenrodAlgebra(2)
             sage: IR = [A.1+A.2,A.1^2]*A
             sage: IL = A*[A.1+A.2,A.1^2]
             sage: IT = A*[A.1+A.2,A.1^2]*A
             sage: IT==IL
             False
             sage: IR==[A.1+A.2,A.1^2]*A
             True

        """
        if not isinstance(right,Ideal_nc):
            return -1
        c = cmp(self.side(),right.side())
        if c: return c
        S = set(self.gens())
        T = set(right.gens())
        if S == T:
            return 0
        return cmp(self.gens(), right.gens())

    def side(self):
        """
        Return a string that describes the sidedness of this ideal.

        EXAMPLES::

            sage: A = SteenrodAlgebra(2)
            sage: IL = A*[A.1+A.2,A.1^2]
            sage: IR = [A.1+A.2,A.1^2]*A
            sage: IT = A*[A.1+A.2,A.1^2]*A
            sage: IL.side()
            'left'
            sage: IR.side()
            'right'
            sage: IT.side()
            'twosided'

        """
        return self.__side

    def __mul__(self, other):
        """
        Multiplication of a one-sided ideal with its ring from the other side
        yields a two-sided ideal.

        TESTS::

            sage: MS = MatrixSpace(QQ,2,2)
            sage: IL = MS*[2*MS.0,3*MS.1]; IL
            Left Ideal
            (
              [2 0]
              [0 0],
            <BLANKLINE>
              [0 3]
              [0 0]
            )
             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
            sage: IR = MS.3*MS; IR
            Right Ideal
            (
              [0 0]
              [0 1]
            )
             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
            sage: IL*MS     # indirect doctest
            Twosided Ideal
            (
              [2 0]
              [0 0],
            <BLANKLINE>
              [0 3]
              [0 0]
            )
             of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
            sage: IR*IR
            Traceback (most recent call last):
            ...
            NotImplementedError: Can not multiply non-commutative ideals.

        """
        if not isinstance(other, Ideal_nc):
            # Perhaps other is a ring and thus has its own
            # multiplication.
            if other == self.ring():
                if self.side()=='right':
                    return self
                return self.ring().ideal(self.gens(),side='twosided')
        if not isinstance(self, Ideal_nc):
            # This may happen...
            if self == other.ring():
                if other.side()=='left':
                    return other
                return other.ring().ideal(other.gens(),side='twosided')
        raise NotImplementedError, "Can not multiply non-commutative ideals."
