# -*- coding: utf-8 -*-
r"""
Clifford Algebras

AUTHORS:

- Travis Scrimshaw (2013-09-06): Initial version
"""

#*****************************************************************************
#       Copyright (C) 2013 Travis Scrimshaw <tscrim at ucdavis.edu>
#
# 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.
#                  http://www.gnu.org/licenses/
#*****************************************************************************

from sage.misc.cachefunc import cached_method
from sage.structure.unique_representation import UniqueRepresentation
from copy import copy

from sage.categories.algebras_with_basis import AlgebrasWithBasis
from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis
from sage.modules.with_basis.morphism import ModuleMorphismByLinearity
from sage.categories.poor_man_map import PoorManMap
from sage.rings.all import ZZ
from sage.modules.free_module import FreeModule, FreeModule_generic
from sage.matrix.constructor import Matrix
from sage.matrix.args import MatrixArgs
from sage.sets.family import Family
from sage.combinat.free_module import CombinatorialFreeModule
from sage.combinat.subset import SubsetsSorted
from sage.quadratic_forms.quadratic_form import QuadraticForm
from sage.algebras.weyl_algebra import repr_from_monomials
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
from sage.typeset.ascii_art import ascii_art
from sage.typeset.unicode_art import unicode_art
import unicodedata


class CliffordAlgebraElement(CombinatorialFreeModule.Element):
    """
    An element in a Clifford algebra.

    TESTS::

        sage: Q = QuadraticForm(ZZ, 3, [1, 2, 3, 4, 5, 6])
        sage: Cl.<x,y,z> = CliffordAlgebra(Q)
        sage: elt = ((x^3-z)*x + y)^2
        sage: TestSuite(elt).run()
    """
    def _repr_(self):
        """
        Return a string representation of ``self``.

        TESTS::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: ((x^3-z)*x + y)^2
            -2*x*y*z - x*z + 5*x - 4*y + 2*z + 2
            sage: Cl.zero()
            0
        """
        return repr_from_monomials(self.list(), self.parent()._repr_term)

    def _latex_(self):
        r"""
        Return a `\LaTeX` representation of ``self``.

        TESTS::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: latex( ((x^3-z)*x + y)^2 )
            -2  x y z -  x z + 5  x - 4  y + 2  z + 2
            sage: Cl.<x0,x1,x2> = CliffordAlgebra(Q)
            sage: latex(  (x1 - x2)*x0 + 5*x0*x1*x2 )
            5  x_{0} x_{1} x_{2} -  x_{0} x_{1} +  x_{0} x_{2} - 1
        """
        return repr_from_monomials(self.list(), self.parent()._latex_term, True)

    def _mul_(self, other):
        """
        Return ``self`` multiplied by ``other``.

        INPUT:

        - ``other`` -- element of the same Clifford algebra as ``self``

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: (x^3 - z*y)*x*(y*z + x*y*z)
            x*y*z + y*z - 24*x + 12*y + 2*z - 24
            sage: y*x
            -x*y + 2
            sage: z*x
            -x*z + 3
            sage: z*z
            6
            sage: x*0
            0
            sage: 0*x
            0
        """
        Q = self.parent()._quadratic_form
        zero = self.parent().base_ring().zero()
        d = {}

        for ml,cl in self:
            # Distribute the current term ``cl`` * ``ml`` over ``other``.
            cur = copy(other._monomial_coefficients) # The current distribution of the term
            for i in reversed(ml):
                # Distribute the current factor ``e[i]`` (the ``i``-th
                # element of the standard basis).
                next = {}
                # At the end of the following for-loop, ``next`` will be
                # the dictionary describing the element
                # ``e[i]`` * (the element described by the dictionary ``cur``)
                # (where ``e[i]`` is the ``i``-th standard basis vector).
                for mr,cr in cur.items():
                    # Commute the factor as necessary until we are in order
                    pos = 0
                    for j in mr:
                        if i <= j:
                            break
                        # Add the additional term from the commutation
                        t = list(mr)
                        t.pop(pos)
                        t = tuple(t)
                        next[t] = next.get(t, zero) + cr * Q[i,j]
                        # Note: ``Q[i,j] == Q(e[i]+e[j]) - Q(e[i]) - Q(e[j])`` for
                        # ``i != j``, where ``e[k]`` is the ``k``-th standard
                        # basis vector.
                        cr = -cr
                        if next[t] == zero:
                            del next[t]
                        pos += 1

                    # Check to see if we have a squared term or not
                    t = list(mr)
                    if i in t:
                        t.remove(i)
                        cr *= Q[i,i]
                        # Note: ``Q[i,i] == Q(e[i])`` where ``e[i]`` is the
                        # ``i``-th standard basis vector.
                    else:
                        t.insert(pos, i)
                        # Note that ``t`` is now sorted.
                    t = tuple(t)
                    next[t] = next.get(t, zero) + cr
                    if next[t] == zero:
                        del next[t]
                cur = next

            # Add the distributed terms to the total
            for index,coeff in cur.items():
                d[index] = d.get(index, zero) + cl * coeff
                if d[index] == zero:
                    del d[index]

        return self.__class__(self.parent(), d)

    def list(self):
        """
        Return the list of monomials and their coefficients in ``self``
        (as a list of `2`-tuples, each of which has the form
        ``(monomial, coefficient)``).

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: elt = 5*x + y
            sage: elt.list()
            [((0,), 5), ((1,), 1)]
        """
        return sorted(self._monomial_coefficients.items(), key=lambda m_c : (-len(m_c[0]), m_c[0]))

    def support(self):
        """
        Return the support of ``self``.

        This is the list of all monomials which appear with nonzero
        coefficient in ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: elt = 5*x + y
            sage: elt.support()
            [(0,), (1,)]
        """
        return sorted(self._monomial_coefficients.keys(), key=lambda x: (-len(x), x))

    def reflection(self):
        r"""
        Return the image of the reflection automorphism on ``self``.

        The *reflection automorphism* of a Clifford algebra is defined
        as the linear endomorphism of this algebra which maps

        .. MATH::

            x_1 \wedge x_2 \wedge \cdots \wedge x_m \mapsto
            (-1)^m x_1 \wedge x_2 \wedge \cdots \wedge x_m.

        It is an algebra automorphism of the Clifford algebra.

        :meth:`degree_negation` is an alias for :meth:`reflection`.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: elt = 5*x + y + x*z
            sage: r = elt.reflection(); r
            x*z - 5*x - y
            sage: r.reflection() == elt
            True

        TESTS:

        We check that the reflection is an involution::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: all(x.reflection().reflection() == x for x in Cl.basis())
            True
        """
        return self.__class__(self.parent(), {m: (-1)**len(m) * c for m,c in self})

    degree_negation = reflection

    def transpose(self):
        r"""
        Return the transpose of ``self``.

        The transpose is an anti-algebra involution of a Clifford algebra
        and is defined (using linearity) by

        .. MATH::

            x_1 \wedge x_2 \wedge \cdots \wedge x_m \mapsto
            x_m \wedge \cdots \wedge x_2 \wedge x_1.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: elt = 5*x + y + x*z
            sage: t = elt.transpose(); t
            -x*z + 5*x + y + 3
            sage: t.transpose() == elt
            True
            sage: Cl.one().transpose()
            1

        TESTS:

        We check that the transpose is an involution::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: all(x.transpose().transpose() == x for x in Cl.basis())
            True

        Zero is sent to zero::

            sage: Cl.zero().transpose() == Cl.zero()
            True
        """
        P = self.parent()
        if not self._monomial_coefficients:
            return P.zero()
        g = P.gens()
        return P.sum(c * P.prod(g[i] for i in reversed(m)) for m,c in self)

    def conjugate(self):
        r"""
        Return the Clifford conjugate of ``self``.

        The Clifford conjugate of an element `x` of a Clifford algebra is
        defined as

        .. MATH::

            \bar{x} := \alpha(x^t) = \alpha(x)^t

        where `\alpha` denotes the :meth:`reflection <reflection>`
        automorphism and `t` the :meth:`transposition <transpose>`.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: elt = 5*x + y + x*z
            sage: c = elt.conjugate(); c
            -x*z - 5*x - y + 3
            sage: c.conjugate() == elt
            True

        TESTS:

        We check that the conjugate is an involution::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: all(x.conjugate().conjugate() == x for x in Cl.basis())
            True
        """
        return self.reflection().transpose()

    clifford_conjugate = conjugate

    # TODO: This is a general function which should be moved to a
    #   superalgebras category when one is implemented.
    def supercommutator(self, x):
        r"""
        Return the supercommutator of ``self`` and ``x``.

        Let `A` be a superalgebra. The *supercommutator* of homogeneous
        elements `x, y \in A` is defined by

        .. MATH::

            [x, y\} = x y - (-1)^{|x| |y|} y x

        and extended to all elements by linearity.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: a = x*y - z
            sage: b = x - y + y*z
            sage: a.supercommutator(b)
            -5*x*y + 8*x*z - 2*y*z - 6*x + 12*y - 5*z
            sage: a.supercommutator(Cl.one())
            0
            sage: Cl.one().supercommutator(a)
            0
            sage: Cl.zero().supercommutator(a)
            0
            sage: a.supercommutator(Cl.zero())
            0

            sage: Q = QuadraticForm(ZZ, 2, [-1,1,-3])
            sage: Cl.<x,y> = CliffordAlgebra(Q)
            sage: [a.supercommutator(b) for a in Cl.basis() for b in Cl.basis()]
            [0, 0, 0, 0, 0, -2, 1, -x - 2*y, 0, 1,
             -6, 6*x + y, 0, x + 2*y, -6*x - y, 0]
            sage: [a*b-b*a for a in Cl.basis() for b in Cl.basis()]
            [0, 0, 0, 0, 0, 0, 2*x*y - 1, -x - 2*y, 0,
             -2*x*y + 1, 0, 6*x + y, 0, x + 2*y, -6*x - y, 0]

        Exterior algebras inherit from Clifford algebras, so
        supercommutators work as well. We verify the exterior algebra
        is supercommutative::

            sage: E.<x,y,z,w> = ExteriorAlgebra(QQ)
            sage: all(b1.supercommutator(b2) == 0
            ....:     for b1 in E.basis() for b2 in E.basis())
            True
        """
        P = self.parent()
        ret = P.zero()
        for ms,cs in self:
            for mx,cx in x:
                ret += P.term(ms, cs) * P.term(mx, cx)
                s = (-1)**(P.degree_on_basis(ms) * P.degree_on_basis(mx))
                ret -= s * P.term(mx, cx) * P.term(ms, cs)
        return ret

