"""
(Non-negative) Integer vectors

AUTHORS:

 *   Mike Hansen (2007) - original module
 *   Nathann Cohen, David Joyner (2009-2010) - Gale-Ryser stuff
 *   Nathann Cohen, David Joyner (2011) - Gale-Ryser bugfix
 *   Travis Scrimshaw (2012-05-12) - Updated doc-strings to tell the user of
     that the class's name is a misnomer (that they only contains non-negative
     entries).
 *   Federico Poloni (2013) - specialized rank()
"""
#*****************************************************************************
#       Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>,
#       Copyright (C) 2012 Travis Scrimshaw <tscrim@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:
#
#                  http://www.gnu.org/licenses/
#*****************************************************************************
from __future__ import print_function, absolute_import, division

from six.moves import range
import itertools
from . import misc
from six.moves.builtins import list as builtinlist
from sage.categories.enumerated_sets import EnumeratedSets
from sage.combinat.combinat import CombinatorialClass
from sage.rings.integer import Integer
from sage.arith.all import binomial
from sage.rings.infinity import PlusInfinity
import functools
from sage.combinat.integer_lists import IntegerListsLex


def is_gale_ryser(r,s):
    r"""
    Tests whether the given sequences satisfy the condition
    of the Gale-Ryser theorem.

    Given a binary matrix `B` of dimension `n\times m`, the
    vector of row sums is defined as the vector whose
    `i^{\mbox{th}}` component is equal to the sum of the `i^{\mbox{th}}`
    row in `A`. The vector of column sums is defined similarly.

    If, given a binary matrix, these two vectors are easy to compute,
    the Gale-Ryser theorem lets us decide whether, given two
    non-negative vectors `r,s`, there exists a binary matrix
    whose row/colum sums vectors are `r` and `s`.

    This functions answers accordingly.

    INPUT:

    - ``r``, ``s`` -- lists of non-negative integers.

    ALGORITHM:

    Without loss of generality, we can assume that:

    - The two given sequences do not contain any `0` ( which would
      correspond to an empty column/row )

    - The two given sequences are ordered in decreasing order
      (reordering the sequence of row (resp. column) sums amounts to
      reordering the rows (resp. columns) themselves in the matrix,
      which does not alter the columns (resp. rows) sums.

    We can then assume that `r` and `s` are partitions
    (see the corresponding class ``Partition``)

    If `r^*` denote the conjugate of `r`, the Gale-Ryser theorem
    asserts that a binary Matrix satisfying the constraints exists
    if and only if `s\preceq r^*`, where `\preceq` denotes
    the domination order on partitions.

    EXAMPLES::

        sage: from sage.combinat.integer_vector import is_gale_ryser
        sage: is_gale_ryser([4,2,2],[3,3,1,1])
        True
        sage: is_gale_ryser([4,2,1,1],[3,3,1,1])
        True
        sage: is_gale_ryser([3,2,1,1],[3,3,1,1])
        False

    REMARK: In the literature, what we are calling a
    Gale-Ryser sequence sometimes goes by the (rather
    generic-sounding) term ''realizable sequence''.
    """

    # The sequences only contain non-negative integers
    if [x for x in r if x < 0] or [x for x in s if x < 0]:
        return False

    # builds the corresponding partitions, i.e.
    # removes the 0 and sorts the sequences
    from sage.combinat.partition import Partition
    r2 = Partition(sorted([x for x in r if x>0], reverse=True))
    s2 = Partition(sorted([x for x in s if x>0], reverse=True))

    # If the two sequences only contained zeroes
    if len(r2) == 0 and len(s2) == 0:
        return True

    rstar = Partition(r2).conjugate()

    #                                same number of 1s           domination
    return len(rstar) <= len(s2) and  sum(r2) == sum(s2) and rstar.dominates(s)

