# -*- coding: utf-8 -*-
r"""
An element in an indexed free module.

AUTHORS:

- Travis Scrimshaw (03-2017): Moved code from :mod:`sage.combinat.free_module`.
"""

#*****************************************************************************
#       Copyright (C) 2017 Travis Scrimshaw <tcscrims at gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#                  http://www.gnu.org/licenses/
#*****************************************************************************

from sage.structure.element cimport parent
from sage.structure.richcmp cimport richcmp, rich_to_bool
from cpython.object cimport Py_NE, Py_EQ

from sage.misc.repr import repr_lincomb
from sage.misc.cachefunc import cached_method
from sage.misc.lazy_attribute import lazy_attribute
from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art
from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art
from sage.categories.all import Category, Sets, ModulesWithBasis
from sage.data_structures.blas_dict cimport add, negate, scal, axpy

cdef class IndexedFreeModuleElement(ModuleElement):
    def __init__(self, M, x):
        """
        Create a combinatorial module element. This should never be
        called directly, but only through the parent combinatorial
        free module's :meth:`__call__` method.

        TESTS::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']; f
            B['a'] + 3*B['c']
            sage: f == loads(dumps(f))
            True
        """
        ModuleElement.__init__(self, M)
        self._monomial_coefficients = x
        self._hash_set = False

    def __iter__(self):
        """
        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: [i for i in sorted(f)]
            [('a', 1), ('c', 3)]

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: a = s([2,1]) + s([3])
            sage: [i for i in sorted(a)]
            [([2, 1], 1), ([3], 1)]
        """
        return iter(self._monomial_coefficients.iteritems())

    def __contains__(self, x):
        """
        Returns whether or not a combinatorial object x indexing a basis
        element is in the support of self.

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: 'a' in f
            True
            sage: 'b' in f
            False

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: a = s([2,1]) + s([3])
            sage: Partition([2,1]) in a
            True
            sage: Partition([1,1,1]) in a
            False
        """
        return x in self._monomial_coefficients and self._monomial_coefficients[x] != 0

    def __hash__(self):
        """
        Return the hash value for ``self``.

        The result is cached.

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: hash(f) == hash(B['a'] + 3*B['c'])
            True
            sage: hash(f) == hash(B['a'] + 4*B['c'])
            False

            sage: F = RootSystem(['A',2]).ambient_space()
            sage: f = F.simple_root(0)
            sage: hash(f) == hash(F.simple_root(0))
            True
            sage: hash(f) == hash(F.simple_root(1))
            False

        This uses the recipe that was proposed for frozendicts in
        :pep:`416` (and adds the hash of the parent). This recipe
        relies on the hash function for frozensets which uses tricks
        to mix the hash values of the items in case they are similar.

        .. TODO::

            It would be desirable to make the hash value depend on the
            hash value of the parent. See :trac:`15959`.
        """
        if not self._hash_set:
            self._hash = hash(frozenset(self._monomial_coefficients.iteritems()))
            self._hash_set = True
        return self._hash

    def __reduce__(self):
        """
        For pickling.

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: loads(dumps(F.an_element())) == F.an_element()
            True
        """
        return (_unpickle_element, (self._parent, self._monomial_coefficients))

    def __setstate__(self, state):
        r"""
        For unpickling old ``CombinatorialFreeModuleElement`` classes.
        See :trac:`22632` and register_unpickle_override below.

        EXAMPLES::

            sage: loads(b'x\x9c\x95R\xcbn\x131\x14\xd5\x00\r\x89KK\xcb\xa3'
            ....: b'\xbc\xa1\xbc\xd3\xcd,\xe0\x0f\n\xad\xc4\xa2Y\x0c\xb2XZ'
            ....: b'\x8e\xe7N\xe6\x8a\xb1\xa7\xd7\x0f\x91,F\x82E&\xe2\xafq3'
            ....: b'\x13\xa4"X\xb0\xb1}\xae}\xce=\xf7\xc8\xdf\xaf(\'g\x90:o'
            ....: b'\x83\xf2\xc1B\x9a/\x8c\xd4\xa8\x84\xaa\xa4s\xec2\xa2d'
            ....: b'\xcc\xdf\x7f\xa8\xf5\x14\x8d\xf4\xb5EY\x9dZ\x80\xb3:'
            ....: b'\x0f\x15\x88o\xe8K\xa1\xa4\x87Ym\x17)T\xa0\xc1\xf8\x8eH}'
            ....: b'\x17\xd5S\xd3"\xd2\x84^\xf3\xd8?\xf4N:\x01FW\x95\x10\xd3'
            ....: b'\x80\x95G#\x04\x9b\x81\x97\xde[F\xd7:I\x8dN\xad\x17\xa6dU'
            ....: b'\t\r\xbe\xacsF[\xe5\xd6\x9f\x83\x05\x83\x14@X8\xb7\xe0'
            ....: b'\xa2\xb2\xf4X\x1b\x16\x8c\x85<(`4\xe8=v\x13 \xb8\xb43'
            ....: b'\xe8\xd8Y\xbf\xd3\xf5\xee\x89E3s)\x9a\xf8\x10\xac\xb8@'
            ....: b'\xecS\x07\xb2\x8b3\r\x8f2\x1a-\x1bb|\x98\xa3;\x97^\x95'
            ....: b'\xb4\xfd\xd3\xad\xe8FF;|\xbbKJ\xce\xb1\xd6\xb4\xcbG_":'
            ....: b'\x96\x0e\x1d\xdd\\e\xb4W\xee\xf2\xfdS4\xe8\xe1#\xc6\x00'
            ....: b'\\4)+\xda\x8fW\xb7\xf8\xce\xe5To\xb7\x19\xddi\xe9\xeed2'
            ....: b'\xf1\x19\x1d\x1c\xfd\xa0{\xe5\xe0\xff\x93ft\xbf\x1cm\x88'
            ....: b'\x0e\xbcK\x8bu\x7f\x01&h\xb01\x8f\\\xc42\xeb\\\x9d\xfc.~'
            ....: b'\x8e5z\xc0\x939O\x16-=\\6+z\x94\xd1\xe3\xb6\xa1\'c>\xdc'
            ....: b'\xfc\x04zZ\xee\xf1A\xcc\xbc\xc09=\xe3\xc9qX\xd1aF\xcf'
            ....: b'\x1bz\xc1\x0f\xa23S\xeb\xe8F\xa8\x1a\x8a\x02\x15\xc6\xe9'
            ....: b'\x1c\xbdl\xe8\xd58\xaa\xfe%n\xa6\xe5W\x10\x1b@\xafy\xf2n'
            ....: b'\x99\xd1\x9b\xe8\xa2\xec\xcfo\x83k\xa7\xe9/\xc1\xe1\t\x17')
            2*B['x'] + 2*B['y']
        """
        self._set_parent(state[0])
        for k, v in state[1].iteritems():
            setattr(self, k, v)

    cpdef dict monomial_coefficients(self, bint copy=True):
        """
        Return the internal dictionary which has the combinatorial objects
        indexing the basis as keys and their corresponding coefficients as
        values.

        INPUT:

        - ``copy`` -- (default: ``True``) if ``self`` is internally
          represented by a dictionary ``d``, then make a copy of ``d``;
          if ``False``, then this can cause undesired behavior by
          mutating ``d``

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: d = f.monomial_coefficients()
            sage: d['a']
            1
            sage: d['c']
            3

        To run through the monomials of an element, it is better to
        use the idiom::

            sage: for (t,c) in f:
            ....:     print("{} {}".format(t,c))
            a 1
            c 3

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: a = s([2,1])+2*s([3,2])
            sage: d = a.monomial_coefficients()
            sage: type(d)
            <... 'dict'>
            sage: d[ Partition([2,1]) ]
            1
            sage: d[ Partition([3,2]) ]
            2
        """
        if copy:
            return dict(self._monomial_coefficients)
        return self._monomial_coefficients

    def _sorted_items_for_printing(self):
        """
        Returns the items (i.e terms) of ``self``, sorted for printing

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 2*B['c'] + 3 * B['b']
            sage: f._sorted_items_for_printing()
            [('a', 1), ('b', 3), ('c', 2)]
            sage: F.print_options(sorting_reverse=True)
            sage: f._sorted_items_for_printing()
            [('c', 2), ('b', 3), ('a', 1)]
            sage: F.print_options(sorting_reverse=False) #reset to original state

        .. SEEALSO:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options`
        """
        print_options = self._parent.print_options()
        v = list(self._monomial_coefficients.iteritems())
        try:
            v.sort(key=lambda monomial_coeff:
                        print_options['sorting_key'](monomial_coeff[0]),
                   reverse=print_options['sorting_reverse'])
        except Exception: # Sorting the output is a plus, but if we can't, no big deal
            pass
        return v

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

            sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='F')
            sage: e = F.basis()
            sage: e['a'] + 2*e['b'] # indirect doctest
            F['a'] + 2*F['b']
            sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='')
            sage: e = F.basis()
            sage: e['a'] + 2*e['b'] # indirect doctest
            ['a'] + 2*['b']
            sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='', scalar_mult=' ', bracket=False)
            sage: e = F.basis()
            sage: e['a'] + 2*e['b'] # indirect doctest
            'a' + 2 'b'

        Controling the order of terms by providing a comparison
        function on elements of the support::

            sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'],
            ....:                             sorting_reverse=True)
            sage: e = F.basis()
            sage: e['a'] + 3*e['b'] + 2*e['c']
            2*B['c'] + 3*B['b'] + B['a']

            sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'],
            ....:                             sorting_key=lambda x: x[1])
            sage: e = F.basis()
            sage: e['ac'] + 3*e['ba'] + 2*e['cb']
            3*B['ba'] + 2*B['cb'] + B['ac']
        """
        return repr_lincomb(self._sorted_items_for_printing(),
                            scalar_mult=self._parent._print_options['scalar_mult'],
                            repr_monomial = self._parent._repr_term,
                            strip_one = True)

    def _ascii_art_(self):
        """
        TESTS::

            sage: M = QuasiSymmetricFunctions(QQ).M()
            sage: ascii_art(M[1,3]**2)  # indirect doctest
            4*M      + 2*M       + 2*M      + 2*M       + 2*M       + M
                 ***      ******        ***         ***         ***     ******
               ***        *             *        ****         ***      **
               *          *           ***        *           **
               *                      *
            sage: ascii_art(M.zero())
            0
        """
        from sage.misc.repr import coeff_repr
        terms = self._sorted_items_for_printing()
        scalar_mult = self._parent._print_options['scalar_mult']
        repr_monomial = self._parent._ascii_art_term
        strip_one = True

        if repr_monomial is None:
            repr_monomial = str

        chunks = []
        first = True

        if scalar_mult is None:
            scalar_mult = "*"

        for (monomial,c) in terms:
            b = repr_monomial(monomial) # PCR
            if c != 0:
                break_points = []
                coeff = coeff_repr(c, False)
                if coeff != "0":
                    if coeff == "1":
                        coeff = ""
                    elif coeff == "-1":
                        coeff = "-"
                    elif b._l > 0:
                        if len(coeff) > 0 and monomial == 1 and strip_one:
                            b = empty_ascii_art # ""
                        else:
                            b = AsciiArt([scalar_mult]) + b
                    if not first:
                        if len(coeff) > 0 and coeff[0] == "-":
                            coeff = " - %s"%coeff[1:]
                        else:
                            coeff = " + %s"%coeff
                        break_points = [2]
                    else:
                        coeff = "%s"%coeff
                if coeff:
                    chunks.append(AsciiArt([coeff], break_points))
                if b._l:
                    chunks.append(b)
                first = False
        s = ascii_art(*chunks)
        if first:
            return AsciiArt(["0"])
        elif s == empty_ascii_art:
            return AsciiArt(["1"])
        else:
            return s

    def _unicode_art_(self):
        """
        TESTS::

            sage: M = QuasiSymmetricFunctions(QQ).M()
            sage: unicode_art(M[1,1]**2)  # indirect doctest
            6*M   + 2*M    + 2*M    + 2*M    + M
               ┌┐      ┌┬┐       ┌┐       ┌┐     ┌┬┐
               ├┤      ├┼┘      ┌┼┤       ├┤    ┌┼┼┘
               ├┤      ├┤       ├┼┘      ┌┼┤    └┴┘
               ├┤      └┘       └┘       └┴┘
               └┘

        The following test failed before :trac:`26850`::

            sage: unicode_art([M.zero()])  # indirect doctest
            [ 0 ]
        """
        from sage.misc.repr import coeff_repr
        terms = self._sorted_items_for_printing()
        scalar_mult = self._parent._print_options['scalar_mult']
        repr_monomial = self._parent._unicode_art_term
        strip_one = True

        if repr_monomial is None:
            repr_monomial = str

        chunks = []
        first = True

        if scalar_mult is None:
            scalar_mult = "*"

        for (monomial, c) in terms:
            b = repr_monomial(monomial)  # PCR
            if c != 0:
                break_points = []
                coeff = coeff_repr(c, False)
                if coeff != "0":
                    if coeff == "1":
                        coeff = ""
                    elif coeff == "-1":
                        coeff = "-"
                    elif b._l > 0:
                        if len(coeff) > 0 and monomial == 1 and strip_one:
                            b = empty_unicode_art  # ""
                        else:
                            b = UnicodeArt([scalar_mult]) + b
                    if not first:
                        if len(coeff) > 0 and coeff[0] == "-":
                            coeff = " - %s" % coeff[1:]
                        else:
                            coeff = " + %s" % coeff
                        break_points = [2]
                    else:
                        coeff = "%s" % coeff
                if coeff:
                    chunks.append(UnicodeArt([coeff], break_points))
                if b._l:
                    chunks.append(b)
                first = False
        s = unicode_art(*chunks)
        if first:
            return UnicodeArt(["0"])
        elif s == empty_unicode_art:
            return UnicodeArt(["1"])
        else:
            return s

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

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: latex(f)
            B_{a} + 3B_{c}

        ::

            sage: QS3 = SymmetricGroupAlgebra(QQ,3)
            sage: a = 2 + QS3([2,1,3])
            sage: latex(a) #indirect doctest
            2[1, 2, 3] + [2, 1, 3]

       ::

            sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='beta', latex_prefix='\\beta')
            sage: x = F.an_element()
            sage: x
            2*beta['a'] + 2*beta['b']
            sage: latex(x)
            2\beta_{a} + 2\beta_{b}

        Controling the order of terms by providing a comparison
        function on elements of the support::

            sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'],
            ....:                             sorting_reverse=True)
            sage: e = F.basis()
            sage: latex(e['a'] + 3*e['b'] + 2*e['c'])
            2B_{c} + 3B_{b} + B_{a}

            sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'],
            ....:                             sorting_key=lambda x: x[1])
            sage: e = F.basis()
            sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb'])
            3B_{ba} + 2B_{cb} + B_{ac}
        """
        return repr_lincomb(self._sorted_items_for_printing(),
                            scalar_mult       = self._parent._print_options['scalar_mult'],
                            latex_scalar_mult = self._parent._print_options['latex_scalar_mult'],
                            repr_monomial = self._parent._latex_term,
                            is_latex=True, strip_one=True)

    cpdef _richcmp_(self, other, int op):
        """
        Rich comparison for equal parents.

        EXAMPLES::

            sage: F1 = CombinatorialFreeModule(QQ, [1, 2, 3])
            sage: F2 = CombinatorialFreeModule(QQ, [1, 2, 3], prefix = "g")
            sage: F1.zero() == F1.zero()
            True
            sage: F1.zero() == F1.an_element()
            False
            sage: F1.an_element() == F1.an_element()
            True
            sage: F1.an_element() is None
            False

        ::

            sage: F3 = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: F3.an_element() != F3.an_element()
            False
            sage: F3.an_element() != F3.zero()
            True

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: a = s([2,1])
            sage: b = s([1,1,1])
            sage: a == b
            False

        .. TODO::

            Currently, if ``self`` and ``other`` do not have the same parent,
            seemingly equal elements do not evaluate equal, since conversions
            between different modules have not been established.

        ::

            sage: F1.zero() == 0
            True
            sage: F1(0)
            0

        ::

            sage: F1.zero() == F2.zero()
            False
            sage: F1(F2.zero())
            Traceback (most recent call last):
            ...
            TypeError: do not know how to make x (= 0) an element of self (=Free module generated by {1, 2, 3} over Rational Field)
            sage: F = AlgebrasWithBasis(QQ).example()
            sage: F.one() == 1
            True
            sage: 1 == F.one()
            True
            sage: 2 * F.one() == int(2)
            True
            sage: int(2) == 2 * F.one()
            True

            sage: S = SymmetricFunctions(QQ); s = S.s(); p = S.p()
            sage: p[2] == s[2] - s[1, 1]
            True
            sage: p[2] == s[2]
            False

        This feature is disputable, in particular since it can make
        equality testing costly. It may be removed at some point.

        Equality testing can be a bit tricky when the order of terms
        can vary because their indices are incomparable with
        ``cmp``. The following test did fail before :trac:`12489` ::

            sage: F = CombinatorialFreeModule(QQ, Subsets([1,2,3]))
            sage: x = F.an_element()
            sage: (x+F.zero()).terms()  # random
            [2*B[{1}], 3*B[{2}], B[{}]]
            sage: x.terms()             # random
            [2*B[{1}], B[{}], 3*B[{2}]]
            sage: x+F.zero() == x
            True

        TESTS::

            sage: TestSuite(F1).run()
            sage: TestSuite(F).run()
        """
        cdef IndexedFreeModuleElement elt = <IndexedFreeModuleElement> other

        if self._monomial_coefficients == elt._monomial_coefficients:
            return rich_to_bool(op, 0)

        # Not equal
        if op == Py_EQ:
            return False
        if op == Py_NE:
            return True

        v = sorted(self._monomial_coefficients.iteritems())
        w = sorted(elt._monomial_coefficients.iteritems())
        return richcmp(v, w, op)

    cpdef _add_(self, other):
        """
        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: B['a'] + 3*B['c']
            B['a'] + 3*B['c']

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: s([2,1]) + s([5,4]) # indirect doctest
            s[2, 1] + s[5, 4]
            sage: a = s([2,1]) + 0
            sage: len(a.monomial_coefficients())
            1
        """
        return type(self)(self._parent,
                          add(self._monomial_coefficients,
                              (<IndexedFreeModuleElement>other)._monomial_coefficients))

    cpdef _neg_(self):
        """
        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] + 3*B['c']
            sage: -f
            -B['a'] - 3*B['c']

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: -s([2,1]) # indirect doctest
            -s[2, 1]
        """
        return type(self)(self._parent, negate(self._monomial_coefficients))

    cpdef _sub_(self, other):
        """
        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: B['a'] - 3*B['c']
            B['a'] - 3*B['c']

        ::

            sage: s = SymmetricFunctions(QQ).schur()
            sage: s([2,1]) - s([5,4]) # indirect doctest
            s[2, 1] - s[5, 4]
        """
        return type(self)(self._parent,
                          axpy(-1,
                               (<IndexedFreeModuleElement>other)._monomial_coefficients,
                               self._monomial_coefficients))

    def __getitem__(self, m):
        """
        Return the coefficient of ``m`` in ``self``.

        EXAMPLES::

            sage: p = Partition([2,1])
            sage: q = Partition([1,1,1])
            sage: s = SymmetricFunctions(QQ).schur()
            sage: a = s(p)
            sage: a[p]
            1
            sage: a[q]
            0
            sage: a[[2,1]]
            Traceback (most recent call last):
            ...
            TypeError: unhashable type: 'list'
        """
        res = self._monomial_coefficients.get(m)
        if res is None:
            return self.base_ring().zero()
        return res

    def _vector_(self, new_base_ring=None, order=None):
        """
        Returns ``self`` as a dense vector

        INPUT:

        - ``new_base_ring`` -- a ring (default: ``None``)
        - ``order`` -- (optional) an ordering of the support of ``self``

        OUTPUT: a dense :func:`FreeModule` vector

        .. WARNING:: This will crash/run forever if ``self`` is infinite dimensional!

        .. SEEALSO::

            - :func:`vector`
            - :meth:`CombinatorialFreeModule.get_order`
            - :meth:`CombinatorialFreeModule.from_vector`
            - :meth:`CombinatorialFreeModule._dense_free_module`

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: f = B['a'] - 3*B['c']
            sage: f._vector_()
            (1, 0, -3)

        One can use equivalently::

            sage: f.to_vector()
            (1, 0, -3)
            sage: vector(f)
            (1, 0, -3)

        More examples::

            sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
            sage: a = 2*QS3([1,2,3]) + 4*QS3([3,2,1])
            sage: a._vector_()
            (2, 0, 0, 0, 0, 4)
            sage: a.to_vector()
            (2, 0, 0, 0, 0, 4)
            sage: vector(a)
            (2, 0, 0, 0, 0, 4)
            sage: a == QS3.from_vector(a.to_vector())
            True

        If ``new_base_ring`` is specified, then a vector over
        ``new_base_ring`` is returned::

            sage: a._vector_(RDF)
            (2.0, 0.0, 0.0, 0.0, 0.0, 4.0)

        .. NOTE::

            :trac:`13406`: the current implementation has been optimized, at
            the price of breaking the encapsulation for FreeModule
            elements creation, with the following use case as metric,
            on a 2008' Macbook Pro::

                sage: F = CombinatorialFreeModule(QQ, range(10))
                sage: f = F.an_element()
                sage: %timeit f._vector_()   # not tested
                625 loops, best of 3: 17.5 micros per loop

             Other use cases may call for different or further
             optimizations.
        """
        dense_free_module = self._parent._dense_free_module(new_base_ring)
        d = self._monomial_coefficients
        zero = dense_free_module.base_ring().zero()
        if order is None:
            order = self._parent.get_order()
        return dense_free_module.element_class(dense_free_module,
                                               [d.get(m, zero) for m in order],
                                               coerce=True, copy=False)

    to_vector = _vector_

    cpdef _acted_upon_(self, scalar, bint self_on_left):
        """
        Return the action of ``scalar`` (an element of the base ring) on
        ``self``.

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
            sage: B = F.basis()
            sage: B['a']*(1/2)  # indirect doctest
            1/2*B['a']
            sage: B['a']/2
            1/2*B['a']
            sage: B['a']*2      # indirect doctest
            2*B['a']
            sage: B['a']*int(2) # indirect doctest
            2*B['a']

            sage: 1/2*B['a']
            1/2*B['a']
            sage: 2*B['a']      # indirect doctest
            2*B['a']
            sage: int(2)*B['a'] # indirect doctest
            2*B['a']

        TESTS::

            sage: coercion_model.get_action(F, QQ, operator.mul)
            Right scalar multiplication by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field
            sage: coercion_model.get_action(QQ, F, operator.mul)
            Left scalar multiplication by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field
            sage: coercion_model.get_action(F, ZZ, operator.mul)
            Right scalar multiplication by Integer Ring on Free module generated by {'a', 'b', 'c'} over Rational Field
            sage: print(coercion_model.get_action(F, F, operator.mul))
            None

        This also works when a coercion of the coefficient is needed, for
        example with polynomials or fraction fields (:trac:`8832`)::

            sage: P.<q> = QQ['q']
            sage: V = CombinatorialFreeModule(P, Permutations())
            sage: el = V(Permutation([3,1,2]))
            sage: (3/2)*el
            3/2*B[[3, 1, 2]]

            sage: P.<q> = QQ['q']
            sage: F = FractionField(P)
            sage: V = CombinatorialFreeModule(F, Words())
            sage: w = Words()('abc')
            sage: (1+q)*V(w)
            (q+1)*B[word: abc]
            sage: ((1+q)/q)*V(w)
            ((q+1)/q)*B[word: abc]

        .. TODO::

            Add non commutative tests.
        """
        # With the current design, the coercion model does not have
        # enough information to detect a priori that this method only
        # accepts scalars; so it tries on some elements(), and we need
        # to make sure to report an error.
        if isinstance(scalar, Element) and parent(scalar) is not self.base_ring():
            # Temporary needed by coercion (see Polynomial/FractionField tests).
            if self.base_ring().has_coerce_map_from(parent(scalar)):
                scalar = self.base_ring()( scalar )
            else:
                return None

        return type(self)(self._parent,
                          scal(scalar, self._monomial_coefficients,
                               factor_on_left=not self_on_left))

    cpdef _lmul_(self, Element right):
        """
        For backward compatibility.

        EXAMPLES::

            sage: C = CombinatorialFreeModule(QQ, [1,2,3])
            sage: C.an_element()._lmul_(2)
            4*B[1] + 4*B[2] + 6*B[3]
        """
        return self._acted_upon_(right, True)

    cpdef _rmul_(self, Element left):
        """
        For backward compatibility.

        EXAMPLES::

            sage: C = CombinatorialFreeModule(QQ, [1,2,3])
            sage: C.an_element()._rmul_(2)
            4*B[1] + 4*B[2] + 6*B[3]
        """
        return self._acted_upon_(left, False)

    def __truediv__(left, x):
        """
        Division by coefficients.

        EXAMPLES::

            sage: F = CombinatorialFreeModule(QQ, [1,2,3])
            sage: x = F._from_dict({1:2, 2:3})
            sage: from operator import truediv
            sage: truediv(x, 2)
            B[1] + 3/2*B[2]

        ::

            sage: F = CombinatorialFreeModule(QQ, [1,2,3])
            sage: B = F.basis()
            sage: f = 2*B[2] + 4*B[3]
            sage: truediv(f, 2)
            B[2] + 2*B[3]

        TESTS::

            sage: truediv(x, x)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert 2*B[1] + 3*B[2] to a rational
            sage: truediv("hello", x)
            Traceback (most recent call last):
            ...
            TypeError: unsupported operand type(s) for /: 'str' and 'CombinatorialFreeModule_with_category.element_class'
        """
        if not isinstance(left, IndexedFreeModuleElement):
            return NotImplemented

        cdef IndexedFreeModuleElement self = <IndexedFreeModuleElement>left
        F = self._parent
        B = self.base_ring()
        D = self._monomial_coefficients
        if not B.is_field():
            return type(self)(F, {k: c._divide_if_possible(x)
                                  for k, c in D.iteritems()})

        x_inv = B(x) ** -1
        return type(self)(F, scal(x_inv, D))

def _unpickle_element(C, d):
    """
    Unpickle an element in ``C`` given by ``d``.

    EXAMPLES::

        sage: from sage.modules.with_basis.indexed_element import _unpickle_element
        sage: C = CombinatorialFreeModule(QQ, [1,2,3])
        sage: _unpickle_element(C, {1: -2, 3: -12})
        -2*B[1] - 12*B[3]
    """
    return C._from_dict(d, coerce=False, remove_zeros=False)

# Handle old CombinatorialFreeModuleElement pickles, see trac #22632
from sage.misc.persist import register_unpickle_override
register_unpickle_override("sage.combinat.free_module",
                           "CombinatorialFreeModuleElement",
                           IndexedFreeModuleElement)