class CliffordAlgebra(CombinatorialFreeModule):
    r"""
    The Clifford algebra of a quadratic form.

    Let `Q : V \to \mathbf{k}` denote a quadratic form on a vector space `V`
    over a field `\mathbf{k}`. The Clifford algebra `Cl(V, Q)` is defined as
    `T(V) / I_Q` where `T(V)` is the tensor algebra of `V` and `I_Q` is the
    two-sided ideal generated by all elements of the form `v \otimes v - Q(v)`
    for all `v \in V`.

    We abuse notation to denote the projection of a pure tensor
    `x_1 \otimes x_2 \otimes \cdots \otimes x_m \in T(V)` onto
    `T(V) / I_Q = Cl(V, Q)` by `x_1 \wedge x_2 \wedge \cdots \wedge x_m`.
    This is motivated by the fact that `Cl(V, Q)` is the exterior algebra
    `\wedge V` when `Q = 0` (one can also think of a Clifford algebra as
    a quantization of the exterior algebra). See :class:`ExteriorAlgebra`
    for the concept of an exterior algebra.

    From the definition, a basis of `Cl(V, Q)` is given by monomials of
    the form

    .. MATH::

        \{ e_{i_1} \wedge \cdots \wedge e_{i_k} \mid 1 \leq i_1 < \cdots <
        i_k \leq n \},

    where `n = \dim(V)` and where `\{ e_1, e_2, \cdots, e_n \}` is any
    fixed basis of `V`. Hence

    .. MATH::

        \dim(Cl(V, Q)) = \sum_{k=0}^n \binom{n}{k} = 2^n.

    .. NOTE::

        The algebra `Cl(V, Q)` is a `\ZZ / 2\ZZ`-graded algebra, but not
        (in general) `\ZZ`-graded (in a reasonable way).

    This construction satisfies the following universal property. Let
    `i : V \to Cl(V, Q)` denote the natural inclusion (which is an
    embedding). Then for every associative `\mathbf{k}`-algebra `A`
    and any `\mathbf{k}`-linear map `j : V \to A` satisfying

    .. MATH::

        j(v)^2 = Q(v) \cdot 1_A

    for all `v \in V`, there exists a unique `\mathbf{k}`-algebra
    homomorphism `f : Cl(V, Q) \to A` such that `f \circ i = j`.
    This property determines the Clifford algebra uniquely up to
    canonical isomorphism. The inclusion `i` is commonly used to
    identify `V` with a vector subspace of `Cl(V)`.

    The Clifford algebra `Cl(V, Q)` is a `\ZZ_2`-graded algebra
    (where `\ZZ_2 = \ZZ / 2 \ZZ`); this grading is determined by
    placing all elements of `V` in degree `1`. It is also an
    `\NN`-filtered algebra, with the filtration too being defined
    by placing all elements of `V` in degree `1`. The :meth:`degree` gives
    the `\NN`-*filtration* degree, and to get the super degree use instead
    :meth:`~sage.categories.super_modules.SuperModules.ElementMethods.is_even_odd`.

    The Clifford algebra also can be considered as a covariant functor
    from the category of vector spaces equipped with quadratic forms
    to the category of algebras. In fact, if `(V, Q)` and `(W, R)`
    are two vector spaces endowed with quadratic forms, and if
    `g : W \to V` is a linear map preserving the quadratic form,
    then we can define an algebra morphism
    `Cl(g) : Cl(W, R) \to Cl(V, Q)` by requiring that it send every
    `w \in W` to `g(w) \in V`. Since the quadratic form `R` on `W`
    is uniquely determined by the quadratic form `Q` on `V` (due to
    the assumption that `g` preserves the quadratic form), this fact
    can be rewritten as follows: If `(V, Q)` is a vector space with a
    quadratic form, and `W` is another vector space, and
    `\phi : W \to V` is any linear map, then we obtain an algebra
    morphism `Cl(\phi) : Cl(W, \phi(Q)) \to Cl(V, Q)` where
    `\phi(Q) = \phi^T \cdot Q \cdot \phi` (we consider `\phi` as a
    matrix) is the quadratic form `Q` pulled back to `W`. In fact, the
    map `\phi` preserves the quadratic form because of

    .. MATH::

        \phi(Q)(x) = x^T \cdot \phi^T \cdot Q \cdot \phi \cdot x
        = (\phi \cdot x)^T \cdot Q \cdot (\phi \cdot x) = Q(\phi(x)).

    Hence we have `\phi(w)^2 = Q(\phi(w)) = \phi(Q)(w)` for all `w \in W`.

    REFERENCES:

    - :wikipedia:`Clifford_algebra`

    INPUT:

    - ``Q`` -- a quadratic form
    - ``names`` -- (default: ``'e'``) the generator names

    EXAMPLES:

    To create a Clifford algebra, all one needs to do is specify a
    quadratic form::

        sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
        sage: Cl = CliffordAlgebra(Q)
        sage: Cl
        The Clifford algebra of the Quadratic form in 3 variables
         over Integer Ring with coefficients:
        [ 1 2 3 ]
        [ * 4 5 ]
        [ * * 6 ]

    We can also explicitly name the generators. In this example, the
    Clifford algebra we construct is an exterior algebra (since we
    choose the quadratic form to be zero)::

        sage: Q = QuadraticForm(ZZ, 4, [0]*10)
        sage: Cl.<a,b,c,d> = CliffordAlgebra(Q)
        sage: a*d
        a*d
        sage: d*c*b*a + a + 4*b*c
        a*b*c*d + 4*b*c + a
    """
    @staticmethod
    def __classcall_private__(cls, Q, names=None):
        """
        Normalize arguments to ensure a unique representation.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl1.<e0,e1,e2> = CliffordAlgebra(Q)
            sage: Cl2 = CliffordAlgebra(Q)
            sage: Cl3 = CliffordAlgebra(Q, ['e0','e1','e2'])
            sage: Cl1 is Cl2 and Cl2 is Cl3
            True
        """
        if not isinstance(Q, QuadraticForm):
            raise ValueError("{} is not a quadratic form".format(Q))
        if names is None:
            names = 'e'
        names = tuple(names)
        if len(names) != Q.dim():
            if len(names) == 1:
                names = tuple( '{}{}'.format(names[0], i) for i in range(Q.dim()) )
            else:
                raise ValueError("the number of variables does not match the number of generators")
        return super(CliffordAlgebra, cls).__classcall__(cls, Q, names)

    def __init__(self, Q, names, category=None):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl = CliffordAlgebra(Q)
            sage: Cl.category()
            Category of finite dimensional super algebras with basis over
             (euclidean domains and infinite enumerated sets and metric spaces)
            sage: TestSuite(Cl).run()

        TESTS:

        We check that the basis elements are indeed indexed by
        *strictly increasing* tuples::

            sage: Q = QuadraticForm(ZZ, 9)
            sage: Cl = CliffordAlgebra(Q)
            sage: ba = Cl.basis().keys()
            sage: all( tuple(sorted(S)) in ba
            ....:      for S in Subsets(range(9)) )
            True
        """
        self._quadratic_form = Q
        R = Q.base_ring()
        category = AlgebrasWithBasis(R.category()).Super().Filtered().FiniteDimensional().or_subcategory(category)
        indices = SubsetsSorted(range(Q.dim()))
        CombinatorialFreeModule.__init__(self, R, indices, category=category)
        self._assign_names(names)

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

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: CliffordAlgebra(Q)
            The Clifford algebra of the Quadratic form in 3 variables
             over Integer Ring with coefficients:
            [ 1 2 3 ]
            [ * 4 5 ]
            [ * * 6 ]
        """
        return "The Clifford algebra of the {}".format(self._quadratic_form)

    def _repr_term(self, m):
        """
        Return a string representation of the basis element indexed by ``m``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl._repr_term((0,2))
            'x*z'
            sage: Cl._repr_term(())
            '1'
            sage: Cl._repr_term((1,))
            'y'
        """
        if not m:
            return '1'
        term = ''
        for i in m:
            if term:
                term += '*'
            term += self.variable_names()[i]
        return term

    def _latex_term(self, m):
        r"""
        Return a `\LaTeX` representation of the basis element indexed
        by ``m``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl._latex_term((0,2))
            ' x z'
        """
        if not m:
            return '1'
        term = ''
        for i in m:
            term += ' ' + self.latex_variable_names()[i]
        return term

    def _coerce_map_from_(self, V):
        """
        Return if there is a coerce map from ``V`` into ``self``.

        The things which coerce into ``self`` are:

        - Clifford algebras with the same generator names and an equal
          quadratic form over a ring which coerces into the base
          ring of ``self``.
        - The underlying free module of ``self``.
        - The base ring of ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Qp = QuadraticForm(QQ, 3, [1,2,3,4,5,6])
            sage: Cl = CliffordAlgebra(Q)
            sage: Clp = CliffordAlgebra(Qp)
            sage: Cl.has_coerce_map_from(Clp)
            False
            sage: Clp.has_coerce_map_from(Cl)
            True

        Check that we preserve the multiplicative structure::

            sage: all(Clp(b)*Clp(b) == Clp(b*b) for b in Cl.basis())
            True

        Check from the underlying free module::

            sage: M = ZZ^3
            sage: Mp = QQ^3
            sage: Cl.has_coerce_map_from(M)
            True
            sage: Cl.has_coerce_map_from(Mp)
            False
            sage: Clp.has_coerce_map_from(M)
            True
            sage: Clp.has_coerce_map_from(Mp)
            True

        Names matter::

            sage: Cln = CliffordAlgebra(Q, names=['x','y','z'])
            sage: Cln.has_coerce_map_from(Cl)
            False
            sage: Cl.has_coerce_map_from(Cln)
            False

        Non-injective homomorphisms of base rings don't cause zero
        values in the coordinate dictionary (this had to be manually
        ensured)::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Qp = QuadraticForm(Integers(3), 3, [1,2,3,4,5,6])
            sage: Cl = CliffordAlgebra(Q)
            sage: Clp = CliffordAlgebra(Qp)
            sage: a = Cl.basis()[(1,2)]
            sage: a
            e1*e2
            sage: Clp(a) # so far so good
            e1*e2
            sage: Clp(3*a) # but now
            0
            sage: Clp(3*a) == 0
            True
            sage: b = Cl.basis()[(0,2)]
            sage: Clp(3*a-4*b)
            2*e0*e2
        """
        if isinstance(V, CliffordAlgebra):
            Q = self._quadratic_form
            try:
                return (V.variable_names() == self.variable_names() and
                        V._quadratic_form.base_change_to(self.base_ring()) == Q)
            except Exception:
                return False

        if self.free_module().has_coerce_map_from(V):
            return True

        return super(CliffordAlgebra, self)._coerce_map_from_(V)

    def _element_constructor_(self, x):
        """
        Construct an element of ``self`` from ``x``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Qp = QuadraticForm(QQ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Clp = CliffordAlgebra(Qp, names=['x','y','z'])
            sage: M = ZZ^3
            sage: Mp = QQ^3
            sage: Cl(2/3)
            Traceback (most recent call last):
            ...
            TypeError: do not know how to make x (= 2/3) an element of self ...
            sage: Clp(2/3)
            2/3
            sage: Clp(x)
            x
            sage: M = ZZ^3
            sage: Clp( M((1,-3,2)) )
            x - 3*y + 2*z

        Zero coordinates are handled appropriately::

            sage: Q3 = QuadraticForm(Integers(3), 3, [1,2,3,4,5,6])
            sage: Cl3 = CliffordAlgebra(Q3, names='xyz')  # different syntax for a change
            sage: Cl3( M((1,-3,2)) )
            x + 2*z
        """
        # This is the natural lift morphism of the underlying free module
        if x in self.free_module():
            R = self.base_ring()
            if x.parent().base_ring() is R:
                return self.element_class(self, {(i,): c for i,c in x.items()})
            return self.element_class(self, {(i,): R(c) for i,c in x.items() if R(c) != R.zero()})

        if (isinstance(x, CliffordAlgebraElement)
            and self.has_coerce_map_from(x.parent())):
            R = self.base_ring()
            return self.element_class(self, {i: R(c) for i,c in x if R(c) != R.zero()})

        return super(CliffordAlgebra, self)._element_constructor_(x)

    def gen(self, i):
        """
        Return the ``i``-th standard generator of the algebra ``self``.

        This is the ``i``-th basis vector of the vector space on which
        the quadratic form defining ``self`` is defined, regarded as an
        element of ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: [Cl.gen(i) for i in range(3)]
            [x, y, z]
        """
        return self._from_dict({(i,): self.base_ring().one()}, remove_zeros=False)

    def algebra_generators(self):
        """
        Return the algebra generators of ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.algebra_generators()
            Finite family {'x': x, 'y': y, 'z': z}
        """
        d = {x: self.gen(i) for i,x in enumerate(self.variable_names())}
        return Family(self.variable_names(), lambda x: d[x])

    def gens(self):
        r"""
        Return the generators of ``self`` (as an algebra).

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.gens()
            (x, y, z)
        """
        return tuple(self.algebra_generators())

    def ngens(self):
        """
        Return the number of algebra generators of ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.ngens()
            3
        """
        return self._quadratic_form.dim()

    @cached_method
    def one_basis(self):
        """
        Return the basis index of the element `1`.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.one_basis()
            ()
        """
        return ()

    def is_commutative(self):
        """
        Check if ``self`` is a commutative algebra.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.is_commutative()
            False
        """
        return self._quadratic_form.dim() < 2

    def quadratic_form(self):
        """
        Return the quadratic form of ``self``.

        This is the quadratic form used to define ``self``. The
        quadratic form on ``self`` is yet to be implemented.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.quadratic_form()
            Quadratic form in 3 variables over Integer Ring with coefficients:
            [ 1 2 3 ]
            [ * 4 5 ]
            [ * * 6 ]
        """
        return self._quadratic_form

    def degree_on_basis(self, m):
        r"""
        Return the degree of the monomial indexed by ``m``.

        We are considering the Clifford algebra to be `\NN`-filtered,
        and the degree of the monomial ``m`` is the length of ``m``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.degree_on_basis((0,))
            1
            sage: Cl.degree_on_basis((0,1))
            2
        """
        return ZZ(len(m))

    def graded_algebra(self):
        """
        Return the associated graded algebra of ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.graded_algebra()
            The exterior algebra of rank 3 over Integer Ring
        """
        return ExteriorAlgebra(self.base_ring(), self.variable_names())

    @cached_method
    def free_module(self):
        """
        Return the underlying free module `V` of ``self``.

        This is the free module on which the quadratic form that was
        used to construct ``self`` is defined.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.free_module()
            Ambient free module of rank 3 over the principal ideal domain Integer Ring
        """
        return FreeModule(self.base_ring(), self._quadratic_form.dim())

    def dimension(self):
        """
        Return the rank of ``self`` as a free module.

        Let `V` be a free `R`-module of rank `n`; then, `Cl(V, Q)` is a
        free `R`-module of rank `2^n`.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.dimension()
            8
        """
        return ZZ(2)**self._quadratic_form.dim()

    def pseudoscalar(self):
        r"""
        Return the unit pseudoscalar of ``self``.

        Given the basis `e_1, e_2, \ldots, e_n` of the underlying
        `R`-module, the unit pseudoscalar is defined as
        `e_1 \cdot e_2 \cdots e_n`.

        This depends on the choice of basis.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.pseudoscalar()
            x*y*z

            sage: Q = QuadraticForm(ZZ, 0, [])
            sage: Cl = CliffordAlgebra(Q)
            sage: Cl.pseudoscalar()
            1

        REFERENCES:

        - :wikipedia:`Classification_of_Clifford_algebras#Unit_pseudoscalar`
        """
        d = self._quadratic_form.dim()
        return self.element_class(self, {tuple(range(d)): self.base_ring().one()})

    def lift_module_morphism(self, m, names=None):
        r"""
        Lift the matrix ``m`` to an algebra morphism of Clifford algebras.

        Given a linear map `m : W \to V` (here represented by a matrix
        acting on column vectors), this method returns the algebra
        morphism `Cl(m) : Cl(W, m(Q)) \to Cl(V, Q)`, where `Cl(V, Q)`
        is the Clifford algebra ``self`` and where `m(Q)` is the pullback
        of the quadratic form `Q` to `W`. See the documentation
        of :class:`CliffordAlgebra` for how this pullback and the
        morphism `Cl(m)` are defined.

        .. NOTE::

            This is a map into ``self``.

        INPUT:

        - ``m`` -- a matrix
        - ``names`` -- (default: ``'e'``) the names of the generators of the
          Clifford algebra of the domain of (the map represented by) ``m``

        OUTPUT:

        The algebra morphism `Cl(m)` from `Cl(W, m(Q))` to ``self``.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: m = matrix([[1,-1,-1],[0,1,-1],[1,1,1]])
            sage: phi = Cl.lift_module_morphism(m, 'abc')
            sage: phi
            Generic morphism:
              From: The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients:
            [ 10 17 3 ]
            [ * 11 0 ]
            [ * * 5 ]
              To:   The Clifford algebra of the Quadratic form in 3 variables over Integer Ring with coefficients:
            [ 1 2 3 ]
            [ * 4 5 ]
            [ * * 6 ]
            sage: a,b,c = phi.domain().gens()
            sage: phi(a)
            x + z
            sage: phi(b)
            -x + y + z
            sage: phi(c)
            -x - y + z
            sage: phi(a + 3*b)
            -2*x + 3*y + 4*z
            sage: phi(a) + 3*phi(b)
            -2*x + 3*y + 4*z
            sage: phi(a*b)
            x*y + 2*x*z - y*z + 7
            sage: phi(b*a)
            -x*y - 2*x*z + y*z + 10
            sage: phi(a*b + c)
            x*y + 2*x*z - y*z - x - y + z + 7
            sage: phi(a*b) + phi(c)
            x*y + 2*x*z - y*z - x - y + z + 7

        We check that the map is an algebra morphism::

            sage: phi(a)*phi(b)
            x*y + 2*x*z - y*z + 7
            sage: phi(a*b)
            x*y + 2*x*z - y*z + 7
            sage: phi(a*a)
            10
            sage: phi(a)*phi(a)
            10
            sage: phi(b*a)
            -x*y - 2*x*z + y*z + 10
            sage: phi(b) * phi(a)
            -x*y - 2*x*z + y*z + 10
            sage: phi((a + b)*(a + c)) == phi(a + b) * phi(a + c)
            True

        We can also lift arbitrary linear maps::

            sage: m = matrix([[1,1],[0,1],[1,1]])
            sage: phi = Cl.lift_module_morphism(m, 'ab')
            sage: a,b = phi.domain().gens()
            sage: phi(a)
            x + z
            sage: phi(b)
            x + y + z
            sage: phi(a*b)
            x*y - y*z + 15
            sage: phi(a)*phi(b)
            x*y - y*z + 15
            sage: phi(b*a)
            -x*y + y*z + 12
            sage: phi(b)*phi(a)
            -x*y + y*z + 12

            sage: m = matrix([[1,1,1,2], [0,1,1,1], [0,1,1,1]])
            sage: phi = Cl.lift_module_morphism(m, 'abcd')
            sage: a,b,c,d = phi.domain().gens()
            sage: phi(a)
            x
            sage: phi(b)
            x + y + z
            sage: phi(c)
            x + y + z
            sage: phi(d)
            2*x + y + z
            sage: phi(a*b*c + d*a)
            -x*y - x*z + 21*x + 7
            sage: phi(a*b*c*d)
            21*x*y + 21*x*z + 42

        TESTS:

        Check that the resulting morphism knows it is for
        finite-dimensional algebras (:trac:`25339`)::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: m = matrix([[1,-1,-1],[0,1,-1],[1,1,1]])
            sage: phi = Cl.lift_module_morphism(m, 'abc')
            sage: phi.category_for()
            Category of finite dimensional super algebras with basis over
             (euclidean domains and infinite enumerated sets and metric spaces)
            sage: phi.matrix()
            [  1   0   0   0   7  -3  -7   0]
            [  0   1  -1  -1   0   0   0 -17]
            [  0   0   1  -1   0   0   0  -4]
            [  0   1   1   1   0   0   0   3]
            [  0   0   0   0   1  -1   2   0]
            [  0   0   0   0   2   2   0   0]
            [  0   0   0   0  -1   1   2   0]
            [  0   0   0   0   0   0   0   4]
        """
        Q = self._quadratic_form(m)
        # If R is a quadratic form and m is a matrix, then R(m) returns
        # the quadratic form m^t R m.

        if Q == self._quadratic_form and names is None:
            Cl = self
        else:
            Cl = CliffordAlgebra(Q, names)

        n = self._quadratic_form.dim()
        f = lambda x: self.prod(self._from_dict( {(j,): m[j,i] for j in range(n)},
                                                 remove_zeros=True )
                                for i in x)
        cat = AlgebrasWithBasis(self.category().base_ring()).Super().FiniteDimensional()
        return Cl.module_morphism(on_basis=f, codomain=self, category=cat)

    def lift_isometry(self, m, names=None):
        r"""
        Lift an invertible isometry ``m`` of the quadratic form of
        ``self`` to a Clifford algebra morphism.

        Given an invertible linear map `m : V \to W` (here represented by
        a matrix acting on column vectors), this method returns the
        algebra morphism `Cl(m)` from `Cl(V, Q)` to `Cl(W, m^{-1}(Q))`,
        where `Cl(V, Q)` is the Clifford algebra ``self`` and where
        `m^{-1}(Q)` is the pullback of the quadratic form `Q` to `W` along
        the inverse map `m^{-1} : W \to V`. See the documentation of
        :class:`CliffordAlgebra` for how this pullback and the morphism
        `Cl(m)` are defined.

        INPUT:

        - ``m`` -- an isometry of the quadratic form of ``self``
        - ``names`` -- (default: ``'e'``) the names of the generators of
          the Clifford algebra of the codomain of (the map represented by)
          ``m``

        OUTPUT:

        The algebra morphism `Cl(m)` from ``self`` to `Cl(W, m^{-1}(Q))`.

        EXAMPLES::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: m = matrix([[1,1,2],[0,1,1],[0,0,1]])
            sage: phi = Cl.lift_isometry(m, 'abc')
            sage: phi(x)
            a
            sage: phi(y)
            a + b
            sage: phi(x*y)
            a*b + 1
            sage: phi(x) * phi(y)
            a*b + 1
            sage: phi(z*y)
            a*b - a*c - b*c
            sage: phi(z) * phi(y)
            a*b - a*c - b*c
            sage: phi(x + z) * phi(y + z) == phi((x + z) * (y + z))
            True

        TESTS:

        Check that the resulting morphism knows it is for
        finite-dimensional algebras (:trac:`25339`)::

            sage: Q = QuadraticForm(ZZ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: m = matrix([[1,1,2],[0,1,1],[0,0,1]])
            sage: phi = Cl.lift_isometry(m, 'abc')
            sage: phi.category_for()
            Category of finite dimensional super algebras with basis over
             (euclidean domains and infinite enumerated sets and metric spaces)
            sage: phi.matrix()
            [ 1  0  0  0  1  2  5  0]
            [ 0  1  1  2  0  0  0  5]
            [ 0  0  1  1  0  0  0 -1]
            [ 0  0  0  1  0  0  0  1]
            [ 0  0  0  0  1  1 -1  0]
            [ 0  0  0  0  0  1  1  0]
            [ 0  0  0  0  0  0  1  0]
            [ 0  0  0  0  0  0  0  1]
        """
        MS = m.parent()
        if not m.is_invertible():
            raise ValueError('{} is not invertible')
        Q = self._quadratic_form(MS(m.inverse()))

        if Q == self._quadratic_form and names is None:
            Cl = self
        else:
            if names is None:
                names = 'e'
            Cl = CliffordAlgebra(Q, names)

        n = Q.dim()
        f = lambda x: Cl.prod(Cl._from_dict( {(j,): m[j,i] for j in range(n)},
                                             remove_zeros=True )
                              for i in x)
        cat = AlgebrasWithBasis(self.category().base_ring()).Super().FiniteDimensional()
        return self.module_morphism(on_basis=f, codomain=Cl, category=cat)

    # This is a general method for finite dimensional algebras with bases
    #   and should be moved to the corresponding category once there is
    #   a category level method for getting the indexing set of the basis;
    #   similar to #15289 but on a category level.
    @cached_method
    def center_basis(self):
        """
        Return a list of elements which correspond to a basis for the center
        of ``self``.

        This assumes that the ground ring can be used to compute the
        kernel of a matrix.

        .. SEEALSO::

            :meth:`supercenter_basis`,
            http://math.stackexchange.com/questions/129183/center-of-clifford-algebra-depending-on-the-parity-of-dim-v

        .. TODO::

            Deprecate this in favor of a method called `center()` once
            subalgebras are properly implemented in Sage.

        EXAMPLES::

            sage: Q = QuadraticForm(QQ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Z = Cl.center_basis(); Z
            (1, -2/5*x*y*z + x - 3/5*y + 2/5*z)
            sage: all(z*b - b*z == 0 for z in Z for b in Cl.basis())
            True

            sage: Q = QuadraticForm(QQ, 3, [1,-2,-3, 4, 2, 1])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Z = Cl.center_basis(); Z
            (1, -x*y*z + x + 3/2*y - z)
            sage: all(z*b - b*z == 0 for z in Z for b in Cl.basis())
            True

            sage: Q = QuadraticForm(QQ, 2, [1,-2,-3])
            sage: Cl.<x,y> = CliffordAlgebra(Q)
            sage: Cl.center_basis()
            (1,)

            sage: Q = QuadraticForm(QQ, 2, [-1,1,-3])
            sage: Cl.<x,y> = CliffordAlgebra(Q)
            sage: Cl.center_basis()
            (1,)

        A degenerate case::

            sage: Q = QuadraticForm(QQ, 3, [4,4,-4,1,-2,1])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.center_basis()
            (1, x*y*z + x - 2*y - 2*z, x*y + x*z - 2*y*z)

        The most degenerate case (the exterior algebra)::

            sage: Q = QuadraticForm(QQ, 3)
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.center_basis()
            (1, x*y, x*z, y*z, x*y*z)
        """
        R = self.base_ring()
        B = self.basis()
        K = list(B.keys())
        k = len(K)
        d = {}
        for a,i in enumerate(K):
            Bi = B[i]
            for b,j in enumerate(K):
                Bj = B[j]
                for m,c in (Bi*Bj - Bj*Bi):
                    d[(a, K.index(m)+k*b)] = c
        m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True)
        from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in x.items()),
                                                  distinct=True)
        return tuple(map( from_vector, m.kernel().basis() ))

    # Same as center except for superalgebras
    @cached_method
    def supercenter_basis(self):
        """
        Return a list of elements which correspond to a basis for the
        supercenter of ``self``.

        This assumes that the ground ring can be used to compute the
        kernel of a matrix.

        .. SEEALSO::

            :meth:`center_basis`,
            http://math.stackexchange.com/questions/129183/center-of-clifford-algebra-depending-on-the-parity-of-dim-v

        .. TODO::

            Deprecate this in favor of a method called `supercenter()` once
            subalgebras are properly implemented in Sage.

        EXAMPLES::

            sage: Q = QuadraticForm(QQ, 3, [1,2,3,4,5,6])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: SZ = Cl.supercenter_basis(); SZ
            (1,)
            sage: all(z.supercommutator(b) == 0 for z in SZ for b in Cl.basis())
            True

            sage: Q = QuadraticForm(QQ, 3, [1,-2,-3, 4, 2, 1])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1,)

            sage: Q = QuadraticForm(QQ, 2, [1,-2,-3])
            sage: Cl.<x,y> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1,)

            sage: Q = QuadraticForm(QQ, 2, [-1,1,-3])
            sage: Cl.<x,y> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1,)

        Singular vectors of a quadratic form generate in the supercenter::

            sage: Q = QuadraticForm(QQ, 3, [1/2,-2,4,256/249,3,-185/8])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1, x + 249/322*y + 22/161*z)

            sage: Q = QuadraticForm(QQ, 3, [4,4,-4,1,-2,1])
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1, x + 2*z, y + z, x*y + x*z - 2*y*z)

        The most degenerate case::

            sage: Q = QuadraticForm(QQ, 3)
            sage: Cl.<x,y,z> = CliffordAlgebra(Q)
            sage: Cl.supercenter_basis()
            (1, x, y, z, x*y, x*z, y*z, x*y*z)
        """
        R = self.base_ring()
        B = self.basis()
        K = list(B.keys())
        k = len(K)
        d = {}
        for a,i in enumerate(K):
            Bi = B[i]
            for b,j in enumerate(K):
                Bj = B[j]
                if len(i) % 2 and len(j) % 2:
                    supercommutator = Bi * Bj + Bj * Bi
                else:
                    supercommutator = Bi * Bj - Bj * Bi
                for m,c in supercommutator:
                    d[(a, K.index(m)+k*b)] = c
        m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True)
        from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in x.items()),
                                                  distinct=True)
        return tuple(map( from_vector, m.kernel().basis() ))

    Element = CliffordAlgebraElement