def gale_ryser_theorem(p1, p2, algorithm="gale"):
        r"""
        Returns the binary matrix given by the Gale-Ryser theorem.

        The Gale Ryser theorem asserts that if `p_1,p_2` are two
        partitions of `n` of respective lengths `k_1,k_2`, then there is
        a binary `k_1\times k_2` matrix `M` such that `p_1` is the vector
        of row sums and `p_2` is the vector of column sums of `M`, if
        and only if the conjugate of `p_2` dominates `p_1`.

        INPUT:

        - ``p1, p2``-- list of integers representing the vectors
          of row/column sums

        - ``algorithm`` -- two possible string values :

            - ``"ryser"`` implements the construction due
              to Ryser [Ryser63]_.

            - ``"gale"`` (default) implements the construction due to Gale [Gale57]_.

        OUTPUT:

        - A binary matrix if it exists, ``None`` otherwise.

        Gale's Algorithm:

        (Gale [Gale57]_): A matrix satisfying the constraints of its
        sums can be defined as the solution of the following
        Linear Program, which Sage knows how to solve.

        .. MATH::

            \forall i&\sum_{j=1}^{k_2} b_{i,j}=p_{1,j}\\
            \forall i&\sum_{j=1}^{k_1} b_{j,i}=p_{2,j}\\
            &b_{i,j}\mbox{ is a binary variable}

        Ryser's Algorithm:

        (Ryser [Ryser63]_): The construction of an `m\times n` matrix
        `A=A_{r,s}`, due to Ryser, is described as follows. The
        construction works if and only if have `s\preceq r^*`.

        * Construct the `m\times n` matrix `B` from `r` by defining
          the `i`-th row of `B` to be the vector whose first `r_i`
          entries are `1`, and the remainder are 0's, `1\leq i\leq
          m`.  This maximal matrix `B` with row sum `r` and ones left
          justified has column sum `r^{*}`.

        * Shift the last `1` in certain rows of `B` to column `n` in
          order to achieve the sum `s_n`.  Call this `B` again.

          * The `1`'s in column n are to appear in those
            rows in which `A` has the largest row sums, giving
            preference to the bottom-most positions in case of ties.
          * Note: When this step automatically "fixes" other columns,
            one must skip ahead to the first column index
            with a wrong sum in the step below.

        * Proceed inductively to construct columns `n-1`, ..., `2`, `1`.
          Note: when performing the induction on step `k`, we consider
          the row sums of the first `k` columns.

        * Set `A = B`. Return `A`.

        EXAMPLES:

        Computing the matrix for `p_1=p_2=2+2+1` ::

            sage: from sage.combinat.integer_vector import gale_ryser_theorem
            sage: p1 = [2,2,1]
            sage: p2 = [2,2,1]
            sage: print(gale_ryser_theorem(p1, p2))     # not tested
            [1 1 0]
            [1 0 1]
            [0 1 0]
            sage: A = gale_ryser_theorem(p1, p2)
            sage: rs = [sum(x) for x in A.rows()]
            sage: cs = [sum(x) for x in A.columns()]
            sage: p1 == rs; p2 == cs
            True
            True

        Or for a non-square matrix with `p_1=3+3+2+1` and `p_2=3+2+2+1+1`, using Ryser's algorithm ::

            sage: from sage.combinat.integer_vector import gale_ryser_theorem
            sage: p1 = [3,3,1,1]
            sage: p2 = [3,3,1,1]
            sage: gale_ryser_theorem(p1, p2, algorithm = "ryser")
            [1 1 1 0]
            [1 1 0 1]
            [1 0 0 0]
            [0 1 0 0]
            sage: p1 = [4,2,2]
            sage: p2 = [3,3,1,1]
            sage: gale_ryser_theorem(p1, p2, algorithm = "ryser")
            [1 1 1 1]
            [1 1 0 0]
            [1 1 0 0]
            sage: p1 = [4,2,2,0]
            sage: p2 = [3,3,1,1,0,0]
            sage: gale_ryser_theorem(p1, p2, algorithm = "ryser")
            [1 1 1 1 0 0]
            [1 1 0 0 0 0]
            [1 1 0 0 0 0]
            [0 0 0 0 0 0]
            sage: p1 = [3,3,2,1]
            sage: p2 = [3,2,2,1,1]
            sage: print(gale_ryser_theorem(p1, p2, algorithm="gale"))  # not tested
            [1 1 1 0 0]
            [1 1 0 0 1]
            [1 0 1 0 0]
            [0 0 0 1 0]

        With `0` in the sequences, and with unordered inputs ::

            sage: from sage.combinat.integer_vector import gale_ryser_theorem
            sage: gale_ryser_theorem([3,3,0,1,1,0], [3,1,3,1,0], algorithm = "ryser")
            [1 1 1 0 0]
            [1 0 1 1 0]
            [0 0 0 0 0]
            [1 0 0 0 0]
            [0 0 1 0 0]
            [0 0 0 0 0]
            sage: p1 = [3,1,1,1,1]; p2 = [3,2,2,0]
            sage: gale_ryser_theorem(p1, p2, algorithm = "ryser")
            [1 1 1 0]
            [1 0 0 0]
            [1 0 0 0]
            [0 1 0 0]
            [0 0 1 0]

        TESTS:

        This test created a random bipartite graph on `n+m` vertices. Its
        adjacency matrix is binary, and it is used to create some
        "random-looking" sequences which correspond to an existing matrix. The
        ``gale_ryser_theorem`` is then called on these sequences, and the output
        checked for correctness.::

            sage: def test_algorithm(algorithm, low = 10, high = 50):
            ....:     n,m = randint(low,high), randint(low,high)
            ....:     g = graphs.RandomBipartite(n, m, .3)
            ....:     s1 = sorted(g.degree([(0,i) for i in range(n)]), reverse = True)
            ....:     s2 = sorted(g.degree([(1,i) for i in range(m)]), reverse = True)
            ....:     m = gale_ryser_theorem(s1, s2, algorithm = algorithm)
            ....:     ss1 = sorted(map(lambda x : sum(x) , m.rows()), reverse = True)
            ....:     ss2 = sorted(map(lambda x : sum(x) , m.columns()), reverse = True)
            ....:     if ((ss1 != s1) or (ss2 != s2)):
            ....:         print("Algorithm %s failed with this input:" % algorithm)
            ....:         print(s1, s2)

            sage: for algorithm in ["gale", "ryser"]:                        # long time
            ....:     for i in range(50):                                    # long time
            ....:         test_algorithm(algorithm, 3, 10)                   # long time
            
        Null matrix::

            sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="gale")
            [0 0 0 0]
            [0 0 0 0]
            [0 0 0 0]
            sage: gale_ryser_theorem([0,0,0],[0,0,0,0], algorithm="ryser")
            [0 0 0 0]
            [0 0 0 0]
            [0 0 0 0]

        Check that :trac:`16638` is fixed::

            sage: tests = [([4, 3, 3, 2, 1, 1, 1, 1, 0], [6, 5, 1, 1, 1, 1, 1]),
            ....:          ([4, 4, 3, 3, 1, 1, 0], [5, 5, 2, 2, 1, 1]),
            ....:          ([4, 4, 3, 2, 1, 1], [5, 5, 1, 1, 1, 1, 1, 0, 0]),
            ....:          ([3, 3, 3, 3, 2, 1, 1, 1, 0], [7, 6, 2, 1, 1, 0]),
            ....:          ([3, 3, 3, 1, 1, 0], [4, 4, 1, 1, 1])]
            sage: for s1, s2 in tests:
            ....:     m = gale_ryser_theorem(s1, s2, algorithm="ryser")
            ....:     ss1 = sorted(map(lambda x : sum(x) , m.rows()), reverse = True)
            ....:     ss2 = sorted(map(lambda x : sum(x) , m.columns()), reverse = True)
            ....:     if ((ss1 != s1) or (ss2 != s2)):
            ....:         print("Error in Ryser algorithm")
            ....:         print(s1, s2)

        REFERENCES:

        ..  [Ryser63] \H. J. Ryser, Combinatorial Mathematics,
            Carus Monographs, MAA, 1963.
        ..  [Gale57] \D. Gale, A theorem on flows in networks, Pacific J. Math.
            7(1957)1073-1082.
        """
        from sage.matrix.constructor import matrix

        if not(is_gale_ryser(p1,p2)):
            return False

        if algorithm=="ryser": # ryser's algorithm
            from sage.combinat.permutation import Permutation

            # Sorts the sequences if they are not, and remembers the permutation
            # applied
            tmp = sorted(enumerate(p1), reverse=True, key=lambda x:x[1])
            r = [x[1] for x in tmp]
            r_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()]
            m = len(r)

            tmp = sorted(enumerate(p2), reverse=True, key=lambda x:x[1])
            s = [x[1] for x in tmp]
            s_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()]
            n = len(s)

            # This is the partition equivalent to the sliding algorithm
            cols = []
            for t in reversed(s):
                c = [0] * m
                i = 0
                while t:
                    k = i + 1
                    while k < m and r[i] == r[k]:
                        k += 1
                    if t >= k - i: # == number rows of the same length
                        for j in range(i, k):
                            r[j] -= 1
                            c[j] = 1
                        t -= k - i
                    else: # Remove the t last rows of that length
                        for j in range(k-t, k):
                            r[j] -= 1
                            c[j] = 1
                        t = 0
                    i = k
                cols.append(c)

            # We added columns to the back instead of the front
            A0 = matrix(list(reversed(cols))).transpose()

            # Applying the permutations to get a matrix satisfying the
            # order given by the input
            A0 = A0.matrix_from_rows_and_columns(r_permutation, s_permutation)
            return A0

        elif algorithm == "gale":
          from sage.numerical.mip import MixedIntegerLinearProgram
          k1, k2=len(p1), len(p2)
          p = MixedIntegerLinearProgram()
          b = p.new_variable(binary = True)
          for (i,c) in enumerate(p1):
              p.add_constraint(p.sum([b[i,j] for j in range(k2)]) ==c)
          for (i,c) in enumerate(p2):
              p.add_constraint(p.sum([b[j,i] for j in range(k1)]) ==c)
          p.set_objective(None)
          p.solve()
          b = p.get_values(b)
          M = [[0]*k2 for i in range(k1)]
          for i in range(k1):
              for j in range(k2):
                  M[i][j] = int(b[i,j])
          return matrix(M)

        else:
            raise ValueError("The only two algorithms available are \"gale\" and \"ryser\"")

