# sage_setup: distribution = sagemath-combinat
# sage.doctest: needs sage.combinat sage.modules
"""
`k`-Schur functions
"""
# ****************************************************************************
#       Copyright (C) 2011 Jason Bandlow <jbandlow@gmail.com>,
#                     2012 Anne Schilling <anne@math.ucdavis.edu>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#
#    This code is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    General Public License for more details.
#
#  The full text of the GPL is available at:
#
#                  https://www.gnu.org/licenses/
# ****************************************************************************
from sage.arith.srange import srange
from sage.categories.graded_coalgebras import GradedCoalgebras
from sage.categories.graded_coalgebras_with_basis import GradedCoalgebrasWithBasis
from sage.categories.graded_hopf_algebras import GradedHopfAlgebras
from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis
from sage.categories.homset import Hom
from sage.categories.morphism import SetMorphism
from sage.categories.realizations import Category_realization_of_parent, Realizations
from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
from sage.categories.tensor import tensor
from sage.combinat.free_module import CombinatorialFreeModule
from sage.combinat.partition import Partition, Partitions, Partitions_all_bounded
from sage.combinat.sf.sf import SymmetricFunctions
from sage.cpython.getattr import raw_getattr
from sage.matrix.constructor import matrix
from sage.misc.cachefunc import cached_method
from sage.misc.constant_function import ConstantFunction
from sage.misc.misc_c import prod
from sage.rings.integer import Integer
from sage.rings.integer_ring import ZZ
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation


class KBoundedSubspace(UniqueRepresentation, Parent):
    r"""
    This class implements the subspace of the ring of symmetric functions spanned by
    `\{ s_{\lambda}[X/(1-t)] \}_{\lambda_1\le k} = \{ s_{\lambda}^{(k)}[X;t]\}_{\lambda_1 \le k}`
    over the base ring `\QQ[t]`. When `t=1`, this space is in fact a subring of
    the ring of symmetric functions generated by the complete homogeneous symmetric functions
    `h_i` for `1\le i \le k`.

    EXAMPLES::

        sage: Sym = SymmetricFunctions(QQ)
        sage: KB = Sym.kBoundedSubspace(3,1); KB
        3-bounded Symmetric Functions over Rational Field with t=1

        sage: Sym = SymmetricFunctions(QQ['t'])
        sage: KB = Sym.kBoundedSubspace(3); KB
        3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field

    The `k`-Schur function basis can be constructed as follows::

        sage: ks = KB.kschur(); ks
        3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-Schur basis
    """

    def __init__(self, Sym, k, t='t'):
        r"""
        The class modeling the abstract vector space of `k`-Schur
        functions.

        If `t=1` this is actually an abstract ring. Another
        way to describe this space is as the subspace of a ring of
        symmetric functions generated by the complete homogeneous
        symmetric functions `h_i` for `1\le i \le k`.

        TESTS::

            sage: Sym = SymmetricFunctions(QQ)
            sage: from sage.combinat.sf.new_kschur import KBoundedSubspace
            sage: L3 = KBoundedSubspace(Sym,3,1)
            sage: TestSuite(L3).run(skip=["_test_not_implemented_methods"])
            sage: Sym.kBoundedSubspace(0,1)
            Traceback (most recent call last):
            ...
            ValueError: k must be a positive integer

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: TestSuite(Sym.kBoundedSubspace(1)).run(skip=["_test_not_implemented_methods"])
        """
        if not isinstance(k, (int, Integer)) or (k < 1):
            raise ValueError("k must be a positive integer")

        if not isinstance(Sym, SymmetricFunctions):
            raise ValueError("Sym must be an algebra of symmetric functions")

        self.indices = ConstantFunction(Partitions_all_bounded(k))

        R = Sym.base_ring()

        # The following line is a work around for the fact that Parent defines
        # self.base_ring as NotImplemented, hence it cannot be defined by the
        # category framework.
        self.base_ring = ConstantFunction(R)

        self.ambient = ConstantFunction(Sym)

        self.k = k
        self.t = R(t)

        category = GradedHopfAlgebras(R) if t == 1 else GradedCoalgebras(R)
        Parent.__init__(self, category=category.Subobjects().WithRealizations())

        ks = self.kschur()
        # Coercions
        if t == 1:
            s = ks.ambient()
            kh = self.khomogeneous()
            h = kh.ambient()
            h_to_s = s._internal_coerce_map_from(h)
            kh_to_ks = ks.retract * h_to_s * kh.lift
            ks.register_coercion(kh_to_ks)
            s_to_h = h._internal_coerce_map_from(s)
            ks_to_kh = kh.retract * s_to_h * ks.lift
            kh.register_coercion(ks_to_kh)
        # temporary workaround until handled by trac 125959
            self.one = ConstantFunction(ks.one())
        self.zero = ConstantFunction(ks.zero())

    def retract(self, sym):
        r"""
        Return the retract of ``sym`` from the ring of symmetric functions to ``self``.

        INPUT:

        - ``sym`` -- a symmetric function

        OUTPUT:

        - the analogue of the symmetric function in the `k`-bounded
          subspace (if possible)

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)
            sage: s = Sym.schur()
            sage: KB = Sym.kBoundedSubspace(3,1); KB
            3-bounded Symmetric Functions over Rational Field with t=1
            sage: KB.retract(s[2]+s[3])
            ks3[2] + ks3[3]
            sage: KB.retract(s[2,1,1])
            Traceback (most recent call last):
            ...
            ValueError: s[2, 1, 1] is not in the image
        """
        s = self.ambient().schur()
        ks = self.kschur()
        return ks.retract(s(sym))

    def realizations(self):
        r"""
        A list of realizations of this algebra.

        EXAMPLES::

            sage: SymmetricFunctions(QQ).kBoundedSubspace(3,1).realizations()
            [3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis,
             3-bounded Symmetric Functions over Rational Field with t=1 in the 3-split basis,
             3-bounded Symmetric Functions over Rational Field with t=1 in the 3-bounded homogeneous basis,
             3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis]
            sage: SymmetricFunctions(QQ['t']).kBoundedSubspace(3).realizations()
            [3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-Schur basis,
             3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-split basis]
        """
        if self.t == 1:
            return [self.kschur(), self.ksplit(), self.khomogeneous(),
                self.K_kschur()]
        else:
            return [self.kschur(), self.ksplit()]

    def kschur(self):
        r"""
        The `k`-Schur basis of this algebra.

        .. SEEALSO:: :meth:`kSchur`

        EXAMPLES::

            sage: ks3 = SymmetricFunctions(QQ).kBoundedSubspace(3,1).kschur()
            sage: TestSuite(ks3).run()
        """
        return kSchur(self)

    def ksplit(self):
        r"""
        The `k`-split basis of this algebra.

        .. SEEALSO:: :meth:`kSplit`

        EXAMPLES::

            sage: ksp3 = SymmetricFunctions(QQ).kBoundedSubspace(3,1).ksplit()
            sage: TestSuite(ksp3).run()
        """
        return kSplit(self)

    def khomogeneous(self):
        r"""
        The homogeneous basis of this algebra.

        .. SEEALSO:: :meth:`kHomogeneous`

        EXAMPLES::

            sage: kh3 = SymmetricFunctions(QQ).kBoundedSubspace(3,1).khomogeneous()
            sage: TestSuite(kh3).run()
        """
        if self.t != 1:
            raise ValueError("This basis only exists for t=1")
        return kHomogeneous(self)

    def K_kschur(self):
        r"""
        Return the `k`-bounded basis called the K-`k`-Schur basis.

        See [Morse11]_ and [LamSchillingShimozono10]_.

        REFERENCES:

        .. [Morse11] \J. Morse, Combinatorics of the K-theory of affine Grassmannians,
           Adv. in Math., Volume 229, Issue 5, pp. 2950--2984.

        .. [LamSchillingShimozono10] \T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian,
           Compositio Math. 146 (2010), 811-852.

        EXAMPLES::

            sage: kB = SymmetricFunctions(QQ).kBoundedSubspace(3,1)
            sage: g = kB.K_kschur()
            sage: g
            3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis
            sage: kB = SymmetricFunctions(QQ['t']).kBoundedSubspace(3)
            sage: g = kB.K_kschur()
            Traceback (most recent call last):
            ...
            ValueError: This basis only exists for t=1
        """
        if self.t != 1:
            raise ValueError("This basis only exists for t=1")
        return K_kSchur(self)

    def _repr_(self):
        r"""
        Representation of this algebra.

        EXAMPLES::

            sage: SymmetricFunctions(QQ).kBoundedSubspace(3,1) # indirect doctest
            3-bounded Symmetric Functions over Rational Field with t=1

            sage: SymmetricFunctions(QQ['t']).kBoundedSubspace(3)
            3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field
        """
        ending = ""
        if str(self.t) != 't':
            ending = ' with t=%s' % (self.t)
        return "%s-bounded %s" % (self.k, self.ambient()) + ending