class ExteriorAlgebra(CliffordAlgebra):
    r"""
    An exterior algebra of a free module over a commutative ring.

    Let `V` be a module over a commutative ring `R`. The exterior algebra
    (or Grassmann algebra) `\Lambda(V)` of `V` is defined as the quotient
    of the tensor algebra `T(V)` of `V` modulo the two-sided ideal
    generated by all tensors of the form `x \otimes x` with `x \in V`. The
    multiplication on `\Lambda(V)` is denoted by `\wedge` (so
    `v_1 \wedge v_2 \wedge \cdots \wedge v_n` is the projection of
    `v_1 \otimes v_2 \otimes \cdots \otimes v_n` onto `\Lambda(V)`) and
    called the "exterior product" or "wedge product".

    If `V` is a rank-`n` free `R`-module with a basis
    `\{e_1, \ldots, e_n\}`, then `\Lambda(V)` is the `R`-algebra
    noncommutatively generated by the `n` generators `e_1, \ldots, e_n`
    subject to the relations `e_i^2 = 0` for all `i`, and
    `e_i e_j = - e_j e_i` for all `i < j`. As an `R`-module,
    `\Lambda(V)` then has a basis `(\bigwedge_{i \in I} e_i)` with `I`
    ranging over the subsets of `\{1, 2, \ldots, n\}` (where
    `\bigwedge_{i \in I} e_i` is the wedge product of `e_i` for `i`
    running through all elements of `I` from smallest to largest), and
    hence is free of rank `2^n`.

    The exterior algebra of an `R`-module `V` can also be realized
    as the Clifford algebra of `V` for the quadratic form `Q` given by
    `Q(v) = 0` for all vectors `v \in V`. See :class:`CliffordAlgebra`
    for the notion of a Clifford algebra.

    The exterior algebra of an `R`-module `V` is a connected `\ZZ`-graded
    Hopf superalgebra. It is commutative in the super sense (i.e., the
    odd elements anticommute and square to `0`).

    This class implements the exterior algebra `\Lambda(R^n)` for
    `n` a nonnegative integer.

    INPUT:

    - ``R`` -- the base ring, *or* the free module whose exterior algebra
      is to be computed

    - ``names`` -- a list of strings to name the generators of the
      exterior algebra; this list can either have one entry only (in which
      case the generators will be called ``e + '0'``, ``e + '1'``, ...,
      ``e + 'n-1'``, with ``e`` being said entry), or have ``n`` entries
      (in which case these entries will be used directly as names for the
      generators)

    - ``n`` -- the number of generators, i.e., the rank of the free
      module whose exterior algebra is to be computed (this doesn't have
      to be provided if it can be inferred from the rest of the input)

    REFERENCES:

    - :wikipedia:`Exterior_algebra`
    """
    @staticmethod
    def __classcall_private__(cls, R, names=None, n=None):
        """
        Normalize arguments to ensure a unique representation.

        EXAMPLES::

            sage: E1.<e0,e1,e2> = ExteriorAlgebra(QQ)
            sage: E2 = ExteriorAlgebra(QQ, 3)
            sage: E3 = ExteriorAlgebra(QQ, ['e0','e1','e2'])
            sage: E1 is E2 and E2 is E3
            True
        """
        if names is None:
            names = 'e'
        elif names in ZZ:
            n = names
            names = 'e'

        if isinstance(R, FreeModule_generic):
            if n is not None and n != R.dimension():
                raise ValueError("the number of variables does not match the dimension")
            n = R.dimension()
            R = R.base_ring()

        names = tuple(names)
        if n is not None and len(names) != n:
            if len(names) == 1:
                names = tuple( '{}{}'.format(names[0], i) for i in range(n) )
            else:
                raise ValueError("the number of variables does not match the number of generators")
        return super(ExteriorAlgebra, cls).__classcall__(cls, R, names)

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

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.category()
            Category of finite dimensional supercommutative supercocommutative
             super hopf algebras with basis over Rational Field
            sage: TestSuite(E).run()

            sage: TestSuite(ExteriorAlgebra(GF(3), ['a', 'b'])).run()
        """
        cat = HopfAlgebrasWithBasis(R).FiniteDimensional().Supercommutative().Supercocommutative()
        CliffordAlgebra.__init__(self, QuadraticForm(R, len(names)), names, category=cat)

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

        EXAMPLES::

            sage: ExteriorAlgebra(QQ, 3)
            The exterior algebra of rank 3 over Rational Field
        """
        return "The exterior algebra of rank {} over {}".format(self.ngens(), self.base_ring())

    def _repr_term(self, m):
        """
        Return a string representation of the basis element indexed by
        ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E._repr_term((0,1,2))
            'x*y*z'
            sage: y*x + x*z
            -x*y + x*z
        """
        if len(m) == 0:
            return '1'
        term = ''
        for i in m:
            if len(term) != 0:
                term += '*'
            term += self.variable_names()[i]
        return term

    def _ascii_art_term(self, m):
        r"""
        Return ascii art for the basis element indexed by ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E._ascii_art_term((0,1,2))
            x/\y/\z
            sage: ascii_art(y*x + 2*x*z)
            -x/\y + 2*x/\z
        """
        if len(m) == 0:
            return ascii_art('1')
        wedge = '/\\'
        return ascii_art(*[self.variable_names()[i] for i in m], sep=wedge)

    def _unicode_art_term(self, m):
        """
        Return unicode art for the basis element indexed by ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E._unicode_art_term((0,1,2))
            x∧y∧z
            sage: unicode_art(y*x + x*z)
            -x∧y + x∧z
        """
        if len(m) == 0:
            return unicode_art('1')
        wedge = unicodedata.lookup('LOGICAL AND')
        return unicode_art(*[self.variable_names()[i] for i in m], sep=wedge)

    def _latex_term(self, m):
        r"""
        Return a `\LaTeX` representation of the basis element indexed
        by ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E._latex_term((0,1,2))
            ' x \\wedge y \\wedge z'
            sage: E.<x0,x1,x2> = ExteriorAlgebra(QQ)
            sage: E._latex_term((0,1,2))
            ' x_{0} \\wedge x_{1} \\wedge x_{2}'
            sage: E._latex_term(())
            '1'
            sage: E._latex_term((0,))
            ' x_{0}'
        """
        if len(m) == 0:
            return '1'
        term = ''
        for i in m:
            if len(term) != 0:
                term += ' \\wedge'
            term += ' ' + self.latex_variable_names()[i]
        return term

    def lift_morphism(self, phi, names=None):
        r"""
        Lift the matrix ``m`` to an algebra morphism of exterior algebras.

        Given a linear map `\phi : V \to W` (here represented by a matrix
        acting on column vectors over the base ring of `V`), this method
        returns the algebra morphism
        `\Lambda(\phi) : \Lambda(V) \to \Lambda(W)`. This morphism is defined
        on generators `v_i \in \Lambda(V)` by `v_i \mapsto \phi(v_i)`.

        .. NOTE::

            This is the map going out of ``self`` as opposed to
            :meth:`~sage.algebras.clifford_algebra.CliffordAlgebraElement.lift_module_morphism()`
            for general Clifford algebras.

        INPUT:

        - ``phi`` -- a linear map `\phi` from `V` to `W`, encoded as a
          matrix
        - ``names`` -- (default: ``'e'``) the names of the generators of
          the Clifford algebra of the domain of (the map represented by)
          ``phi``

        OUTPUT:

        The algebra morphism `\Lambda(\phi)` from ``self`` to `\Lambda(W)`.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: phi = matrix([[0,1],[1,1],[1,2]]); phi
            [0 1]
            [1 1]
            [1 2]
            sage: L = E.lift_morphism(phi, ['a','b','c']); L
            Generic morphism:
              From: The exterior algebra of rank 2 over Rational Field
              To:   The exterior algebra of rank 3 over Rational Field
            sage: L(x)
            b + c
            sage: L(y)
            a + b + 2*c
            sage: L.on_basis()((1,))
            a + b + 2*c
            sage: p = L(E.one()); p
            1
            sage: p.parent()
            The exterior algebra of rank 3 over Rational Field
            sage: L(x*y)
            -a*b - a*c + b*c
            sage: L(x)*L(y)
            -a*b - a*c + b*c
            sage: L(x + y)
            a + 2*b + 3*c
            sage: L(x) + L(y)
            a + 2*b + 3*c
            sage: L(1/2*x + 2)
            1/2*b + 1/2*c + 2
            sage: L(E(3))
            3

            sage: psi = matrix([[1, -3/2]]); psi
            [   1 -3/2]
            sage: Lp = E.lift_morphism(psi, ['a']); Lp
            Generic morphism:
              From: The exterior algebra of rank 2 over Rational Field
              To:   The exterior algebra of rank 1 over Rational Field
            sage: Lp(x)
            a
            sage: Lp(y)
            -3/2*a
            sage: Lp(x + 2*y + 3)
            -2*a + 3

        TESTS:

        Check that the resulting morphism knows it is for
        finite-dimensional algebras (:trac:`25339`)::

            sage: E = ExteriorAlgebra(ZZ, 'e', 3)
            sage: T = jordan_block(0, 2).block_sum(jordan_block(0, 1))
            sage: phi = E.lift_morphism(T)
            sage: phi.category_for()
            Category of finite dimensional super algebras with basis over Integer Ring
            sage: phi.matrix()
            [1 0 0 0 0 0 0 0]
            [0 0 1 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
            [0 0 0 0 0 0 0 0]
        """
        n = phi.nrows()
        R = self.base_ring()
        E = ExteriorAlgebra(R, names, n)
        f = lambda x: E.prod(E._from_dict( {(j,): phi[j,i] for j in range(n)},
                                           remove_zeros=True )
                             for i in x)
        cat = AlgebrasWithBasis(R).Super().FiniteDimensional()
        return self.module_morphism(on_basis=f, codomain=E, category=cat)

    def volume_form(self):
        r"""
        Return the volume form of ``self``.

        Given the basis `e_1, e_2, \ldots, e_n` of the underlying
        `R`-module, the volume form is defined as `e_1 \wedge e_2
        \wedge \cdots \wedge e_n`.

        This depends on the choice of basis.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.volume_form()
            x*y*z
        """
        d = self._quadratic_form.dim()
        return self.element_class(self, {tuple(range(d)): self.base_ring().one()})

    def boundary(self, s_coeff):
        r"""
        Return the boundary operator `\partial` defined by the structure
        coefficients ``s_coeff`` of a Lie algebra.

        For more on the boundary operator, see
        :class:`ExteriorAlgebraBoundary`.

        INPUT:

        - ``s_coeff`` -- a dictionary whose keys are in `I \times I`, where
          `I` is the index set of the underlying vector space `V`, and whose
          values can be coerced into 1-forms (degree 1 elements) in ``E``
          (usually, these values will just be elements of `V`)

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.boundary({(0,1): z, (1,2): x, (2,0): y})
            Boundary endomorphism of The exterior algebra of rank 3 over Rational Field
        """
        return ExteriorAlgebraBoundary(self, s_coeff)

    def coboundary(self, s_coeff):
        r"""
        Return the coboundary operator `d` defined by the structure
        coefficients ``s_coeff`` of a Lie algebra.

        For more on the coboundary operator, see
        :class:`ExteriorAlgebraCoboundary`.

        INPUT:

        - ``s_coeff`` -- a dictionary whose keys are in `I \times I`, where
          `I` is the index set of the underlying vector space `V`, and whose
          values can be coerced into 1-forms (degree 1 elements) in ``E``
          (usually, these values will just be elements of `V`)

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.coboundary({(0,1): z, (1,2): x, (2,0): y})
            Coboundary endomorphism of The exterior algebra of rank 3 over Rational Field
        """
        return ExteriorAlgebraCoboundary(self, s_coeff)

    def degree_on_basis(self, m):
        r"""
        Return the degree of the monomial indexed by ``m``.

        The degree of ``m`` in the `\ZZ`-grading of ``self`` is defined
        to be the length of ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.degree_on_basis(())
            0
            sage: E.degree_on_basis((0,))
            1
            sage: E.degree_on_basis((0,1))
            2
        """
        return ZZ(len(m))

    def coproduct_on_basis(self, a):
        r"""
        Return the coproduct on the basis element indexed by ``a``.

        The coproduct is defined by

        .. MATH::

            \Delta(e_{i_1} \wedge \cdots \wedge e_{i_m}) = \sum_{k=0}^m
            \sum_{\sigma \in Ush_{k,m-k}} (-1)^{\sigma}
            (e_{i_{\sigma(1)}} \wedge \cdots \wedge e_{i_{\sigma(k)}}) \otimes
            (e_{i_{\sigma(k+1)}} \wedge \cdots \wedge e_{i_{\sigma(m)}}),

        where `Ush_{k,m-k}` denotes the set of all `(k,m-k)`-unshuffles
        (i.e., permutations in `S_m` which are increasing on the interval
        `\{1, 2, \ldots, k\}` and on the interval
        `\{k+1, k+2, \ldots, k+m\}`).

        .. WARNING::

            This coproduct is a homomorphism of superalgebras, not a
            homomorphism of algebras!

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.coproduct_on_basis((0,))
            1 # x + x # 1
            sage: E.coproduct_on_basis((0,1))
            1 # x*y + x # y + x*y # 1 - y # x
            sage: E.coproduct_on_basis((0,1,2))
            1 # x*y*z + x # y*z + x*y # z + x*y*z # 1
             - x*z # y - y # x*z + y*z # x + z # x*y
        """
        from sage.combinat.combinat import unshuffle_iterator
        one = self.base_ring().one()
        return self.tensor_square().sum_of_terms(unshuffle_iterator(a, one),
                                                 distinct=True)

    def antipode_on_basis(self, m):
        r"""
        Return the antipode on the basis element indexed by ``m``.

        Given a basis element `\omega`, the antipode is defined by
        `S(\omega) = (-1)^{\deg(\omega)} \omega`.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.antipode_on_basis(())
            1
            sage: E.antipode_on_basis((1,))
            -y
            sage: E.antipode_on_basis((1,2))
            y*z
        """
        return self.term(m, (-self.base_ring().one())**len(m))

    def counit(self, x):
        r"""
        Return the counit of ``x``.

        The counit of an element `\omega` of the exterior algebra
        is its constant coefficient.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: elt = x*y - 2*x + 3
            sage: E.counit(elt)
            3
        """
        return x.constant_coefficient()

    def interior_product_on_basis(self, a, b):
        r"""
        Return the interior product `\iota_b a` of ``a`` with respect to
        ``b``.

        See :meth:`~sage.algebras.clifford_algebra.CliffordAlgebra.Element.interior_product`
        for more information.

        In this method, ``a`` and ``b`` are supposed to be
        basis elements (see
        :meth:`~sage.algebras.clifford_algebra.CliffordAlgebra.Element.interior_product`
        for a method that computes interior product of arbitrary
        elements), and to be input as their keys.

        This depends on the choice of basis of the vector space
        whose exterior algebra is ``self``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: E.interior_product_on_basis((0,), (0,))
            1
            sage: E.interior_product_on_basis((0,2), (0,))
            z
            sage: E.interior_product_on_basis((1,), (0,2))
            0
            sage: E.interior_product_on_basis((0,2), (1,))
            0
            sage: E.interior_product_on_basis((0,1,2), (0,2))
            -y
        """
        sgn = True
        t = list(a)
        for i in b:
            if i not in t:
                return self.zero()
            if t.index(i) % 2:
                sgn = not sgn
            t.remove(i)
        R = self.base_ring()
        return self.term(tuple(t), (R.one() if sgn else - R.one()))

    def lifted_bilinear_form(self, M):
        r"""
        Return the bilinear form on the exterior algebra ``self``
        `= \Lambda(V)` which is obtained by lifting the bilinear
        form `f` on `V` given by the matrix ``M``.

        Let `V` be a module over a commutative ring `R`, and let
        `f : V \times V \to R` be a bilinear form on `V`. Then,
        a bilinear form `\Lambda(f) : \Lambda(V) \times
        \Lambda(V) \to R` on `\Lambda(V)` can be canonically
        defined as follows: For every `n \in \NN`, `m \in \NN`,
        `v_1, v_2, \ldots, v_n, w_1, w_2, \ldots, w_m \in V`,
        we define

        .. MATH::

            \Lambda(f)
            ( v_1 \wedge v_2 \wedge \cdots \wedge v_n ,
              w_1 \wedge w_2 \wedge \cdots \wedge w_m )
            := \begin{cases}
              0, &\mbox{if } n \neq m ; \\
              \det G, & \mbox{if } n = m \end{cases} ,

        where `G` is the `n \times m`-matrix whose
        `(i, j)`-th entry is `f(v_i, w_j)`. This bilinear form
        `\Lambda(f)` is known as the bilinear form on
        `\Lambda(V)` obtained by lifting the bilinear form `f`.
        Its restriction to the `1`-st homogeneous component
        `V` of `\Lambda(V)` is `f`.

        The bilinear form `\Lambda(f)` is symmetric if `f` is.

        INPUT:

        - ``M`` -- a matrix over the same base ring as ``self``,
          whose `(i, j)`-th entry is `f(e_i, e_j)`, where
          `(e_1, e_2, \ldots, e_N)` is the standard basis of the
          module `V` for which ``self`` `= \Lambda(V)` (so that
          `N = \dim(V)`), and where `f` is the bilinear form
          which is to be lifted.

        OUTPUT:

        A bivariate function which takes two elements `p` and
        `q` of ``self`` to `\Lambda(f)(p, q)`.

        .. NOTE::

            This takes a bilinear form on `V` as matrix, and
            returns a bilinear form on ``self`` as a function in
            two arguments. We do not return the bilinear form as
            a matrix since this matrix can be huge and one often
            needs just a particular value.

        .. TODO::

            Implement a class for bilinear forms and rewrite this
            method to use that class.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: M = Matrix(QQ, [[1, 2, 3], [2, 3, 4], [3, 4, 5]])
            sage: Eform = E.lifted_bilinear_form(M)
            sage: Eform
            Bilinear Form from The exterior algebra of rank 3 over Rational
            Field (+) The exterior algebra of rank 3 over Rational Field to
            Rational Field
            sage: Eform(x*y, y*z)
            -1
            sage: Eform(x*y, y)
            0
            sage: Eform(x*(y+z), y*z)
            -3
            sage: Eform(x*(y+z), y*(z+x))
            0
            sage: N = Matrix(QQ, [[3, 1, 7], [2, 0, 4], [-1, -3, -1]])
            sage: N.determinant()
            -8
            sage: Eform = E.lifted_bilinear_form(N)
            sage: Eform(x, E.one())
            0
            sage: Eform(x, x*z*y)
            0
            sage: Eform(E.one(), E.one())
            1
            sage: Eform(E.zero(), E.one())
            0
            sage: Eform(x, y)
            1
            sage: Eform(z, y)
            -3
            sage: Eform(x*z, y*z)
            20
            sage: Eform(x+x*y+x*y*z, z+z*y+z*y*x)
            11

        TESTS:

        Exterior algebra over a zero space (a border case)::

            sage: E = ExteriorAlgebra(QQ, 0)
            sage: M = Matrix(QQ, [])
            sage: Eform = E.lifted_bilinear_form(M)
            sage: Eform(E.one(), E.one())
            1
            sage: Eform(E.zero(), E.one())
            0

        .. TODO::

            Another way to compute this bilinear form seems to be to
            map `x` and `y` to the appropriate Clifford algebra and
            there compute `x^t y`, then send the result back to the
            exterior algebra and return its constant coefficient. Or
            something like this. Once the maps to the Clifford and
            back are implemented, check if this is faster.
        """
        R = self.base_ring()
        def lifted_form(x, y):
            result = R.zero()
            for mx, cx in x:
                for my, cy in y:
                    n = len(mx)
                    m = len(my)
                    if m != n:
                        continue
                    matrix_list = [M[mx[i], my[j]]
                                   for i in range(n)
                                   for j in range(n)]
                    MA = MatrixArgs(R, n, matrix_list)
                    del matrix_list
                    result += cx * cy * MA.matrix(False).determinant()
            return result
        from sage.categories.cartesian_product import cartesian_product
        return PoorManMap(lifted_form, domain=cartesian_product([self, self]),
                          codomain=self.base_ring(),
                          name="Bilinear Form")

    class Element(CliffordAlgebraElement):
        """
        An element of an exterior algebra.
        """
        def _mul_(self, other):
            """
            Return ``self`` multiplied by ``other``.

            INPUT:

            - ``other`` -- element of the same exterior algebra as ``self``

            EXAMPLES::

                sage: E.<x,y,z> = ExteriorAlgebra(QQ)
                sage: x*y
                x*y
                sage: y*x
                -x*y
                sage: z*y*x
                -x*y*z
                sage: (x*z)*y
                -x*y*z
                sage: (3*x + y)^2
                0
                sage: (x - 3*y + z/3)^2
                0
                sage: (x+y) * (y+z)
                x*y + x*z + y*z
            """
            zero = self.parent().base_ring().zero()
            d = {}

            for ml,cl in self:
                for mr,cr in other:
                    # Create the next term
                    t = list(mr)
                    for i in reversed(ml):
                        pos = 0
                        for j in t:
                            if i == j:
                                pos = None
                                break
                            if i < j:
                                break
                            pos += 1
                            cr = -cr
                        if pos is None:
                            t = None
                            break
                        t.insert(pos, i)

                    if t is None: # The next term is 0, move along
                        continue

                    t = tuple(t)
                    d[t] = d.get(t, zero) + cl * cr
                    if d[t] == zero:
                        del d[t]

            return self.__class__(self.parent(), d)

        def interior_product(self, x):
            r"""
            Return the interior product (also known as antiderivation) of
            ``self`` with respect to ``x`` (that is, the element
            `\iota_{x}(\text{self})` of the exterior algebra).

            If `V` is an `R`-module, and if `\alpha` is a fixed element of
            `V^*`, then the *interior product* with respect to `\alpha` is
            an `R`-linear map
            `i_{\alpha} \colon \Lambda(V) \to \Lambda(V)`, determined by
            the following requirements:

            - `i_{\alpha}(v) = \alpha(v)` for all `v \in V = \Lambda^1(V)`,
            - it is a graded derivation of degree `-1`: all `x` and `y`
              in `\Lambda(V)` satisfy

            .. MATH::

                i_{\alpha}(x \wedge y) = (i_{\alpha} x) \wedge y
                + (-1)^{\deg x} x \wedge (i_{\alpha} y).

            It can be shown that this map `i_{\alpha}` is graded of
            degree `-1` (that is, sends `\Lambda^k(V)` into
            `\Lambda^{k-1}(V)` for every `k`).

            When `V` is a finite free `R`-module, the interior product can
            also be defined by

            .. MATH::

                (i_{\alpha} \omega)(u_1, \ldots, u_k)
                = \omega(\alpha, u_1, \ldots, u_k),

            where `\omega \in \Lambda^k(V)` is thought of as an
            alternating multilinear mapping from
            `V^* \times \cdots \times V^*` to `R`.

            Since Sage is only dealing with exterior powers of modules
            of the form `R^d` for some nonnegative integer `d`, the
            element `\alpha \in V^*` can be thought of as an element of
            `V` (by identifying the standard basis of `V = R^d` with its
            dual basis). This is how `\alpha` should be passed to this
            method.

            We then extend the interior product to all
            `\alpha \in \Lambda (V^*)` by

            .. MATH::

                i_{\beta \wedge \gamma} = i_{\gamma} \circ i_{\beta}.

            INPUT:

            - ``x`` -- element of (or coercing into) `\Lambda^1(V)`
              (for example, an element of `V`); this plays the role of
              `\alpha` in the above definition

            EXAMPLES::

                sage: E.<x,y,z> = ExteriorAlgebra(QQ)
                sage: x.interior_product(x)
                1
                sage: (x + x*y).interior_product(2*y)
                -2*x
                sage: (x*z + x*y*z).interior_product(2*y - x)
                -2*x*z - y*z - z
                sage: x.interior_product(E.one())
                x
                sage: E.one().interior_product(x)
                0
                sage: x.interior_product(E.zero())
                0
                sage: E.zero().interior_product(x)
                0

            REFERENCES:

            - :wikipedia:`Exterior_algebra#Interior_product`
            """
            P = self.parent()
            return P.sum([c * cx * P.interior_product_on_basis(m, mx)
                          for m,c in self for mx,cx in x])

        antiderivation = interior_product

        def hodge_dual(self):
            r"""
            Return the Hodge dual of ``self``.

            The Hodge dual of an element `\alpha` of the exterior algebra is
            defined as `i_{\alpha} \sigma`, where `\sigma` is the volume
            form
            (:meth:`~sage.algebras.clifford_algebra.ExteriorAlgebra.volume_form`)
            and `i_{\alpha}` denotes the antiderivation function with
            respect to `\alpha` (see :meth:`interior_product` for the
            definition of this).

            .. NOTE::

                The Hodge dual of the Hodge dual of a homogeneous element
                `p` of `\Lambda(V)` equals `(-1)^{k(n-k)} p`, where
                `n = \dim V` and `k = \deg(p) = |p|`.

            EXAMPLES::

                sage: E.<x,y,z> = ExteriorAlgebra(QQ)
                sage: x.hodge_dual()
                y*z
                sage: (x*z).hodge_dual()
                -y
                sage: (x*y*z).hodge_dual()
                1
                sage: [a.hodge_dual().hodge_dual() for a in E.basis()]
                [1, x, y, z, x*y, x*z, y*z, x*y*z]
                sage: (x + x*y).hodge_dual()
                y*z + z
                sage: (x*z + x*y*z).hodge_dual()
                -y + 1
                sage: E = ExteriorAlgebra(QQ, 'wxyz')
                sage: [a.hodge_dual().hodge_dual() for a in E.basis()]
                [1, -w, -x, -y, -z, w*x, w*y, w*z, x*y, x*z, y*z,
                 -w*x*y, -w*x*z, -w*y*z, -x*y*z, w*x*y*z]
            """
            volume_form = self.parent().volume_form()
            return volume_form.interior_product(self)

        def constant_coefficient(self):
            """
            Return the constant coefficient of ``self``.

            .. TODO::

                Define a similar method for general Clifford algebras once
                the morphism to exterior algebras is implemented.

            EXAMPLES::

                sage: E.<x,y,z> = ExteriorAlgebra(QQ)
                sage: elt = 5*x + y + x*z + 10
                sage: elt.constant_coefficient()
                10
                sage: x.constant_coefficient()
                0
            """
            return self._monomial_coefficients.get(self.parent().one_basis(),
                                                   self.base_ring().zero())

        def scalar(self, other):
            r"""
            Return the standard scalar product of ``self`` with ``other``.

            The standard scalar product of `x, y \in \Lambda(V)` is
            defined by `\langle x, y \rangle = \langle x^t y \rangle`, where
            `\langle a \rangle` denotes the degree-0 term of `a`, and where
            `x^t` denotes the transpose
            (:meth:`~sage.algebras.clifford_algebra.CliffordAlgebraElement.transpose`)
            of `x`.

            .. TODO::

                Define a similar method for general Clifford algebras once
                the morphism to exterior algebras is implemented.

            EXAMPLES::

                sage: E.<x,y,z> = ExteriorAlgebra(QQ)
                sage: elt = 5*x + y + x*z
                sage: elt.scalar(z + 2*x)
                0
                sage: elt.transpose() * (z + 2*x)
                -2*x*y + 5*x*z + y*z
            """
            return (self.transpose() * other).constant_coefficient()