def _default_function(l, default, i):
    """
    EXAMPLES::

        sage: from sage.combinat.integer_vector import _default_function
        sage: import functools
        sage: f = functools.partial(_default_function, [1,2,3], 99)
        sage: f(-1)
        99
        sage: f(0)
        1
        sage: f(1)
        2
        sage: f(2)
        3
        sage: f(3)
        99
    """
    try:
        if i < 0:
            return default
        return l[i]
    except IndexError:
        return default

infinity = PlusInfinity()
def list2func(l, default=None):
    """
    Given a list ``l``, return a function that takes in a value `i` and
    return `l[i]`. If default is not None, then the function will
    return the default value for out of range `i`'s.

    EXAMPLES::

        sage: f = sage.combinat.integer_vector.list2func([1,2,3])
        sage: f(0)
        1
        sage: f(1)
        2
        sage: f(2)
        3
        sage: f(3)
        Traceback (most recent call last):
        ...
        IndexError: list index out of range

    ::

        sage: f = sage.combinat.integer_vector.list2func([1,2,3], 0)
        sage: f(2)
        3
        sage: f(3)
        0
    """
    if default is None:
        return lambda i: l[i]
    else:
        return functools.partial(_default_function, l, default)


def constant_func(i):
    """
    Returns the constant function i.

    EXAMPLES::

        sage: f = sage.combinat.integer_vector.constant_func(3)
        sage: f(-1)
        3
        sage: f('asf')
        3
    """
    return lambda x: i