class KBoundedSubspaceBases(Category_realization_of_parent):
    r"""
    The category of bases for the `k`-bounded subspace of symmetric functions.
    """

    def __init__(self, base, t='t'):
        """
        Initialization of the bases of the `k`-bounded subspace.

        INPUT:

        - ``base`` -- a basis in the `k`-bounded subspace
        - ``t`` -- a parameter (default: ``'t'``)

        TESTS::

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: from sage.combinat.sf.new_kschur import KBoundedSubspaceBases
            sage: KB = Sym.kBoundedSubspace(3)
            sage: KBB = KBoundedSubspaceBases(KB); KBB
            Category of k bounded subspace bases of 3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field
        """
        self.t = t
        Category_realization_of_parent.__init__(self, base)

    def super_categories(self):
        r"""
        The super categories of ``self``.

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: from sage.combinat.sf.new_kschur import KBoundedSubspaceBases
            sage: KB = Sym.kBoundedSubspace(3)
            sage: KBB = KBoundedSubspaceBases(KB); KBB
            Category of k bounded subspace bases of 3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field
            sage: KBB.super_categories()
            [Category of realizations of 3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field,
             Join of Category of graded coalgebras with basis over Univariate Polynomial Ring in t over Rational Field
                 and Category of subobjects of filtered modules with basis over Univariate Polynomial Ring in t over Rational Field]
        """
        R = self.base().base_ring()
        category = GradedHopfAlgebrasWithBasis(R) if self.t == 1 else GradedCoalgebrasWithBasis(R)
        return [Realizations(self.base()), category.Subobjects()]

    class ParentMethods:

        def _element_constructor_(self, x):
            r"""
            Needed to rewrite the element constructor because of a bug in free_module.py.
            Ideally _element_constructor_ would be inherited from free_module.py, but
            it allows for bad inputs.

            EXAMPLES::

                sage: kB = SymmetricFunctions(QQ).kBoundedSubspace(3,1)
                sage: ks = kB.kschur()
                sage: ks([2,1])
                ks3[2, 1]
                sage: ks([4,1])
                Traceback (most recent call last):
                ...
                TypeError: do not know how to make x (= [4, 1]) an element of self (=3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis)
                sage: ks(Partition([4,1]))
                Traceback (most recent call last):
                ...
                TypeError: do not know how to make x (= [4, 1]) an element of self (=3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis)
            """
            R = self.base_ring()

            # Coerce ints to Integers
            if isinstance(x, int):
                x = Integer(x)
            if x in R:
                if x == 0:
                    return self.zero()
                else:
                    raise TypeError("do not know how to make x (= %s) an element of %s" % (x, self))
            # x is an element of the basis enumerated set;
            elif x in self._indices:
                return self.monomial(self._indices(x))
            raise TypeError("do not know how to make x (= %s) an element of self (=%s)" % (x, self))

        def _convert_map_from_(self, Q):
            r"""
            Implement conversion from an arbitrary parent to ``self``.

            This is done by first coercing to the appropriate lift basis.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: e = Sym.elementary(); ks3 = Sym.kschur(3,1)
                sage: ks3(e[3, 2])                   # indirect doctest
                ks3[1, 1, 1, 1, 1]
            """
            P = self.lift.codomain()
            if P.has_coerce_map_from(Q):
                return self.retract * P._internal_coerce_map_from(Q)
            return None

        def __getitem__(self, c):
            r"""
            Implement shorthand for accessing basis elements.

            For a basis `X` indexed by partitions, this method allows for
            `X[[3,2]]` and `X[3,2]` to be equivalent to `X[Partition([3,2])]`.

            Due to limitations in Python syntax, one must use `X[[]]` and not
            `X[]` for the basis element indexed by the empty partition.

            EXAMPLES::

                sage: ks3 = SymmetricFunctions(QQ).kschur(3,1)
                sage: ks3[3,2]
                ks3[3, 2]
                sage: ks3[[]]
                ks3[]

            TESTS::

                sage: ks3 = SymmetricFunctions(QQ).kschur(3,1)
                sage: ks3[4,1]
                Traceback (most recent call last):
                ...
                TypeError: do not know how to make [4, 1] an element of 3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis
                sage: ks3[Partition([4,1])]
                Traceback (most recent call last):
                ...
                TypeError: do not know how to make [4, 1] an element of 3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis
            """
            if not isinstance(c, Partition):
                if c in ZZ:
                    c = Partition([c])
                else:
                    c = Partition(c)

            if c not in self._indices:
                raise TypeError("do not know how to make %s an element of %s" % (c, self))
            return self.monomial(c)

        def _repr_term(self, c):
            """
            Display elements with single brackets.

            The default implementation of CombinatorialFreeModule gives double
            brackets for basis elements indexed by partitions, i.e.,
            `X[[3,2]]`.

            EXAMPLES::

                sage: ks3 = SymmetricFunctions(QQ).kschur(3,1)
                sage: ks3[3,2]    # indirect doctest
                ks3[3, 2]
            """
            return self.prefix() + str(c)

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

            EXAMPLES::

                sage: ks3 = SymmetricFunctions(QQ).kschur(3,1)
                sage: ks3.one()  # indirect doctest
                ks3[]
            """
            return Partition([])

        def transition_matrix(self, other, n):
            """
            Return the degree ``n`` transition matrix between ``self`` and ``other``.

            INPUT:

            - ``other`` -- a basis in the ring of symmetric functions
            - ``n`` -- positive integer

            The entry in the `i`-th row and `j`-th column is the
            coefficient obtained by writing the `i`-th element of the
            basis of ``self`` in terms of the basis ``other``, and extracting the
            `j`-th coefficient.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ); s = Sym.schur()
                sage: ks3 = Sym.kschur(3,1)
                sage: ks3.transition_matrix(s,5)
                [1 1 1 0 0 0 0]
                [0 1 0 1 0 0 0]
                [0 0 1 0 1 0 0]
                [0 0 0 1 0 1 0]
                [0 0 0 0 1 1 1]

                sage: Sym = SymmetricFunctions(QQ['t'])
                sage: s = Sym.schur()
                sage: ks = Sym.kschur(3)
                sage: ks.transition_matrix(s,5)
                [t^2   t   1   0   0   0   0]
                [  0   t   0   1   0   0   0]
                [  0   0   t   0   1   0   0]
                [  0   0   0   t   0   1   0]
                [  0   0   0   0 t^2   t   1]
            """
            P = Partitions(n, max_part=self.k)
            # todo: Q should be set by getting the degree n index set for
            # `other`.
            Q = Partitions(n)
            return matrix([[other(self[row]).coefficient(col) for col in Q]
                           for row in P])

        def _an_element_(self):
            r"""
            Return an element of ``self``.

            EXAMPLES::

                sage: SymmetricFunctions(QQ['t']).kschur(3).an_element()
                2*ks3[] + 2*ks3[1] + 3*ks3[2]
            """
            return self(Partition(srange(self.k, 0, -1)))

        # This is sufficient for degree to work
        def degree_on_basis(self, b):
            r"""
            Return the degree of the basis element indexed by `b`.

            INPUT:

            - ``b`` -- a partition

            EXAMPLES::

                sage: ks3 = SymmetricFunctions(QQ).kschur(3,1)
                sage: ks3.degree_on_basis(Partition([3,2]))
                5
            """
            return sum(b)

        def coproduct(self, element):
            r"""
            Return the coproduct operation on ``element``.

            The coproduct is first computed on the homogeneous basis if `t=1`
            and on the Hall-Littlewood ``Qp`` basis otherwise.  The result is
            computed then converted to the tensor squared of ``self.parent()``.

            INPUT:

            - ``element`` -- an element in a basis of the ring of symmetric
              functions

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks3 = Sym.kschur(3,1)
                sage: ks3[2,1].coproduct()
                ks3[] # ks3[2, 1] + ks3[1] # ks3[1, 1] + ks3[1] # ks3[2] + ks3[1, 1] # ks3[1] + ks3[2] # ks3[1] + ks3[2, 1] # ks3[]
                sage: h3 = Sym.khomogeneous(3)
                sage: h3[2,1].coproduct()
                h3[] # h3[2, 1] + h3[1] # h3[1, 1] + h3[1] # h3[2] + h3[1, 1] # h3[1] + h3[2] # h3[1] + h3[2, 1] # h3[]
                sage: ks3t = SymmetricFunctions(FractionField(QQ['t'])).kschur(3)
                sage: ks3t[2,1].coproduct()
                ks3[] # ks3[2, 1] + ks3[1] # ks3[1, 1] + ks3[1] # ks3[2] + ks3[1, 1] # ks3[1] + ks3[2] # ks3[1] + ks3[2, 1] # ks3[]
                sage: ks3t[3,1].coproduct()
                ks3[] # ks3[3, 1] + ks3[1] # ks3[2, 1] + (t+1)*ks3[1] # ks3[3] + ks3[1, 1] # ks3[2] + ks3[2] # ks3[1, 1]
                + (t+1)*ks3[2] # ks3[2] + ks3[2, 1] # ks3[1] + (t+1)*ks3[3] # ks3[1] + ks3[3, 1] # ks3[]
                sage: h3.coproduct(h3[2,1])
                h3[] # h3[2, 1] + h3[1] # h3[1, 1] + h3[1] # h3[2] + h3[1, 1] # h3[1] + h3[2] # h3[1] + h3[2, 1] # h3[]
            """
            lifted = element.lift()
            ambient = self.realization_of().ambient()
            t = self.realization_of().t
            if t == 1:
                source_basis = ambient.h()
            else:
                source_basis = ambient.hall_littlewood(t=t).Qp()

            def cpfunc(x, y):
                return tensor([self(x), self(y)])
            return source_basis(lifted).coproduct().apply_multilinear_morphism(cpfunc)

        def antipode(self, element):
            r"""
            Return the antipode on ``self`` by lifting to the space of
            symmetric functions, computing the antipode, and then converting
            to ``self.parent()``. This is only the antipode for `t = 1` and
            for other values of `t` the result may not be in the space where
            the `k`-Schur functions live.

            INPUT:

            - ``element`` -- an element in a basis of the ring of symmetric
              functions

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks3 = Sym.kschur(3,1)
                sage: ks3[3,2].antipode()
                -ks3[1, 1, 1, 1, 1]
                sage: ks3.antipode(ks3[3,2])
                -ks3[1, 1, 1, 1, 1]
            """
            return self(element.lift().antipode())

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

            The counit is the constant term of ``element``.

            INPUT:

            - ``element`` -- an element in a basis of the ring of symmetric
              functions

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks3 = Sym.kschur(3,1)
                sage: f = 2*ks3[2,1] + 3*ks3[[]]
                sage: f.counit()
                3
                sage: ks3.counit(f)
                3
            """
            return element.coefficient([])

    class ElementMethods:
        def _mul_(self, other):
            r"""
            Return the product of two elements ``self`` and ``other``.

            When `t=1`, the `k`-bounded subspace is an algebra, so the
            product of two elements is always in the space. For generic
            `t`, the `k`-bounded subspace is not closed under
            multiplication, so the result is returned in the `k`-bounded
            subspace if possible and else in the ring of symmetric
            functions.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ['t'])
                sage: ks = Sym.kschur(3)
                sage: ks[2]*ks[2]                        # indirect doctest
                s[2, 2] + s[3, 1] + s[4]
                sage: f = ks[2]*ks[3,1]; f
                s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + (t+1)*s[4, 2] + (t+1)*s[5, 1] + t*s[6]
                sage: f.parent()
                Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the Schur basis
                sage: ks(f)
                Traceback (most recent call last):
                ...
                ValueError: s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + (t+1)*s[4, 2] + (t+1)*s[5, 1] + t*s[6] is not in the image
                sage: Sym = SymmetricFunctions(QQ)
                sage: ks = Sym.kschur(3,1)
                sage: f = ks[2]*ks[3,1]; f
                ks3[3, 2, 1] + ks3[3, 3]
                sage: f.parent()
                3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis

            TESTS::

                sage: Sym = SymmetricFunctions(FractionField(QQ['t']))
                sage: ks2 = Sym.kschur(2)
                sage: ks3 = Sym.kschur(3)
                sage: ks5 = Sym.kschur(5)
                sage: ks5(ks3[2]) * ks5(ks2[2,1])
                ks5[2, 2, 1] + ks5[3, 1, 1] + (t+1)*ks5[3, 2] + (t+1)*ks5[4, 1] + t*ks5[5]

                sage: ks3([1]) * ks3([1]) # indirect doctest
                ks3[1, 1] + ks3[2]
                sage: ks3([2,1]) * ks3([2,1])
                s[2, 2, 1, 1] + s[2, 2, 2] + s[3, 1, 1, 1] + 2*s[3, 2, 1] +
                s[3, 3] + s[4, 1, 1] + s[4, 2]
                sage: ks3 = SymmetricFunctions(QQ).kschur(3, t=1)
                sage: ks3([2,1])^2
                ks3[2, 2, 1, 1] + ks3[2, 2, 2] + ks3[3, 1, 1, 1]
            """
            if self.parent().realization_of().t == 1:
                return self.parent()(self.lift() * other.lift())
            result = self.lift() * other.lift()
            try:
                result = self.parent()(result)
            except ValueError:
                pass
            return result

        def hl_creation_operator(self, nu, t=None):
            r"""
            This is the vertex operator that generalizes Jing's operator.

            It is a linear operator that raises the degree by
            `|\nu|`. This creation operator is a t-analogue of
            multiplication by ``s(nu)`` .

            .. SEEALSO:: Proposition 5 in [SZ2001]_.

            INPUT:

            - ``nu`` -- a partition or a list of integers

            - ``t`` -- (default: ``None``, in which case ``t`` is used) an
              element of the base ring

            EXAMPLES::

                sage: Sym = SymmetricFunctions(FractionField(QQ['t']))
                sage: ks = Sym.kschur(4)
                sage: s = Sym.schur()
                sage: s(ks([3,1,1]).hl_creation_operator([1]))
                (t-1)*s[2, 2, 1, 1] + t^2*s[3, 1, 1, 1] + (t^3+t^2-t)*s[3, 2, 1] + (t^3-t^2)*s[3, 3] + (t^4+t^3)*s[4, 1, 1] + t^4*s[4, 2] + t^5*s[5, 1]
                sage: ks([3,1,1]).hl_creation_operator([1])
                (t-1)*ks4[2, 2, 1, 1] + t^2*ks4[3, 1, 1, 1] + t^3*ks4[3, 2, 1] + (t^3-t^2)*ks4[3, 3] + t^4*ks4[4, 1, 1]

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks = Sym.kschur(4,t=1)
                sage: ks([3,1,1]).hl_creation_operator([1])
                ks4[3, 1, 1, 1] + ks4[3, 2, 1] + ks4[4, 1, 1]
            """
            if t is None:
                t = self.parent().realization_of().t
            return self.parent()(self.lift().hl_creation_operator(nu, t=t))

        def omega(self):
            r"""
            Return the `\omega` operator on ``self``.

            At `t=1`, `\omega` maps the `k`-Schur function `s^{(k)}_\lambda` to `s^{(k)}_{\lambda^{(k)}}`, where
            `\lambda^{(k)}` is the `k`-conjugate of the partition `\lambda`.

            .. SEEALSO:: :meth:`~sage.combinat.partition.Partition.k_conjugate`.

            For generic `t`, `\omega` sends `s^{(k)}_\lambda[X;t]` to `t^d s^{(k)}_{\lambda^{(k)}}[X;1/t]`,
            where `d` is the size of the core of `\lambda` minus the size of `\lambda`. Most of the time,
            this result is not in the `k`-bounded subspace.

            .. SEEALSO:: :meth:`omega_t_inverse`.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks = Sym.kschur(3,1)
                sage: ks[2,2,1,1].omega()
                ks3[2, 2, 2]
                sage: kh = Sym.khomogeneous(3)
                sage: kh[3].omega()
                h3[1, 1, 1] - 2*h3[2, 1] + h3[3]

                sage: Sym = SymmetricFunctions(FractionField(QQ['t']))
                sage: ks = Sym.kschur(3)
                sage: ks[3,1,1].omega()
                Traceback (most recent call last):
                ...
                ValueError: t*s[2, 1, 1, 1] + s[3, 1, 1] is not in the image
            """
            return self.parent()(self.lift().omega())

        def omega_t_inverse(self):
            r"""
            Return the map `t\to 1/t` composed with `\omega` on ``self``.

            Unlike the map :meth:`omega`, the result of :meth:`omega_t_inverse` lives in
            the `k`-bounded subspace and hence will return an element even for generic
            `t`. For `t=1`, :meth:`omega` and :meth:`omega_t_inverse` return the same
            result.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(FractionField(QQ['t']))
                sage: ks = Sym.kschur(3)
                sage: ks[3,1,1].omega_t_inverse()
                1/t*ks3[2, 1, 1, 1]
                sage: ks[3,2].omega_t_inverse()
                1/t^2*ks3[1, 1, 1, 1, 1]
            """
            s = self.parent().realization_of().ambient()
            t = s.base_ring().gen()

            def invert(x):
                return s.base_ring()(x.subs(t=1 / t))
            return self.parent()(s(self).map_coefficients(invert).omega())

        def is_schur_positive(self, *args, **kwargs):
            r"""
            Return whether ``self`` is Schur positive.

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks = Sym.kschur(3,1)
                sage: f = ks[3,2]+ks[1]
                sage: f.is_schur_positive()
                True
                sage: f = ks[3,2]-ks[1]
                sage: f.is_schur_positive()
                False

                sage: Sym = SymmetricFunctions(QQ['t'])
                sage: ks = Sym.kschur(3)
                sage: f = ks[3,2]+ks[1]
                sage: f.is_schur_positive()
                True
                sage: f = ks[3,2]-ks[1]
                sage: f.is_schur_positive()
                False
            """
            return self.lift().is_schur_positive(*args, **kwargs)

        def expand(self, *args, **kwargs):
            r"""
            Return the monomial expansion of ``self`` in `n` variables.

            INPUT:

            - ``n`` -- positive integer

            OUTPUT: monomial expansion of ``self`` in `n` variables

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks = Sym.kschur(3,1)
                sage: ks[3,1].expand(2)
                x0^4 + 2*x0^3*x1 + 2*x0^2*x1^2 + 2*x0*x1^3 + x1^4
                sage: s = Sym.schur()
                sage: ks[3,1].expand(2) == s(ks[3,1]).expand(2)
                True

                sage: Sym = SymmetricFunctions(QQ['t'])
                sage: ks = Sym.kschur(3)
                sage: f = ks[3,2]-ks[1]
                sage: f.expand(2)
                t^2*x0^5 + (t^2 + t)*x0^4*x1 + (t^2 + t + 1)*x0^3*x1^2 + (t^2 + t + 1)*x0^2*x1^3 + (t^2 + t)*x0*x1^4 + t^2*x1^5 - x0 - x1
            """
            return self.lift().expand(*args, **kwargs)

        def scalar(self, x, zee=None):
            r"""
            Return standard scalar product between ``self`` and ``x``.

            INPUT:

            - ``x`` -- element of the ring of symmetric functions over the
              same base ring as ``self``

            - ``zee`` -- an optional function on partitions giving
              the value for the scalar product between `p_{\mu}` and `p_{\mu}`
              (default: use the standard :meth:`~sage.combinat.sf.sfa.zee` function)

            .. SEEALSO:: :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.scalar`

            EXAMPLES::

                sage: Sym = SymmetricFunctions(QQ['t'])
                sage: ks3 = Sym.kschur(3)
                sage: ks3[3,2,1].scalar( ks3[2,2,2] )
                t^3 + t
                sage: dks3 = Sym.kBoundedQuotient(3).dks()
                sage: [ks3[3,2,1].scalar(dks3(la)) for la in Partitions(6, max_part=3)]
                [0, 1, 0, 0, 0, 0, 0]
                sage: dks3 = Sym.kBoundedQuotient(3,t=1).dks()
                sage: [ks3[2,2,2].scalar(dks3(la)) for la in Partitions(6, max_part=3)]
                [0, t - 1, 0, 1, 0, 0, 0]
                sage: ks3 = Sym.kschur(3,t=1)
                sage: [ks3[2,2,2].scalar(dks3(la)) for la in Partitions(6, max_part=3)]
                [0, 0, 0, 1, 0, 0, 0]
                sage: kH = Sym.khomogeneous(4)
                sage: kH([2,2,1]).scalar(ks3[2,2,1])
                3

            TESTS::

                sage: Sym = SymmetricFunctions(QQ)
                sage: ks3 = Sym.kschur(3,1)
                sage: ks3(1).scalar(ks3([]))
                1
            """
            if hasattr(x, 'lift'):
                return self.lift().scalar(x.lift(), zee)
            return self.lift().scalar(x, zee)