#####################################################################
## Differentials

class ExteriorAlgebraDifferential(ModuleMorphismByLinearity,
        UniqueRepresentation, metaclass=InheritComparisonClasscallMetaclass):
    r"""
    Internal class to store the data of a boundary or coboundary of
    an exterior algebra `\Lambda(L)` defined by the structure
    coefficients of a Lie algebra `L`.

    See :class:`ExteriorAlgebraBoundary` and
    :class:`ExteriorAlgebraCoboundary` for the actual classes, which
    inherit from this.

    .. WARNING::

        This is not a general class for differentials on the exterior
        algebra.
    """
    @staticmethod
    def __classcall__(cls, E, s_coeff):
        """
        Standardize the structure coefficients to ensure a unique
        representation.

        EXAMPLES::

            sage: from sage.algebras.clifford_algebra import ExteriorAlgebraDifferential
            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par1 = ExteriorAlgebraDifferential(E, {(0,1): z, (1,2): x, (2,0): y})
            sage: par2 = ExteriorAlgebraDifferential(E, {(0,1): z, (1,2): x, (0,2): -y})
            sage: par3 = ExteriorAlgebraDifferential(E, {(1,0): {2:-1}, (1,2): {0:1}, (2,0):{1:1}})
            sage: par1 is par2 and par2 is par3
            True

            sage: par4 = ExteriorAlgebraDifferential(E, {})
            sage: par5 = ExteriorAlgebraDifferential(E, {(1,0): 0, (1,2): {}, (0,2): E.zero()})
            sage: par6 = ExteriorAlgebraDifferential(E, {(1,0): 0, (1,2): 0, (0,2): 0})
            sage: par4 is par5 and par5 is par6
            True
        """
        d = {}

        for k, v in dict(s_coeff).items():
            if not v: # Strip terms with 0
                continue

            if isinstance(v, dict):
                R = E.base_ring()
                v = E._from_dict({(i,): R(c) for i,c in v.items()})
            else:
                # Make sure v is in ``E``
                v = E(v)
                # It's okay if v.degree results in an error
                #   (we'd throw a similar error) unless v == 0 (which
                #   is what v.list() is testing for)
                if v.list() and v.degree() != 1:
                    raise ValueError("elements must be degree 1")

            if k[0] < k[1]:
                d[tuple(k)] = v
            else:
                d[(k[1], k[0])] = -v

        from sage.sets.family import Family
        return super(ExteriorAlgebraDifferential, cls).__classcall__(cls, E, Family(d))

    def __init__(self, E, s_coeff):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({(0,1): z, (1,2):x, (2,0):y})

        We skip the pickling test as there is an infinite recursion when
        doing equality checks::

            sage: TestSuite(par).run(skip="_test_pickling")

        Check that it knows it is a finite-dimensional algebra
        morphism (:trac:`25339`):;

            sage: par.category_for()
            Category of finite dimensional algebras with basis over Rational Field
            sage: par.matrix()
            [ 0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  1  0]
            [ 0  0  0  0  0 -1  0  0]
            [ 0  0  0  0  1  0  0  0]
            [ 0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  0  0]
        """
        self._s_coeff = s_coeff

        # Technically this preserves the grading but with a shift of -1
        cat = AlgebrasWithBasis(E.base_ring()).FiniteDimensional()
        ModuleMorphismByLinearity.__init__(self, domain=E, codomain=E, category=cat)

    def homology(self, deg=None, **kwds):
        """
        Return the homology determined by ``self``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({(0,1): z, (1,2): x, (2,0): y})
            sage: par.homology()
            {0: Vector space of dimension 1 over Rational Field,
             1: Vector space of dimension 0 over Rational Field,
             2: Vector space of dimension 0 over Rational Field,
             3: Vector space of dimension 1 over Rational Field}
            sage: d = E.coboundary({(0,1): z, (1,2): x, (2,0): y})
            sage: d.homology()
            {0: Vector space of dimension 1 over Rational Field,
             1: Vector space of dimension 0 over Rational Field,
             2: Vector space of dimension 0 over Rational Field,
             3: Vector space of dimension 1 over Rational Field}
        """
        return self.chain_complex().homology(deg, **kwds)

class ExteriorAlgebraBoundary(ExteriorAlgebraDifferential):
    r"""
    The boundary `\partial` of an exterior algebra `\Lambda(L)` defined
    by the structure coefficients of `L`.

    Let `L` be a Lie algebra. We give the exterior algebra
    `E = \Lambda(L)` a chain complex structure by considering a
    differential `\partial : \Lambda^{k+1}(L) \to \Lambda^k(L)` defined by

    .. MATH::

        \partial(x_1 \wedge x_2 \wedge \cdots \wedge x_{k+1})
        = \sum_{i < j} (-1)^{i+j+1}
        [x_i, x_j] \wedge x_1 \wedge \cdots \wedge \hat{x}_i \wedge \cdots
        \wedge \hat{x}_j \wedge \cdots \wedge x_{k+1}

    where `\hat{x}_i` denotes a missing index. The corresponding homology is
    the Lie algebra homology.

    INPUT:

    - ``E`` -- an exterior algebra of a vector space `L`
    - ``s_coeff`` -- a dictionary whose keys are in `I \times I`, where
      `I` is the index set of the basis of the vector space `L`, and whose
      values can be coerced into 1-forms (degree 1 elements) in ``E``;
      this dictionary will be used to define the Lie algebra structure
      on `L` (indeed, the `i`-th coordinate of the Lie bracket of the
      `j`-th and `k`-th basis vectors of `L` for `j < k` is set to be
      the value at the key `(j, k)` if this key appears in ``s_coeff``,
      or otherwise the negated of the value at the key `(k, j)`)

    .. WARNING::

        The values of ``s_coeff`` are supposed to be coercible into
        1-forms in ``E``; but they can also be dictionaries themselves
        (in which case they are interpreted as giving the coordinates of
        vectors in ``L``). In the interest of speed, these dictionaries
        are not sanitized or checked.

    .. WARNING::

        For any two distinct elements `i` and `j` of `I`, the dictionary
        ``s_coeff`` must have only one of the pairs `(i, j)` and
        `(j, i)` as a key. This is not checked.

    EXAMPLES:

    We consider the differential given by Lie algebra given by the cross
    product `\times` of `\RR^3`::

        sage: E.<x,y,z> = ExteriorAlgebra(QQ)
        sage: par = E.boundary({(0,1): z, (1,2): x, (2,0): y})
        sage: par(x)
        0
        sage: par(x*y)
        z
        sage: par(x*y*z)
        0
        sage: par(x+y-y*z+x*y)
        -x + z
        sage: par(E.zero())
        0

    We check that `\partial \circ \partial = 0`::

        sage: p2 = par * par
        sage: all(p2(b) == 0 for b in E.basis())
        True

    Another example: the Lie algebra `\mathfrak{sl}_2`, which has a
    basis `e,f,h` satisfying `[h,e] = 2e`, `[h,f] = -2f`, and `[e,f] = h`::

        sage: E.<e,f,h> = ExteriorAlgebra(QQ)
        sage: par = E.boundary({(0,1): h, (2,1): -2*f, (2,0): 2*e})
        sage: par(E.zero())
        0
        sage: par(e)
        0
        sage: par(e*f)
        h
        sage: par(f*h)
        2*f
        sage: par(h*f)
        -2*f
        sage: C = par.chain_complex(); C
        Chain complex with at most 4 nonzero terms over Rational Field
        sage: ascii_art(C)
                                  [ 0 -2  0]       [0]
                                  [ 0  0  2]       [0]
                    [0 0 0]       [ 1  0  0]       [0]
         0 <-- C_0 <-------- C_1 <----------- C_2 <---- C_3 <-- 0
        sage: C.homology()
        {0: Vector space of dimension 1 over Rational Field,
         1: Vector space of dimension 0 over Rational Field,
         2: Vector space of dimension 0 over Rational Field,
         3: Vector space of dimension 1 over Rational Field}

    Over the integers::

        sage: C = par.chain_complex(R=ZZ); C
        Chain complex with at most 4 nonzero terms over Integer Ring
        sage: ascii_art(C)
                                  [ 0 -2  0]       [0]
                                  [ 0  0  2]       [0]
                    [0 0 0]       [ 1  0  0]       [0]
         0 <-- C_0 <-------- C_1 <----------- C_2 <---- C_3 <-- 0
        sage: C.homology()
        {0: Z, 1: C2 x C2, 2: 0, 3: Z}

    REFERENCES:

    - :wikipedia:`Exterior_algebra#Lie_algebra_homology`
    """
    def _repr_type(self):
        """
        TESTS::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({(0,1): z, (1,2): x, (2,0): y})
            sage: par._repr_type()
            'Boundary'
        """
        return "Boundary"

    def _on_basis(self, m):
        """
        Return the differential on the basis element indexed by ``m``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({(0,1): z, (1,2): x, (2,0): y})
            sage: par._on_basis(())
            0
            sage: par._on_basis((0,))
            0
            sage: par._on_basis((0,1))
            z
            sage: par._on_basis((0,2))
            -y
            sage: par._on_basis((0,1,2))
            0
        """
        E = self.domain()
        sc = self._s_coeff
        keys = sc.keys()
        return E.sum((-1)**b * sc[(i,j)]
                      * E.monomial(m[:a] + m[a+1:a+b+1] + m[a+b+2:])
                     for a,i in enumerate(m) for b,j in enumerate(m[a+1:]) if (i,j) in keys)

    @cached_method
    def chain_complex(self, R=None):
        """
        Return the chain complex over ``R`` determined by ``self``.

        INPUT:

        - ``R`` -- the base ring; the default is the base ring of
          the exterior algebra

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({(0,1): z, (1,2): x, (2,0): y})
            sage: C = par.chain_complex(); C
            Chain complex with at most 4 nonzero terms over Rational Field
            sage: ascii_art(C)
                                      [ 0  0  1]       [0]
                                      [ 0 -1  0]       [0]
                        [0 0 0]       [ 1  0  0]       [0]
             0 <-- C_0 <-------- C_1 <----------- C_2 <---- C_3 <-- 0

        TESTS:

        This still works in degree `1`::

            sage: E.<x> = ExteriorAlgebra(QQ)
            sage: par = E.boundary({})
            sage: C = par.chain_complex(); C
            Chain complex with at most 2 nonzero terms over Rational Field
            sage: ascii_art(C)
                        [0]
             0 <-- C_0 <---- C_1 <-- 0

        Also in degree `0`::

            sage: E = ExteriorAlgebra(QQ, 0)
            sage: par = E.boundary({})
            sage: C = par.chain_complex(); C
            Chain complex with at most 1 nonzero terms over Rational Field
            sage: ascii_art(C)
             0 <-- C_0 <-- 0
        """
        from sage.homology.chain_complex import ChainComplex
        from sage.matrix.constructor import Matrix
        E = self.domain()
        n = E.ngens()
        if R is None:
            R = E.base_ring()

        if n == 0:
            # Special case because there are no matrices and thus the
            # ChainComplex constructor needs the dimension of the
            # 0th degree space explicitly given.
            return ChainComplex({1: Matrix(R, [[]])}, degree=-1)
            # If you are reading this because you changed something about
            # the ChainComplex constructor and the doctests are failing:
            # This should return a chain complex with degree -1 and
            # only one nontrivial module, namely a free module of rank 1,
            # situated in degree 0.

        # Group the basis into degrees
        basis_by_deg = {deg: [] for deg in range(n+1)}
        for b in E.basis().keys():
            basis_by_deg[len(b)].append(b)

        # Construct the transition matrices
        data = {}
        prev_basis = basis_by_deg[0]
        for deg in range(1,n+1):
            # Make sure within each basis we're sorted by lex
            basis = sorted(basis_by_deg[deg])
            mat = []
            for b in basis:
                ret = self._on_basis(b)
                mat.append([ret[p] for p in prev_basis])
            data[deg] = Matrix(mat).transpose().change_ring(R)
            prev_basis = basis

        return ChainComplex(data, degree=-1)

class ExteriorAlgebraCoboundary(ExteriorAlgebraDifferential):
    r"""
    The coboundary `d` of an exterior algebra `\Lambda(L)` defined
    by the structure coefficients of a Lie algebra `L`.

    Let `L` be a Lie algebra. We endow its exterior algebra
    `E = \Lambda(L)` with a cochain complex structure by considering a
    differential `d : \Lambda^k(L) \to \Lambda^{k+1}(L)` defined by

    .. MATH::

        d x_i = \sum_{j < k} s_{jk}^i x_j x_k,

    where `(x_1, x_2, \ldots, x_n)` is a basis of `L`, and where
    `s_{jk}^i` is the `x_i`-coordinate of the Lie bracket `[x_j, x_k]`.

    The corresponding cohomology is the Lie algebra cohomology of `L`.

    This can also be thought of as the exterior derivative, in which case
    the resulting cohomology is the de Rham cohomology of a manifold whose
    exterior algebra of differential forms is ``E``.

    INPUT:

    - ``E`` -- an exterior algebra of a vector space `L`
    - ``s_coeff`` -- a dictionary whose keys are in `I \times I`, where
      `I` is the index set of the basis of the vector space `L`, and whose
      values can be coerced into 1-forms (degree 1 elements) in ``E``;
      this dictionary will be used to define the Lie algebra structure
      on `L` (indeed, the `i`-th coordinate of the Lie bracket of the
      `j`-th and `k`-th basis vectors of `L` for `j < k` is set to be
      the value at the key `(j, k)` if this key appears in ``s_coeff``,
      or otherwise the negated of the value at the key `(k, j)`)

    .. WARNING::

        For any two distinct elements `i` and `j` of `I`, the dictionary
        ``s_coeff`` must have only one of the pairs `(i, j)` and
        `(j, i)` as a key. This is not checked.

    EXAMPLES:

    We consider the differential coming from the Lie algebra given by the
    cross product `\times` of `\RR^3`::

        sage: E.<x,y,z> = ExteriorAlgebra(QQ)
        sage: d = E.coboundary({(0,1): z, (1,2): x, (2,0): y})
        sage: d(x)
        y*z
        sage: d(y)
        -x*z
        sage: d(x+y-y*z)
        -x*z + y*z
        sage: d(x*y)
        0
        sage: d(E.one())
        0
        sage: d(E.zero())
        0

    We check that `d \circ d = 0`::

        sage: d2 = d * d
        sage: all(d2(b) == 0 for b in E.basis())
        True

    Another example: the Lie algebra `\mathfrak{sl}_2`, which has a
    basis `e,f,h` satisfying `[h,e] = 2e`, `[h,f] = -2f`, and `[e,f] = h`::

        sage: E.<e,f,h> = ExteriorAlgebra(QQ)
        sage: d = E.coboundary({(0,1): h, (2,1): -2*f, (2,0): 2*e})
        sage: d(E.zero())
        0
        sage: d(e)
        -2*e*h
        sage: d(f)
        2*f*h
        sage: d(h)
        e*f
        sage: d(e*f)
        0
        sage: d(f*h)
        0
        sage: d(e*h)
        0
        sage: C = d.chain_complex(); C
        Chain complex with at most 4 nonzero terms over Rational Field
        sage: ascii_art(C)
                                  [ 0  0  1]       [0]
                                  [-2  0  0]       [0]
                    [0 0 0]       [ 0  2  0]       [0]
         0 <-- C_3 <-------- C_2 <----------- C_1 <---- C_0 <-- 0
        sage: C.homology()
        {0: Vector space of dimension 1 over Rational Field,
         1: Vector space of dimension 0 over Rational Field,
         2: Vector space of dimension 0 over Rational Field,
         3: Vector space of dimension 1 over Rational Field}

    Over the integers::

        sage: C = d.chain_complex(R=ZZ); C
        Chain complex with at most 4 nonzero terms over Integer Ring
        sage: ascii_art(C)
                                  [ 0  0  1]       [0]
                                  [-2  0  0]       [0]
                    [0 0 0]       [ 0  2  0]       [0]
         0 <-- C_3 <-------- C_2 <----------- C_1 <---- C_0 <-- 0
        sage: C.homology()
        {0: Z, 1: 0, 2: C2 x C2, 3: Z}

    REFERENCES:

    - :wikipedia:`Exterior_algebra#Differential_geometry`
    """
    def __init__(self, E, s_coeff):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: d = E.coboundary({(0,1): z, (1,2):x, (2,0):y})
            sage: TestSuite(d).run() # known bug - morphisms are properly in a category
        """
        # Construct the dictionary of costructure coefficients, i.e. given
        # [x_j, x_k] = \sum_i s_{jk}^i x_i, we get x^i |-> \sum_{j<k} s_{jk}^i x^j x^k.
        # This dictionary might contain 0 values and might also be missing
        # some keys (both times meaning that the respective `s_{jk}^i` are
        # zero for all `j` and `k`).
        self._cos_coeff = {}
        zero = E.zero()
        B = E.basis()
        for k, v in dict(s_coeff).items():
            k = B[k]
            for m,c in v:
                self._cos_coeff[m] = self._cos_coeff.get(m, zero) + c * k
        ExteriorAlgebraDifferential.__init__(self, E, s_coeff)

    def _repr_type(self):
        """
        TESTS::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: d = E.coboundary({(0,1): z, (1,2): x, (2,0): y})
            sage: d._repr_type()
            'Coboundary'
        """
        return "Coboundary"

    def _on_basis(self, m):
        r"""
        Return the differential on the basis element indexed by ``m``.

        EXAMPLES:

        The vector space `\RR^3` made into a Lie algebra using the
        cross product::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: d = E.coboundary({(0,1): z, (1,2): x, (2,0): y})
            sage: d._on_basis(())
            0
            sage: d._on_basis((0,))
            y*z
            sage: d._on_basis((1,))
            -x*z
            sage: d._on_basis((2,))
            x*y
            sage: d._on_basis((0,1))
            0
            sage: d._on_basis((0,2))
            0
            sage: d._on_basis((0,1,2))
            0
        """
        E = self.domain()
        cc = self._cos_coeff
        keys = cc.keys()
        return E.sum((-1)**a * E.monomial(m[:a]) * cc[(i,)] * E.monomial(m[a+1:])
                     for a,i in enumerate(m) if (i,) in keys)

    @cached_method
    def chain_complex(self, R=None):
        """
        Return the chain complex over ``R`` determined by ``self``.

        INPUT:

        - ``R`` -- the base ring; the default is the base ring of
          the exterior algebra

        EXAMPLES::

            sage: E.<x,y,z> = ExteriorAlgebra(QQ)
            sage: d = E.coboundary({(0,1): z, (1,2): x, (2,0): y})
            sage: C = d.chain_complex(); C
            Chain complex with at most 4 nonzero terms over Rational Field
            sage: ascii_art(C)
                                      [ 0  0  1]       [0]
                                      [ 0 -1  0]       [0]
                        [0 0 0]       [ 1  0  0]       [0]
             0 <-- C_3 <-------- C_2 <----------- C_1 <---- C_0 <-- 0

        TESTS:

        This still works in degree `1`::

            sage: E.<x> = ExteriorAlgebra(QQ)
            sage: d = E.coboundary({})
            sage: C = d.chain_complex(); C
            Chain complex with at most 2 nonzero terms over Rational Field
            sage: ascii_art(C)
                        [0]
             0 <-- C_1 <---- C_0 <-- 0

        Also in degree `0`::

            sage: E = ExteriorAlgebra(QQ, 0)
            sage: d = E.coboundary({})
            sage: C = d.chain_complex(); C
            Chain complex with at most 1 nonzero terms over Rational Field
            sage: ascii_art(C)
             0 <-- C_0 <-- 0
        """
        from sage.homology.chain_complex import ChainComplex
        from sage.matrix.constructor import Matrix
        E = self.domain()
        n = E.ngens()
        if R is None:
            R = E.base_ring()

        if n == 0:
            # Special case because there are no matrices and thus the
            # ChainComplex constructor needs the dimension of the
            # 0th degree space explicitly given.
            return ChainComplex({-1: Matrix(R, [[]])}, degree=1)
            # If you are reading this because you changed something about
            # the ChainComplex constructor and the doctests are failing:
            # This should return a chain complex with degree 1 and
            # only one nontrivial module, namely a free module of rank 1,
            # situated in degree 0.

        # Group the basis into degrees
        basis_by_deg = {deg: [] for deg in range(n+1)}
        for b in E.basis().keys():
            basis_by_deg[len(b)].append(b)

        # Construct the transition matrices
        data = {}
        basis = basis_by_deg[0]
        for deg in range(n):
            # Make sure within each basis we're sorted by lex
            next_basis = sorted(basis_by_deg[deg+1])
            mat = []
            for b in basis:
                ret = self._on_basis(b)
                mat.append([ret[p] for p in next_basis])
            data[deg] = Matrix(mat).transpose().change_ring(R)
            basis = next_basis

        return ChainComplex(data, degree=1)