def IntegerVectors(n=None, k=None, **kwargs):
    """
    Returns the combinatorial class of (non-negative) integer vectors.

    INPUT:

    - `n` -- if set to an integer, returns the combinatorial class of integer
      vectors whose sum is `n`. If set to ``None`` (default), no such constraint
      is defined.

    - ``k`` -- the length of the vectors. Set to ``None`` (default) if you do
      not want such a constraint.

    All other arguments given to this function are forwarded to the instance of
    :class:`IntegerVectors_all` :class:`IntegerVectors_nconstraints`
    :class:`IntegerVectors_nk` or :class:`IntegerVectors_nkconstraints` that it
    returns.


    NOTE - These integer vectors are non-negative.

    EXAMPLES: If n is not specified, it returns the class of all
    integer vectors.

    ::

        sage: IntegerVectors()
        Integer vectors
        sage: [] in IntegerVectors()
        True
        sage: [1,2,1] in IntegerVectors()
        True
        sage: [1, 0, 0] in IntegerVectors()
        True

    Entries are non-negative.

    ::

        sage: [-1, 2] in IntegerVectors()
        False

    If n is specified, then it returns the class of all integer vectors
    which sum to n.

    ::

        sage: IV3 = IntegerVectors(3); IV3
        Integer vectors that sum to 3

    Note that trailing zeros are ignored so that [3, 0] does not show
    up in the following list (since [3] does)

    ::

        sage: IntegerVectors(3, max_length=2).list()
        [[3], [2, 1], [1, 2], [0, 3]]

    If n and k are both specified, then it returns the class of integer
    vectors that sum to n and are of length k.

    ::

        sage: IV53 = IntegerVectors(5,3); IV53
        Integer vectors of length 3 that sum to 5
        sage: IV53.cardinality()
        21
        sage: IV53.first()
        [5, 0, 0]
        sage: IV53.last()
        [0, 0, 5]
        sage: IV53.random_element()
        [4, 0, 1]

    Further examples::

        sage: IntegerVectors(-1, 0, min_part = 1).list()
        []
        sage: IntegerVectors(-1, 2, min_part = 1).list()
        []
        sage: IntegerVectors(0, 0, min_part=1).list()
        [[]]
        sage: IntegerVectors(3, 0, min_part=1).list()
        []
        sage: IntegerVectors(0, 1, min_part=1).list()
        []
        sage: IntegerVectors(2, 2, min_part=1).list()
        [[1, 1]]
        sage: IntegerVectors(2, 3, min_part=1).list()
        []
        sage: IntegerVectors(4, 2, min_part=1).list()
        [[3, 1], [2, 2], [1, 3]]

    ::

        sage: IntegerVectors(0, 3, outer=[0,0,0]).list()
        [[0, 0, 0]]
        sage: IntegerVectors(1, 3, outer=[0,0,0]).list()
        []
        sage: IntegerVectors(2, 3, outer=[0,2,0]).list()
        [[0, 2, 0]]
        sage: IntegerVectors(2, 3, outer=[1,2,1]).list()
        [[1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1]]
        sage: IntegerVectors(2, 3, outer=[1,1,1]).list()
        [[1, 1, 0], [1, 0, 1], [0, 1, 1]]
        sage: IntegerVectors(2, 5, outer=[1,1,1,1,1]).list()
        [[1, 1, 0, 0, 0],
         [1, 0, 1, 0, 0],
         [1, 0, 0, 1, 0],
         [1, 0, 0, 0, 1],
         [0, 1, 1, 0, 0],
         [0, 1, 0, 1, 0],
         [0, 1, 0, 0, 1],
         [0, 0, 1, 1, 0],
         [0, 0, 1, 0, 1],
         [0, 0, 0, 1, 1]]

    ::

        sage: iv = [ IntegerVectors(n,k) for n in range(-2, 7) for k in range(7) ]
        sage: all(map(lambda x: x.cardinality() == len(x.list()), iv))
        True
        sage: essai = [[1,1,1], [2,5,6], [6,5,2]]
        sage: iv = [ IntegerVectors(x[0], x[1], max_part = x[2]-1) for x in essai ]
        sage: all(map(lambda x: x.cardinality() == len(x.list()), iv))
        True

    TESTS:

    :trac:`17927`::

        sage: IntegerVectors(None, length=3)
        Traceback (most recent call last):
        ...
        TypeError: __init__() got an unexpected keyword argument 'length'
        sage: IntegerVectors(None, 4)
        Traceback (most recent call last):
        ...
        NotImplementedError: k must be None when n is None
    """
    if n is None:
        if k is not None:
            raise NotImplementedError("k must be None when n is None")
        return IntegerVectors_all(**kwargs)
    elif k is None:
        return IntegerVectors_nconstraints(n, kwargs)
    elif isinstance(k, builtinlist):
        if kwargs:
            raise ValueError("if k is a list, no optional argument is supported")
        return IntegerVectors_nnondescents(n,k)
    else:
        if kwargs:
            return IntegerVectors_nkconstraints(n,k,kwargs)
        else:
            return IntegerVectors_nk(n,k)