class kSchur(CombinatorialFreeModule):
    r"""
    Space of `k`-Schur functions.

    EXAMPLES::

        sage: Sym = SymmetricFunctions(QQ['t'])
        sage: KB = Sym.kBoundedSubspace(3); KB
        3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field

    The `k`-Schur function basis can be constructed as follows::

        sage: ks3 = KB.kschur(); ks3
        3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-Schur basis

    We can convert to any basis of the ring of symmetric functions and,
    whenever it makes sense, also the other way round::

        sage: s = Sym.schur()
        sage: s(ks3([3,2,1]))
        s[3, 2, 1] + t*s[4, 1, 1] + t*s[4, 2] + t^2*s[5, 1]
        sage: t = Sym.base_ring().gen()
        sage: ks3(s([3, 2, 1]) + t*s([4, 1, 1]) + t*s([4, 2]) + t^2*s([5, 1]))
        ks3[3, 2, 1]
        sage: s(ks3[2, 1, 1])
        s[2, 1, 1] + t*s[3, 1]
        sage: ks3(s[2, 1, 1] + t*s[3, 1])
        ks3[2, 1, 1]

    `k`-Schur functions are indexed by partitions with first part `\le k`. Constructing a
    `k`-Schur function for a larger partition raises an error::

        sage: ks3([4,3,2,1]) #
        Traceback (most recent call last):
        ...
        TypeError: do not know how to make x (= [4, 3, 2, 1]) an element of self (=3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-Schur basis)

    Similarly, attempting to convert a function that is not in the
    linear span of the `k`-Schur functions raises an error::

        sage: ks3(s([4]))
        Traceback (most recent call last):
        ...
        ValueError: s[4] is not in the image

    Note that the product of `k`-Schur functions is not guaranteed to be in the
    space spanned by the `k`-Schurs. In general, we only have that a
    `k`-Schur times a `j`-Schur function is in the `(k+j)`-bounded subspace. The
    multiplication of two `k`-Schur functions thus generally returns the product of
    the lift of the functions to the ambient symmetric function space.  If the result
    happens to lie in the `k`-bounded subspace, then the result is cast into the
    `k`-Schur basis::

        sage: ks2 = Sym.kBoundedSubspace(2).kschur()
        sage: ks2[1] * ks2[1]
        ks2[1, 1] + ks2[2]
        sage: ks2[1] * ks2[2]
        s[2, 1] + s[3]

    Because the target space of the product of a `k`-Schur and a `j`-Schur has several
    possibilities, the product of a `k`-Schur and `j`-Schur function is not
    implemented for distinct `k` and `j`. Let us show how to get around
    this 'manually'::

        sage: ks3 = Sym.kBoundedSubspace(3).kschur()
        sage: ks2([2,1]) * ks3([3,1])
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for *: '2-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 2-Schur basis' and '3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 3-Schur basis'

    The workaround::

        sage: f = s(ks2([2,1])) * s(ks3([3,1])); f # Convert to Schur functions first and multiply there.
        s[3, 2, 1, 1] + s[3, 2, 2] + (t+1)*s[3, 3, 1] + s[4, 1, 1, 1]
        + (2*t+2)*s[4, 2, 1] + (t^2+t+1)*s[4, 3] + (2*t+1)*s[5, 1, 1]
        + (t^2+2*t+1)*s[5, 2] + (t^2+2*t)*s[6, 1] + t^2*s[7]

    or::

        sage: f = ks2[2,1].lift() * ks3[3,1].lift()
        sage: ks5 = Sym.kBoundedSubspace(5).kschur()
        sage: ks5(f) # The product of a 'ks2' with a 'ks3' is a 'ks5'.
        ks5[3, 2, 1, 1] + ks5[3, 2, 2] + (t+1)*ks5[3, 3, 1] + ks5[4, 1, 1, 1]
        + (t+2)*ks5[4, 2, 1] + (t^2+t+1)*ks5[4, 3] + (t+1)*ks5[5, 1, 1] + ks5[5, 2]

    For other technical reasons, taking powers of `k`-Schur functions
    is not implemented, even when the answer is still in the `k`-bounded
    subspace::

        sage: ks2([1])^2
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for ^: '2-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 2-Schur basis' and 'Integer Ring'

    .. TODO::

        Get rid of said technical "reasons".

    However, at `t=1`, the product of `k`-Schur functions is in the span of the
    `k`-Schur functions always. Below are some examples at `t=1` ::

        sage: ks3 = Sym.kBoundedSubspace(3, t=1).kschur(); ks3
        3-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field with t=1 in the 3-Schur basis
        sage: s = SymmetricFunctions(ks3.base_ring()).schur()
        sage: ks3(s([3]))
        ks3[3]
        sage: s(ks3([3,2,1]))
        s[3, 2, 1] + s[4, 1, 1] + s[4, 2] + s[5, 1]
        sage: ks3([2,1])^2    # taking powers works for t=1
        ks3[2, 2, 1, 1] + ks3[2, 2, 2] + ks3[3, 1, 1, 1]

    TESTS:

    Check that :issue:`13743` is fixed::

        sage: ks3 = SymmetricFunctions(QQ).kschur(3, 1)
        sage: f = ks3[2,1]
        sage: f.coefficient(f.support()[0])
        1
    """

    def __init__(self, kBoundedRing):
        r"""
        TESTS::

            sage: Sym = SymmetricFunctions(QQ)
            sage: from sage.combinat.sf.new_kschur import kSchur
            sage: KB = Sym.kBoundedSubspace(3,t=1)
            sage: kSchur(KB)
            3-bounded Symmetric Functions over Rational Field with t=1 in the 3-Schur basis
        """
        CombinatorialFreeModule.__init__(self, kBoundedRing.base_ring(),
            kBoundedRing.indices(),
            category=KBoundedSubspaceBases(kBoundedRing, kBoundedRing.t),
            prefix='ks%d' % kBoundedRing.k)

        self._kBoundedRing = kBoundedRing

        self.k = kBoundedRing.k
        self.t = kBoundedRing.t

        s = self.realization_of().ambient().schur()

        self.ambient = ConstantFunction(s)

        self.lift = self._module_morphism(self._to_schur_on_basis,
                codomain=s, triangular='lower', unitriangular=True,
                inverse_on_support=lambda p: p if p.get_part(0) <= self.k else None)

        self.lift.register_as_coercion()

        self.retract = SetMorphism(Hom(s, self, SetsWithPartialMaps()),
                self.lift.preimage)
        self.register_conversion(self.retract)

    # The following are meant to be inherited with the category framework, but
    # this fails because they are methods of Parent. The trick below overcomes
    # this problem.
    __getitem__ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "__getitem__")
    _repr_term = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_repr_term")
    _convert_map_from_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_convert_map_from_")
    _element_constructor_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_element_constructor_")

    def _repr_(self):
        """
        Representation of ``self``.

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)
            sage: ks = Sym.kschur(4,1); ks      # indirect doctest
            4-bounded Symmetric Functions over Rational Field with t=1 in the 4-Schur basis

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: ks = Sym.kschur(4); ks
            4-bounded Symmetric Functions over Univariate Polynomial Ring in t over Rational Field in the 4-Schur basis
        """
        return self.realization_of()._repr_() + ' in the %s-Schur basis' % (self.k)

    @cached_method
    def _to_schur_on_basis(self, p):
        r"""
        Compute the change of basis from `k`-Schur functions to Schur functions.

        When `t=1` this procedure does this computation by first factoring out all
        maximal rectangles, computing all the atoms, and then taking the product
        again of the `k`-Schur function indexed by the `k`-irreducible partition and
        the Schur functions indexed by rectangles.

        INPUT:

        - ``p`` -- a partition

        OUTPUT: conversion of the `k`-Schur function indexed by ``p`` in terms of Schur functions

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: ks = Sym.kschur(4)
            sage: ks._to_schur_on_basis(Partition([3,3,2,1]))
            s[3, 3, 2, 1] + t*s[4, 3, 1, 1] + t*s[4, 3, 2] + t^2*s[4, 4, 1] + t^2*s[5, 3, 1] + t^3*s[5, 4]
            sage: ks = Sym.kschur(4,1)
            sage: ks._to_schur_on_basis(Partition([4,4,3,3,2,2,2,1])).coefficient([12,5,4])
            5

        TESTS::

            sage: ks._to_schur_on_basis(Partition([]))
            s[]
        """
        s = self.realization_of().ambient().schur()
        if self.t == 1:
            # in this case factor out maximal rectangles for speed
            pexp = p.to_exp() + [0] * self.k
            katom = p.k_irreducible(self.k).k_atom(self.k)
            return s.sum_of_monomials(tab.shape() for tab in katom) * prod(s([r + 1] * (self.k - r)) for r in range(self.k) for m in range(pexp[r] // (self.k - r)))
        katom = p.k_atom(self.k)
        return s.sum_of_terms((tab.shape(), self.t**tab.charge())
                              for tab in katom)

    def _product_on_basis_via_rectangles(self, left, right):
        r"""
        Multiply two `k`-Schur functions at `t=1` indexed by ``left`` and ``right``.

        This algorithm uses the property that if `R` is an `r \times (k+1-r)`
        rectangle, then

        .. MATH::

            s_{R} \cdot s^{(k)}_\lambda = s^{(k)}_{R \cup \lambda}

        To compute the product of two `k`-Schur functions, all rectangles are factored
        out, the product is performed in the Schur basis, then the rectangles are
        re-inserted.

        INPUT:

        - ``left``, ``right`` -- partitions

        OUTPUT: the product of the `k`-Schur functions indexed by ``left`` and ``right``

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)
            sage: ks = Sym.kschur(5,1)
            sage: ks._product_on_basis_via_rectangles(Partition([5,4,4,3,3,3]),Partition([4,4,2,2,2,2]))
            ks5[5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2]
            sage: ks._product_on_basis_via_rectangles(Partition([5,4,4,3,3,3,1]),Partition([4,4,2]))
            ks5[5, 4, 4, 4, 4, 3, 3, 3, 2, 1] + ks5[5, 4, 4, 4, 4, 3, 3, 3, 3]

        TESTS::

            sage: ks._product_on_basis_via_rectangles(Partition([]), Partition([]))
            ks5[]
        """
        leftir = self._to_schur_on_basis(left.k_irreducible(self.k))
        rightir = self._to_schur_on_basis(right.k_irreducible(self.k))
        heart = self.retract(leftir * rightir)
        leftexp = left.to_exp()
        rightexp = right.to_exp()
        rects = sum(([r + 1] * (self.k - r) for r in range(len(leftexp))
                     for m in range(leftexp[r] // (self.k - r))), [])
        rects += sum(([r + 1] * (self.k - r) for r in range(len(rightexp))
                      for m in range(rightexp[r] // (self.k - r))), [])
        return heart.map_support(lambda lam: Partition(sorted(lam + rects, reverse=True)))

    def product_on_basis(self, left, right):
        r"""
        Take the product of two `k`-Schur functions.

        If `t \neq 1`, then take the product by lifting to the Schur functions and then
        retracting back into the `k`-bounded subspace (if possible).

        If `t=1`, then the product calls :meth:`_product_on_basis_via_rectangles`.

        INPUT:

        - ``left``, ``right`` -- partitions

        OUTPUT: an element of the `k`-Schur functions

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: ks3 = Sym.kschur(3,1)
            sage: kH = Sym.khomogeneous(3)
            sage: ks3(kH[2,1,1])
            ks3[2, 1, 1] + ks3[2, 2] + ks3[3, 1]
            sage: ks3([])*kH[2,1,1]
            ks3[2, 1, 1] + ks3[2, 2] + ks3[3, 1]
            sage: ks3([3,3,3,2,2,1,1,1])^2
            ks3[3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]
            sage: ks3([3,3,3,2,2,1,1,1])*ks3([2,2,2,2,2,1,1,1,1])
            ks3[3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1]
            sage: ks3([2,2,1,1,1,1])*ks3([2,2,2,1,1,1,1])
            ks3[2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1] + ks3[2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]
            sage: ks3[2,1]^2
            ks3[2, 2, 1, 1] + ks3[2, 2, 2] + ks3[3, 1, 1, 1]
            sage: ks3 = Sym.kschur(3)
            sage: ks3[2,1]*ks3[2,1]
            s[2, 2, 1, 1] + s[2, 2, 2] + s[3, 1, 1, 1] + 2*s[3, 2, 1] + s[3, 3] + s[4, 1, 1] + s[4, 2]

        TESTS::

            sage: Sym = SymmetricFunctions(QQ['t'])
            sage: ks3 = Sym.kschur(3,1)
            sage: kH = Sym.khomogeneous(3)
            sage: ks3.product( ks3([]), ks3([]) )
            ks3[]
            sage: ks3.product( ks3([]), kH([]) )
            ks3[]
            sage: ks3 = Sym.kschur(3)
            sage: ks3([]) * ks3([])
            ks3[]
        """
        if self.t == 1:
            return self._product_on_basis_via_rectangles(left, right)
        left_s = self._to_schur_on_basis(left)
        right_s = self._to_schur_on_basis(right)
        return self.retract(left_s * right_s)


class kSplit(CombinatorialFreeModule):
    def __init__(self, kBoundedRing):
        r"""
        The `k`-split basis of the space of `k`-bounded-symmetric functions.

        Fix ``k`` a positive integer and ``t`` an element of the base ring.

        The `k`-split functions are a basis for the space of `k`-bounded
        symmetric functions that also have the bases

        .. MATH::

            \{ Q'_{\lambda}[X;t] \}_{\lambda_1\le k} =
            \{ s_{\lambda}^{(k)}[X;t] \}_{\lambda_1 \le k}

        where `Q'_\lambda[X;t]` are the Hall-Littlewood symmetric functions
        (using the notation of [MAC]_) and `s_{\lambda}^{(k)}[X;t]` are the
        `k`-Schur functions.  If `t` is not a root of unity, then

        .. MATH::

            \{ s_{\lambda}[X/(1-t)] \}_{\lambda_1\le k}

        is also a basis of this space.

        The `k`-split basis has the property that `Q'_\lambda[X;t]` expands
        positively in the `k`-split basis and the `k`-split basis
        conjecturally expands positively in the `k`-Schur functions.
        See [LLMSSZ]_ p. 81.

        The `k`-split basis is defined recursively using the
        Hall-Littlewood creation operator defined in [SZ2001]_.  If a
        partition ``la`` is the concatenation (as lists) of a partition ``mu``
        and ``nu`` where ``mu`` has maximal hook length equal to ``k``
        then ``ksp(la) = ksp(nu).hl_creation_operator(mu)``.  If the
        hook length of ``la`` is less than or equal to ``k``, then
        ``ksp(la)`` is equal to the Schur function indexed by ``la``.

        EXAMPLES::

            sage: Symt = SymmetricFunctions(QQ['t'].fraction_field())
            sage: kBS3 = Symt.kBoundedSubspace(3)
            sage: ks3 = kBS3.kschur()
            sage: ksp3 = kBS3.ksplit()
            sage: ks3(ksp3[2,1,1])
            ks3[2, 1, 1] + t*ks3[2, 2]
            sage: ksp3(ks3[2,1,1])
            ksp3[2, 1, 1] - t*ksp3[2, 2]
            sage: ksp3[2,1]*ksp3[1]
            s[2, 1, 1] + s[2, 2] + s[3, 1]
            sage: ksp3[2,1].hl_creation_operator([1])
            t*ksp3[2, 1, 1] + (-t^2+t)*ksp3[2, 2]

            sage: Qp = Symt.hall_littlewood().Qp()
            sage: ksp3(Qp[3,2,1])
            ksp3[3, 2, 1] + t*ksp3[3, 3]

            sage: kBS4 = Symt.kBoundedSubspace(4)
            sage: ksp4 = kBS4.ksplit()
            sage: ksp4(ksp3([3,2,1]))
            ksp4[3, 2, 1] - t*ksp4[3, 3] + t*ksp4[4, 1, 1]
            sage: ks4 = kBS4.kschur()
            sage: ks4(ksp4[3,2,2,1])
            ks4[3, 2, 2, 1] + t*ks4[3, 3, 1, 1] + t*ks4[3, 3, 2]
        """
        CombinatorialFreeModule.__init__(self, kBoundedRing.base_ring(),
            kBoundedRing.indices(),
            category=KBoundedSubspaceBases(kBoundedRing, kBoundedRing.t),
            prefix='ksp%d' % kBoundedRing.k)

        self._kBoundedRing = kBoundedRing

        self.k = kBoundedRing.k
        self.t = kBoundedRing.t

        s = self.realization_of().ambient().schur()

        self.ambient = ConstantFunction(s)

        self.lift = self._module_morphism(self._to_schur_on_basis,
                codomain=s, triangular='lower', unitriangular=True,
                inverse_on_support=lambda p: p if p.get_part(0) <= self.k else None)

        self.lift.register_as_coercion()

        self.retract = SetMorphism(Hom(s, self, SetsWithPartialMaps()),
                self.lift.preimage)
        self.register_conversion(self.retract)

    # The following are meant to be inherited with the category framework, but
    # this fails because they are methods of Parent. The trick below overcomes
    # this problem.
    __getitem__ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "__getitem__")
    _repr_term = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_repr_term")
    _convert_map_from_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_convert_map_from_")
    _element_constructor_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_element_constructor_")

    def _repr_(self):
        r"""
        Representation of ``self``.

        EXAMPLES::

            sage: SymmetricFunctions(QQ).kBoundedSubspace(3,1).ksplit()
            3-bounded Symmetric Functions over Rational Field with t=1 in the
            3-split basis
            sage: SymmetricFunctions(QQ['t']).kBoundedSubspace(3).ksplit()
            3-bounded Symmetric Functions over Univariate Polynomial Ring in t over
            Rational Field in the 3-split basis
        """
        return self.realization_of()._repr_() + ' in the %s-split basis' % (self.k)

    @cached_method
    def _to_schur_on_basis(self, p):
        r"""
        Compute the change of basis of `k`-split functions to Schur functions.

        When `t=1` the `k`-split basis is the product of the Schur functions
        indexed by the partitions in the `k`-split of the partition.
        For `t \neq 1`, the elements are computed using the Hall-Littlewood
        creation operator defined in [SZ2001]_.

        .. MATH::

            ksplit_\lambda = {\mathbb H}^t_{\nu^{(1)}} \cdots
            {\mathbb H}^t_{\nu^{(r)}} 1

        where the operator `{\mathbb H}^t_{\nu}` is implemented in the method
        ``hl_creation_operator`` and the `k`-split of the partition `\lambda`
        is `(\nu^{(1)}, \ldots, \nu^{(r)})`.

        INPUT:

        - ``p`` -- a partition

        EXAMPLES::

            sage: Sym = SymmetricFunctions(QQ)
            sage: s = Sym.s()
            sage: ksp3 = Sym.kBoundedSubspace(3,1).ksplit()
            sage: ksp3._to_schur_on_basis(Partition([2,1,1,1]))
            s[2, 1, 1, 1] + s[2, 2, 1] + s[3, 1, 1] + s[3, 2]
        """
        s = self.realization_of().ambient().schur()
        if self.t == 1:  # if t==1, then it is computed with products
            return s.prod(s(g) for g in Partition(p).k_split(self.k))
        if not p:
            return s(p)
        ksp = Partition(p).k_split(self.k)
        out = s(ksp[-1])
        for r in range(len(ksp) - 1):
            out = out.hl_creation_operator(ksp[-r - 2], t=self.t)
        return out


class kHomogeneous(CombinatorialFreeModule):
    r"""
    Space of `k`-bounded homogeneous symmetric functions.

    EXAMPLES::

        sage: Sym = SymmetricFunctions(QQ)
        sage: kH = Sym.khomogeneous(3)
        sage: kH[2]
        h3[2]
        sage: kH[2].lift()
        h[2]
    """

    def __init__(self, kBoundedRing):
        r"""
        TESTS::

            sage: Sym = SymmetricFunctions(QQ)
            sage: from sage.combinat.sf.new_kschur import kHomogeneous
            sage: KB = Sym.kBoundedSubspace(3,t=1)
            sage: kHomogeneous(KB)
            3-bounded Symmetric Functions over Rational Field with t=1 in the 3-bounded homogeneous basis
        """
        CombinatorialFreeModule.__init__(self, kBoundedRing.base_ring(),
            kBoundedRing.indices(),
            category=KBoundedSubspaceBases(kBoundedRing, kBoundedRing.t),
            prefix='h%d' % kBoundedRing.k)

        self._kBoundedRing = kBoundedRing

        self.k = kBoundedRing.k
        self.t = 1

        h = self.realization_of().ambient().homogeneous()

        self.lift = self._module_morphism(lambda x: h[x],
                codomain=h, triangular='lower', unitriangular=True,
                inverse_on_support=lambda p: p if p.get_part(0) <= self.k else None)

        self.ambient = ConstantFunction(h)

        self.lift.register_as_coercion()

        self.retract = SetMorphism(Hom(h, self, SetsWithPartialMaps()),
                self.lift.preimage)
        self.register_conversion(self.retract)

    # The following are meant to be inherited with the category framework, but
    # this fails because they are methods of Parent. The trick below overcomes
    # this problem.
    __getitem__ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "__getitem__")
    _repr_term = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_repr_term")
    _convert_map_from_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_convert_map_from_")
    _element_constructor_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_element_constructor_")

    def _repr_(self):
        """
        TESTS::

            sage: Sym = SymmetricFunctions(QQ)
            sage: kH = Sym.khomogeneous(3)
            sage: kH._repr_()
            '3-bounded Symmetric Functions over Rational Field with t=1 in the 3-bounded homogeneous basis'
        """
        return self.realization_of()._repr_() + ' in the %s-bounded homogeneous basis' % (self.k)


class K_kSchur(CombinatorialFreeModule):
    r"""
    This class implements the basis of the `k`-bounded subspace called the K-`k`-Schur basis.

    See [Morse2011]_, [LamSchillingShimozono2010]_.

    REFERENCES:

    .. [Morse2011] \J. Morse, Combinatorics of the K-theory of affine Grassmannians,
        Adv. in Math., Volume 229, Issue 5, pp. 2950--2984.

    .. [LamSchillingShimozono2010] \T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian,
        Compositio Math. 146 (2010), 811-852.
    """

    def __init__(self, kBoundedRing):
        r"""
        TESTS::

            sage: from sage.combinat.sf.new_kschur import K_kSchur
            sage: kB = SymmetricFunctions(QQ).kBoundedSubspace(3,1)
            sage: g = K_kSchur(kB)
            sage: g
            3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis
            sage: g[2,1]*g[1]  # takes a while but caches stuff
            -2*Kks3[2, 1] + Kks3[2, 1, 1] + Kks3[2, 2]
            sage: g([])
            Kks3[]
            sage: TestSuite(g).run()  # long time (11s on sage.math, 2013)
            sage: h = SymmetricFunctions(QQ).h()
            sage: g(h[1,1])
            -Kks3[1] + Kks3[1, 1] + Kks3[2]
        """
        CombinatorialFreeModule.__init__(self, kBoundedRing.base_ring(),
            kBoundedRing.indices(),
            category=KBoundedSubspaceBases(kBoundedRing, kBoundedRing.base_ring().one()),
            prefix='Kks%d' % kBoundedRing.k)

        self._kBoundedRing = kBoundedRing

        self.k = kBoundedRing.k
        self.t = 1

        s = self.realization_of().ambient().schur()

        self.ambient = ConstantFunction(s)
        kh = self.realization_of().khomogeneous()
        g_to_kh = self.module_morphism(self._g_to_kh_on_basis, codomain=kh)
        g_to_kh.register_as_coercion()
        kh_to_g = kh.module_morphism(self._kh_to_g_on_basis, codomain=self)
        kh_to_g.register_as_coercion()
        h = self.realization_of().ambient().h()
        lift = self._module_morphism(self.lift, triangular='lower', unitriangular=True, codomain=h)
        lift.register_as_coercion()
        retract = h._module_morphism(self.retract, codomain=self)
        # retract = SetMorphism(Hom(h, self, SetsWithPartialMaps()), lift.preimage)
        self.register_conversion(retract)

    # The following are meant to be inherited with the category framework, but
    # this fails because they are methods of Parent. The trick below overcomes
    # this problem.
    __getitem__ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "__getitem__")
    _repr_term = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_repr_term")
    _element_constructor_ = raw_getattr(KBoundedSubspaceBases.ParentMethods, "_element_constructor_")

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

            sage: Sym = SymmetricFunctions(QQ)
            sage: kB = Sym.kBoundedSubspace(3,1)
            sage: g = kB.K_kschur()
            sage: g._repr_()
            '3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis'
        """
        return self.realization_of()._repr_() + ' in the K-%s-Schur basis' % (self.k)

    def _homogeneous_generators_noncommutative_variables_zero_Hecke(self, r):
        r"""
        Return the `r`-th homogeneous generator, viewed as an element inside the
        affine zero Hecke algebra.

        This is the sum of all cyclically decreasing elements of order `r`.

        INPUT:

        - ``r`` -- positive integer

        OUTPUT: an element of the affine zero Hecke algebra

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(2)
            T[1,0] + T[2,0] + T[0,3] + T[3,2] + T[3,1] + T[2,1]
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(0)
            1
        """
        from sage.algebras.iwahori_hecke_algebra import IwahoriHeckeAlgebra
        from sage.combinat.root_system.weyl_group import WeylGroup
        W = WeylGroup(['A', self.k, 1])
        H = IwahoriHeckeAlgebra(W, 0, base_ring=self.base_ring()).T()
        Hgens = H.algebra_generators()
        S = [w.reduced_word() for w in W.pieri_factors() if w.length() == r]
        return sum((prod((Hgens[i] for i in w), 1) for w in S), 0)

    @cached_method
    def _homogeneous_basis(self, la):
        r"""
        Return the homogeneous basis element indexed by ``la``, viewed as an element
        inside the affine zero Hecke algebra. This method is only here for caching purposes.

        INPUT:

        - ``la`` -- a `k`-bounded partition

        OUTPUT: an element of the affine zero Hecke algebra

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._homogeneous_basis(Partition([2,1]))
            T[2,1,0] + T[3,1,0] + T[1,2,0] + T[3,2,0] + T[0,1,0] + T[2,0,1] + T[1,0,3] + T[0,3,0] + T[2,0,3] + T[0,3,2] + T[0,3,1] + T[2,3,2] + T[3,2,1] + T[2,3,1] + T[3,1,2] + T[1,2,1] - T[1,0] - 2*T[2,0] - T[0,3] - T[3,2] - 2*T[3,1] - T[2,1]
            sage: g._homogeneous_basis(Partition([]))
            1
        """
        return prod(self._homogeneous_generators_noncommutative_variables_zero_Hecke(la[i]) for i in range(len(la)))

    def homogeneous_basis_noncommutative_variables_zero_Hecke(self, la):
        r"""
        Return the homogeneous basis element indexed by ``la``, viewed as an element
        inside the affine zero Hecke algebra. For the code, see method _homogeneous_basis.

        INPUT:

        - ``la`` -- a `k`-bounded partition

        OUTPUT: an element of the affine zero Hecke algebra

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g.homogeneous_basis_noncommutative_variables_zero_Hecke([2,1])
            T[2,1,0] + T[3,1,0] + T[1,2,0] + T[3,2,0] + T[0,1,0] + T[2,0,1] + T[1,0,3] + T[0,3,0] + T[2,0,3] + T[0,3,2] + T[0,3,1] + T[2,3,2] + T[3,2,1] + T[2,3,1] + T[3,1,2] + T[1,2,1] - T[1,0] - 2*T[2,0] - T[0,3] - T[3,2] - 2*T[3,1] - T[2,1]
            sage: g.homogeneous_basis_noncommutative_variables_zero_Hecke([])
            1
        """
        return self._homogeneous_basis(Partition(la))

    @cached_method
    def _DualGrothMatrix(self, m):
        r"""
        Return the change of basis matrix between the K_kschur basis and the `k`-bounded
        homogeneous basis.

        INPUT:

        - ``m`` -- integer

        OUTPUT: a matrix

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._DualGrothMatrix(3)
            [ 1  1  1  0  0  0  0]
            [ 0  1  2  0  0  0  0]
            [ 0  0  1  0  0  0  0]
            [ 0 -1 -2  1  1  0  0]
            [ 0  0 -2  0  1  0  0]
            [ 0  0  1  0 -1  1  0]
            [ 0  0  0  0  0  0  1]
            sage: g._DualGrothMatrix(0)
            [1]
        """
        new_mat = []
        Sym = SymmetricFunctions(self.base_ring())
        Q = Sym.kBoundedQuotient(self.k, t=1)
        mon = Q.km()
        G = Q.AffineGrothendieckPolynomial
        for i in range(m + 1):
            for x in Partitions(m - i, max_part=self.k):
                f = mon(G(x, m))
                vec = [f.coefficient(y) for j in range(m + 1)
                       for y in Partitions(m - j, max_part=self.k)]
                new_mat.append(vec)
        from sage.matrix.constructor import Matrix
        return Matrix(new_mat)

    @cached_method
    def _DualGrothendieck(self, la):
        r"""
        Return the expansion of the K-`k`-Schur function in the homogeneous basis. This
        method is here for caching purposes.

        INPUT:

        - ``la`` -- a `k`-bounded partition

        OUTPUT: a symmetric function in the homogeneous basis

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._DualGrothendieck(Partition([2,1]))
            h[2] + h[2, 1] - h[3]
            sage: g._DualGrothendieck(Partition([]))
            h[]
            sage: g._DualGrothendieck(Partition([4,1]))  # long time (5s on sage.math, 2013)
            0
        """
        m = la.size()
        h = SymmetricFunctions(self.base_ring()).h()
        M = self._DualGrothMatrix(m)
        vec = []
        for i in range(m + 1):
            for x in Partitions(m - i, max_part=self.k):
                if x == la:
                    vec.append(1)
                else:
                    vec.append(0)
        from sage.modules.free_module_element import vector
        vec = vector(vec)
        sol = M.solve_right(vec)
        new_function = h.zero()
        count = 0
        for i in range(m + 1):
            for x in Partitions(m - i, max_part=self.k):
                new_function += h(x) * sol[count]
                count += 1
        return new_function

    def _g_to_kh_on_basis(self, la):
        r"""
        Return the expansion of the K-`k`-Schur function in the homogeneous basis.

        See method :meth:`_DualGrothendieck` for the code.

        INPUT:

        - ``la`` -- a `k`-bounded partition

        OUTPUT: a symmetric function in the homogeneous basis

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._g_to_kh_on_basis([2,1])
            h[2] + h[2, 1] - h[3]
            sage: g._g_to_kh_on_basis([])
            h[]
            sage: g._g_to_kh_on_basis([4,1])
            Traceback (most recent call last):
            ...
            ValueError: Partition should be 3-bounded
        """
        if la and la[0] > self.k:
            raise ValueError("Partition should be %d-bounded" % self.k)
        return self._DualGrothendieck(Partition(la))

    def K_k_Schur_non_commutative_variables(self, la):
        r"""
        Return the K-`k`-Schur function, as embedded inside the affine zero Hecke algebra.

        INPUT:

        - ``la`` -- a `k`-bounded Partition

        OUTPUT: an element of the affine zero Hecke algebra

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g.K_k_Schur_non_commutative_variables([2,1])
            T[3,1,0] + T[1,2,0] + T[3,2,0] + T[0,1,0] + T[2,0,1] + T[0,3,0] + T[2,0,3] + T[0,3,1] + T[2,3,2] + T[2,3,1] + T[3,1,2] + T[1,2,1] - T[2,0] - T[3,1]
            sage: g.K_k_Schur_non_commutative_variables([])
            1
            sage: g.K_k_Schur_non_commutative_variables([4,1])
            Traceback (most recent call last):
            ...
            ValueError: Partition should be 3-bounded
        """
        SF = SymmetricFunctions(self.base_ring())
        h = SF.h()
        S = h(self._g_to_kh_on_basis(la)).support()
        return sum(h(self._g_to_kh_on_basis(la)).coefficient(x) * self.homogeneous_basis_noncommutative_variables_zero_Hecke(x) for x in S)

    def _kh_to_g_on_basis(self, la):
        r"""
        Given a `k`-homogeneous basis element, this returns the element written in the
        K-`k`-Schur basis.

        INPUT:

        - ``la`` -- a `k`-bounded partition

        OUTPUT: an element of the `k`-bounded subspace, written in the K-`k`-Schur basis

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._kh_to_g_on_basis([2,1])
            -Kks3[2] + Kks3[2, 1] + Kks3[3]
            sage: g._kh_to_g_on_basis([])
            Kks3[]
            sage: g._kh_to_g_on_basis([4,1])
            Traceback (most recent call last):
            ...
            TypeError: do not know how to make x (= [4, 1]) an element of self (=3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis)
        """
        if not la:
            return self([])
        h = self.realization_of().khomogeneous()
        f = h(self(la)) - h(la)
        return self(la) - sum(self._kh_to_g_on_basis(x) * f.coefficient(x)
                              for x in f.support())

    def product(self, x, y):
        r"""
        Return the product of the two K-`k`-Schur functions.

        INPUT:

        - ``x``, ``y`` -- elements of the `k`-bounded subspace, in the K-`k`-Schur basis

        OUTPUT: an element of the `k`-bounded subspace, in the K-`k`-Schur basis

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g.product(g([2,1]), g[1])
            -2*Kks3[2, 1] + Kks3[2, 1, 1] + Kks3[2, 2]
            sage: g.product(g([2,1]), g([]))
            Kks3[2, 1]
        """
        kh = self.realization_of().khomogeneous()
        return self(kh(x) * kh(y))

    def lift(self, x):
        r"""
        Return the lift of a `k`-bounded symmetric function.

        INPUT:

        - ``x`` -- an expression in the K-`k`-Schur basis. Equivalently, ``x`` can be a
            `k`-bounded partition (then ``x`` corresponds to the basis element indexed by ``x``)

        OUTPUT: a symmetric function

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g.lift([2,1])
            h[2] + h[2, 1] - h[3]
            sage: g.lift([])
            h[]
            sage: g.lift([4,1])
            Traceback (most recent call last):
            ...
            TypeError: do not know how to make x (= [4, 1]) an element of self (=3-bounded Symmetric Functions over Rational Field with t=1 in the K-3-Schur basis)
        """
        kh = self.realization_of().khomogeneous()
        return kh(self(x)).lift()

    def retract(self, x):
        r"""
        Return the retract of a symmetric function.

        INPUT:

        - ``x`` -- a symmetric function

        OUTPUT: a `k`-bounded symmetric function in the K-`k`-Schur basis

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: m = SymmetricFunctions(QQ).m()
            sage: g.retract(m[2,1])
            -2*Kks3[1] + 4*Kks3[1, 1] - 2*Kks3[1, 1, 1] - Kks3[2] + Kks3[2, 1]
            sage: g.retract(m([]))
            Kks3[]
        """
        kh = self.realization_of().khomogeneous()
        return self(kh.retract(x))