class IntegerVectors_all(CombinatorialClass):
    def _repr_(self):
        """
        EXAMPLES::

            sage: IntegerVectors()
            Integer vectors
        """
        return "Integer vectors"

    def __contains__(self, x):
        """
        EXAMPLES::

            sage: [] in IntegerVectors()
            True
            sage: [3,2,2,1] in IntegerVectors()
            True
        """
        if not isinstance(x, builtinlist):
            return False
        for i in x:
            if not isinstance(i, (int, Integer)):
                return False
            if i < 0:
                return False

        return True

    def list(self):
        """
        EXAMPLES::

            sage: IntegerVectors().list()
            Traceback (most recent call last):
            ...
            NotImplementedError: infinite list
        """
        raise NotImplementedError("infinite list")  # can't use InfiniteAbstractCombinatorialClass

    def cardinality(self):
        """
        EXAMPLES::

            sage: IntegerVectors().cardinality()
            +Infinity
        """
        return infinity


class IntegerVectors_nk(CombinatorialClass):
    def __init__(self, n, k):
        """
        TESTS::

            sage: IV = IntegerVectors(2,3)
            sage: IV == loads(dumps(IV))
            True

        AUTHORS:

        - Martin Albrecht

        - Mike Hansen
        """
        self.n = n
        self.k = k


    def _list_rec(self, n, k):
        """
        Return a list of a exponent tuples of length `size` such
        that the degree of the associated monomial is `D`.

        INPUT:


        -  ``n`` - degree (must be 0)

        -  ``k`` - length of exponent tuples (must be 0)


        EXAMPLES::

            sage: IV = IntegerVectors(2,3)
            sage: IV._list_rec(2,3)
            [(2, 0, 0), (1, 1, 0), (1, 0, 1), (0, 2, 0), (0, 1, 1), (0, 0, 2)]
        """
        res = []

        if k == 1:
            return [ (n, ) ]

        for nbar in range(n+1):
            n_diff = n-nbar
            for rest in self._list_rec( nbar , k-1):
                res.append((n_diff,)+rest)
        return res

    def list(self):
        """
        EXAMPLE::

            sage: IV = IntegerVectors(2,3)
            sage: IV.list()
            [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
            sage: IntegerVectors(3, 0).list()
            []
            sage: IntegerVectors(3, 1).list()
            [[3]]
            sage: IntegerVectors(0, 1).list()
            [[0]]
            sage: IntegerVectors(0, 2).list()
            [[0, 0]]
            sage: IntegerVectors(2, 2).list()
            [[2, 0], [1, 1], [0, 2]]
        """
        if self.n < 0:
            return []

        if self.k == 0:
            if self.n == 0:
                return [[]]
            else:
                return []
        elif self.k == 1:
            return [[self.n]]

        res = self._list_rec(self.n, self.k)
        return [list(_) for _ in res]


    def __iter__(self):
        """
        EXAMPLE::

            sage: IV = IntegerVectors(2,3)
            sage: list(IV)
            [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
            sage: list(IntegerVectors(3, 0))
            []
            sage: list(IntegerVectors(3, 1))
            [[3]]
            sage: list(IntegerVectors(0, 1))
            [[0]]
            sage: list(IntegerVectors(0, 2))
            [[0, 0]]
            sage: list(IntegerVectors(2, 2))
            [[2, 0], [1, 1], [0, 2]]
            sage: IntegerVectors(0,0).list()
            [[]]
            sage: IntegerVectors(1,0).list()
            []
            sage: IntegerVectors(0,1).list()
            [[0]]
            sage: IntegerVectors(2,2).list()
            [[2, 0], [1, 1], [0, 2]]
            sage: IntegerVectors(-1,0).list()
            []
            sage: IntegerVectors(-1,2).list()
            []
        """
        if self.n < 0:
            return

        if self.k == 0:
            if self.n == 0:
                yield []
            return
        elif self.k == 1:
            yield [self.n]
            return

        rem = -1 # Amount remaining
        cur = [self.n+1]
        k = int(self.k)
        while cur:
            cur[-1] -= 1
            rem += 1
            if rem == 0:
                yield cur + [Integer(0)] * (k - len(cur))
            elif cur[-1] < 0:
                rem += cur.pop()
            elif len(cur) == k - 1:
                yield cur + [Integer(rem)]
            else:
                cur.append(rem + 1)
                rem = -1

    def __repr__(self):
        """
        TESTS::

            sage: IV = IntegerVectors(2,3)
            sage: repr(IV)
            'Integer vectors of length 3 that sum to 2'
        """
        return "Integer vectors of length %s that sum to %s"%(self.k, self.n)

    def __contains__(self, x):
        """
        TESTS::

            sage: IV = IntegerVectors(2,3)
            sage: all(i in IV for i in IV)
            True
            sage: [0,1,2] in IV
            False
            sage: [2.0, 0, 0] in IV
            False
            sage: [0,1,0,1] in IV
            False
            sage: [0,1,1] in IV
            True
            sage: [-1,2,1] in IV
            False
        """
        if x not in IntegerVectors():
            return False

        if sum(x) != self.n:
            return False

        if len(x) != self.k:
            return False

        if len(x) > 0 and min(x) < 0:
            return False

        return True

    def rank(self, x):
        """
        Returns the position of a given element.

        INPUT:

        - ``x`` - a list with ``sum(x) == n`` and ``len(x) == k``

        TESTS::

            sage: IV = IntegerVectors(4,5) 
            sage: range(IV.cardinality()) == [IV.rank(x) for x in IV]
            True
        """

        if x not in self:
            raise ValueError("argument is not a member of IntegerVectors(%d,%d)" % (self.n, self.k))

        n = self.n
        k = self.k

        r = 0
        for i in range(k-1):
          k -= 1
          n -= x[i]
          r += binomial(k+n-1,k)

        return r

class IntegerVectors_nkconstraints(IntegerListsLex):
    def __init__(self, n, k, constraints, category=None):

        """
        EXAMPLES::

            sage: IV = IntegerVectors(2,3,min_slope=0)
            sage: IV == loads(dumps(IV))
            True

            sage: v = IntegerVectors(2,3,min_slope=0).first(); v
            [0, 1, 1]
            sage: type(v)
            <type 'list'>

        TESTS::

            sage: IV.min_length
            3
            sage: IV.max_length
            3
            sage: floor = IV.floor
            sage: [floor(i) for i in range(1,10)]
            [0, 0, 0, 0, 0, 0, 0, 0, 0]
            sage: ceiling = IV.ceiling
            sage: [ceiling(i) for i in range(1,5)]
            [inf, inf, inf, inf]
            sage: IV.min_slope
            0
            sage: IV.max_slope
            inf

            sage: IV = IntegerVectors(3, 10, inner=[4,1,3], min_part=2)
            sage: floor = IV.floor
            sage: floor(0), floor(1), floor(2)
            (4, 2, 3)

            sage: IV = IntegerVectors(3, 10, outer=[4,1,3], max_part=3)
            sage: ceiling = IV.ceiling
            sage: ceiling(0), ceiling(1), ceiling(2)
            (3, 1, 3)
        """
        self.n = n
        self.k = k

        args = constraints.copy()

        if self.k >= 0:
            args['length'] = self.k

        if 'outer' in args:
            args['ceiling'] = args['outer']
            del args['outer']
        if 'inner' in args:
            args['floor'] = args['inner']
            del args['inner']
        self._constraints = constraints
        IntegerListsLex.__init__(self, n, element_constructor=list,
                                 category=category, **args)

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

            sage: IntegerVectors(2,3,min_slope=0).__repr__()
            'Integer vectors of length 3 that sum to 2 with constraints: min_slope=0'
        """
        return "Integer vectors of length %s that sum to %s with constraints: \
        %s"%(self.k, self.n, ", ".join( ["%s=%s"%(key, self._constraints[key]) for key in sorted(self._constraints)] ))

    def __contains__(self, x):
        """
        TESTS::

            sage: [0] in IntegerVectors(0)
            True
            sage: [0] in IntegerVectors(0, 1)
            True
            sage: [] in IntegerVectors(0, 0)
            True
            sage: [] in IntegerVectors(0, 1)
            False
            sage: [] in IntegerVectors(1, 0)
            False
            sage: [3] in IntegerVectors(3)
            True
            sage: [3] in IntegerVectors(2,1)
            False
            sage: [3] in IntegerVectors(2)
            False
            sage: [3] in IntegerVectors(3,1)
            True
            sage: [3,2,2,1] in IntegerVectors(9)
            False
            sage: [3,2,2,1] in IntegerVectors(9,5)
            False
            sage: [3,2,2,1] in IntegerVectors(8)
            True
            sage: [3,2,2,1] in IntegerVectors(8,5)
            False
            sage: [3,2,2,1] in IntegerVectors(8,4)
            True
            sage: [3,2,2,1] in IntegerVectors(8,4, min_part = 1)
            True
            sage: [3,2,2,1] in IntegerVectors(8,4, min_part = 2)
            False
        """
        if x not in IntegerVectors():
            return False

        if sum(x) != self.n:
            return False

        if len(x) != self.k:
            return False

        if self._constraints:
            if not misc.check_integer_list_constraints(x, singleton=True, **self._constraints):
                return False

        return True

    def cardinality(self):
        """
        EXAMPLES::

            sage: IntegerVectors(3,3, min_part=1).cardinality()
            1
            sage: IntegerVectors(5,3, min_part=1).cardinality()
            6
            sage: IntegerVectors(13, 4, min_part=2, max_part=4).cardinality()
            16
        """
        if not self._constraints:
            if self.k < 0:
                return +infinity
            if self.n >= 0:
                return binomial(self.n+self.k-1,self.n)
            else:
                return 0
        else:
            if len(self._constraints) == 1 and 'max_part' in self._constraints and self._constraints['max_part'] != infinity:
                m = self._constraints['max_part']
                if m >= self.n:
                    return binomial(self.n+self.k-1,self.n)
                else: #do by inclusion / exclusion on the number
                      #i of parts greater than m
                    return sum( [(-1)**i * binomial(self.n+self.k-1-i*(m+1), self.k-1)*binomial(self.k,i) for i in range(self.n // (m+1)+1)])
            else:
                return super(IntegerVectors_nkconstraints, self).cardinality()

    def next(self, x):
        """
        EXAMPLES::

            sage: a = IntegerVectors(2,3,min_slope=0).first()
            sage: IntegerVectors(2,3,min_slope=0).next(a)
            [0, 0, 2]
        """
        from sage.combinat.integer_list_old import next
        return next(x, self.min_length, self.max_length, self.floor, self.ceiling, self.min_slope, self.max_slope)

class IntegerVectors_nconstraints(IntegerVectors_nkconstraints):
    def __init__(self, n, constraints):
        """
        TESTS::

            sage: IV = IntegerVectors(3, max_length=2)
            sage: IV == loads(dumps(IV))
            True

            sage: IntegerVectors(3, max_length=2).cardinality()
            4
            sage: IntegerVectors(3).cardinality()
            +Infinity

            sage: IntegerVectors(3, max_length=2).list()
            [[3], [2, 1], [1, 2], [0, 3]]
            sage: IntegerVectors(3).list()
            Traceback (most recent call last):
            ...
            NotImplementedError: infinite list
        """
        category = EnumeratedSets().Infinite() if not constraints else None
        IntegerVectors_nkconstraints.__init__(self, n, -1, constraints,
                                              category=category)

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

            sage: repr(IntegerVectors(3))
            'Integer vectors that sum to 3'
            sage: repr(IntegerVectors(3, max_length=2))
            'Integer vectors that sum to 3 with constraints: max_length=2'
        """
        if self._constraints:
            return "Integer vectors that sum to %s with constraints: %s"%(self.n,", ".join( ["%s=%s"%(key, self._constraints[key]) for key in sorted(self._constraints)] ))
        else:
            return "Integer vectors that sum to %s" % (self.n,)

    def __contains__(self, x):
        """
        EXAMPLES::

            sage: [0,3,0,1,2] in IntegerVectors(6)
            True
            sage: [0,3,0,1,2] in IntegerVectors(6, max_length=3)
            False
        """
        if self._constraints:
            return x in IntegerVectors_all() and misc.check_integer_list_constraints(x, singleton=True, **self._constraints)
        else:
            return x in IntegerVectors_all() and sum(x) == self.n

class IntegerVectors_nnondescents(CombinatorialClass):
    r"""
    The combinatorial class of integer vectors v graded by two
    parameters:

    - n: the sum of the parts of v

    - comp: the non descents composition of v

    In other words: the length of v equals c[1]+...+c[k], and v is
    decreasing in the consecutive blocs of length c[1], ..., c[k]

    Those are the integer vectors of sum n which are lexicographically
    maximal (for the natural left->right reading) in their orbit by the
    young subgroup S_c_1 x x S_c_k. In particular, they form a set
    of orbit representative of integer vectors w.r.t. this young
    subgroup.
    """
    def __init__(self, n, comp):
        """
        EXAMPLES::

            sage: IV = IntegerVectors(4, [2])
            sage: IV == loads(dumps(IV))
            True
        """
        self.n = n
        self.comp = comp

    def __repr__(self):
        """
        EXAMPLES::

            sage: IntegerVectors(4, [2]).__repr__()
            'Integer vectors of 4 with non-descents composition [2]'
        """
        return "Integer vectors of %s with non-descents composition %s"%(self.n, self.comp)

    def __iter__(self):
        """
         TESTS::

             sage: IntegerVectors(0, []).list()
             [[]]
             sage: IntegerVectors(5, []).list()
             []
             sage: IntegerVectors(0, [1]).list()
             [[0]]
             sage: IntegerVectors(4, [1]).list()
             [[4]]
             sage: IntegerVectors(4, [2]).list()
             [[4, 0], [3, 1], [2, 2]]
             sage: IntegerVectors(4, [2,2]).list()
             [[4, 0, 0, 0],
              [3, 1, 0, 0],
              [2, 2, 0, 0],
              [3, 0, 1, 0],
              [2, 1, 1, 0],
              [2, 0, 2, 0],
              [2, 0, 1, 1],
              [1, 1, 2, 0],
              [1, 1, 1, 1],
              [1, 0, 3, 0],
              [1, 0, 2, 1],
              [0, 0, 4, 0],
              [0, 0, 3, 1],
              [0, 0, 2, 2]]
             sage: IntegerVectors(5, [1,1,1]).list()
             [[5, 0, 0],
              [4, 1, 0],
              [4, 0, 1],
              [3, 2, 0],
              [3, 1, 1],
              [3, 0, 2],
              [2, 3, 0],
              [2, 2, 1],
              [2, 1, 2],
              [2, 0, 3],
              [1, 4, 0],
              [1, 3, 1],
              [1, 2, 2],
              [1, 1, 3],
              [1, 0, 4],
              [0, 5, 0],
              [0, 4, 1],
              [0, 3, 2],
              [0, 2, 3],
              [0, 1, 4],
              [0, 0, 5]]
             sage: IntegerVectors(0, [2,3]).list()
             [[0, 0, 0, 0, 0]]
         """
        for iv in IntegerVectors(self.n, len(self.comp)):
            blocks = [ IntegerVectors(iv[i], self.comp[i], max_slope=0).list() for i in range(len(self.comp))]
            for parts in itertools.product(*blocks):
                res = []
                for part in parts:
                    res += part
                yield res


