"""
Vectors with integer entries

AUTHOR:

- William Stein (2007)

EXAMPLES::

    sage: v = vector(ZZ,[1,2,3,4,5])
    sage: v
    (1, 2, 3, 4, 5)
    sage: 3*v
    (3, 6, 9, 12, 15)
    sage: v*7
    (7, 14, 21, 28, 35)
    sage: -v
    (-1, -2, -3, -4, -5)
    sage: v - v
    (0, 0, 0, 0, 0)
    sage: v + v
    (2, 4, 6, 8, 10)
    sage: v * v   # dot product.
    55

We make a large zero vector::

    sage: k = ZZ^100000; k
    Ambient free module of rank 100000 over the principal ideal domain Integer Ring
    sage: v = k(0)
    sage: v[:10]
    (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

TESTS::

    sage: v = vector(ZZ, [1,2,3,4])
    sage: loads(dumps(v)) == v
    True
"""

###############################################################################
#   Sage: System for Algebra and Geometry Experimentation
#       Copyright (C) 2007 William Stein <wstein@gmail.com>
#  Distributed under the terms of the GNU General Public License (GPL)
#                  http://www.gnu.org/licenses/
###############################################################################

include 'sage/ext/interrupt.pxi'
include 'sage/ext/stdsage.pxi'

from sage.structure.element cimport Element, ModuleElement, RingElement, Vector

from sage.rings.integer cimport Integer

cimport free_module_element

from free_module_element import vector

cdef inline _Integer_from_mpz(mpz_t e):
    cdef Integer z = PY_NEW(Integer)
    mpz_set(z.value, e)
    return z

cdef class Vector_integer_dense(free_module_element.FreeModuleElement):
    cdef _new_c(self):
        cdef Vector_integer_dense y
        y = PY_NEW(Vector_integer_dense)
        y._init(self._degree, self._parent)
        return y

    cdef bint is_dense_c(self):
        return 1
    cdef bint is_sparse_c(self):
        return 0

    def __copy__(self):
        cdef Vector_integer_dense y
        y = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init_set(y._entries[i], self._entries[i])
        return y

    cdef _init(self, Py_ssize_t degree, parent):
        self._degree = degree
        self._parent = parent
        self._is_mutable = 1
        self._entries = <mpz_t *> sage_malloc(sizeof(mpz_t) * degree)
        if self._entries == NULL:
            raise MemoryError

    def __cinit__(self, parent=None, x=None, coerce=True,copy=True):
        self._entries = NULL
        self._is_mutable = 1
        if not parent is None:
            self._init(parent.degree(), parent)

    def __init__(self, parent, x, coerce=True, copy=True):
        cdef Py_ssize_t i
        cdef Integer z
        # we have to do this to avoid a garbage collection error in dealloc
        for i from 0 <= i < self._degree:
            mpz_init(self._entries[i])
        if isinstance(x, (list, tuple)):
            if len(x) != self._degree:
                raise TypeError("x must be a list of the right length")
            for i from 0 <= i < self._degree:
                z = Integer(x[i])
                mpz_set(self._entries[i], z.value)
            return
        if x != 0:
            raise TypeError("can't initialize vector from nonzero non-list")

    def __dealloc__(self):
        cdef Py_ssize_t i
        if self._entries:
            for i from 0 <= i < self._degree:
                mpz_clear(self._entries[i])
            sage_free(self._entries)

    cdef int _cmp_c_impl(left, Element right) except -2:
        """
        EXAMPLES::

            sage: v = vector(ZZ, [0,0,0,0])
            sage: v == 0
            True
            sage: v == 1
            False
            sage: v == v
            True
            sage: w = vector(ZZ, [-1,0,0,0])
            sage: w < v
            True
            sage: w > v
            False
        """
        cdef Py_ssize_t i
        cdef int c
        for i from 0 <= i < left.degree():
            c = mpz_cmp(left._entries[i], (<Vector_integer_dense>right)._entries[i])
            if c < 0:
                return -1
            elif c > 0:
                return 1
        return 0

    # see sage/structure/element.pyx
    def __richcmp__(left, right, int op):
        """
        TEST::

            sage: w = vector(ZZ, [-1,0,0,0])
            sage: w == w
            True
        """
        return (<Element>left)._richcmp(right, op)

    # __hash__ is not properly inherited if comparison is changed
    def __hash__(self):
        """
        TEST::

            sage: w = vector(ZZ, [-1,0,0,0])
            sage: w.set_immutable()
            sage: isinstance(hash(w), int)
            True
        """
        return free_module_element.FreeModuleElement.__hash__(self)

    def __len__(self):
        return self._degree

    def __setitem__(self, i, value):
        """
        EXAMPLES::

            sage: v = vector([1,2,3]); v
            (1, 2, 3)
            sage: v[0] = 2
            sage: v[1:3] = [1, 4]; v
            (2, 1, 4)
        """
        if not self._is_mutable:
            raise ValueError("vector is immutable; please change a copy instead (use copy())")
        cdef Integer z
        cdef Py_ssize_t k, d, n
        if isinstance(i, slice):
            start, stop = i.start, i.stop
            d = self.degree()
            R = self.base_ring()
            n = 0
            for k from start <= k < stop:
                if k >= d:
                    return
                if k >= 0:
                    self[k] = R(value[n])
                    n = n + 1
        else:
            if i < 0 or i >= self._degree:
                raise IndexError
            else:
                z = Integer(value)
                mpz_set(self._entries[i], z.value)

    def __getitem__(self, i):
        """
        Returns `i`-th entry or slice of self.

        EXAMPLES::

            sage: v = vector([1,2,3]); v
            (1, 2, 3)
            sage: v[0]
            1
            sage: v[-2]
            2
            sage: v[0:2]
            (1, 2)
            sage: v[5]
            Traceback (most recent call last):
            ...
            IndexError: index out of range
            sage: v[-5]
            Traceback (most recent call last):
            ...
            IndexError: index out of range
        """
        cdef Integer z = PY_NEW(Integer)
        if isinstance(i, slice):
            start, stop, step = i.indices(len(self))
            return vector(self.base_ring(), self.list()[start:stop])
        else:
            if i < 0:
                i += self._degree
            if i < 0 or i >= self._degree:
                raise IndexError('index out of range')
            else:
                mpz_set(z.value, self._entries[i])
                return z

    def list(self,copy=True):
        """
        The list of entries of the vector.

        INPUT:

        - ``copy``, ignored optional argument.

        EXAMPLES::

            sage: v = vector([1,2,3,4])
            sage: a = v.list(copy=False); a
            [1, 2, 3, 4]
            sage: a[0] = 0
            sage: v
            (1, 2, 3, 4)
        """
        cdef int i
        return [_Integer_from_mpz(self._entries[i]) for i in
                                  xrange(self._degree)]

    def __reduce__(self):
        return (unpickle_v1, (self._parent, self.list(), self._degree, self._is_mutable))

    cpdef ModuleElement _add_(self, ModuleElement right):
        cdef Vector_integer_dense z, r
        r = right
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_add(z._entries[i], self._entries[i], r._entries[i])
        return z


    cpdef ModuleElement _sub_(self, ModuleElement right):
        cdef Vector_integer_dense z, r
        r = right
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_sub(z._entries[i], self._entries[i], r._entries[i])
        return z

    cpdef Element _dot_product_(self, Vector right):
        """
        Dot product of dense vectors over the integers.

        EXAMPLES::

            sage: v = vector(ZZ, [1,2,-3]); w = vector(ZZ,[4,3,2])
            sage: v*w
            4
            sage: w*v
            4
        """
        cdef Vector_integer_dense r = right
        cdef Integer z
        z = PY_NEW(Integer)
        cdef mpz_t t
        mpz_init(t)
        mpz_set_si(z.value, 0)
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_mul(t, self._entries[i], r._entries[i])
            mpz_add(z.value, z.value, t)
        mpz_clear(t)
        return z

    cpdef Vector _pairwise_product_(self, Vector right):
        """
        EXAMPLES::

            sage: v = vector(ZZ, [1,2,-3]); w = vector(ZZ,[4,3,2])
            sage: v.pairwise_product(w)
            (4, 6, -6)
        """
        cdef Vector_integer_dense z, r
        r = right
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_mul(z._entries[i], self._entries[i], r._entries[i])
        return z

    cpdef ModuleElement _rmul_(self, RingElement left):
        cdef Vector_integer_dense z
        cdef Integer a
        a = left
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_mul(z._entries[i], self._entries[i], a.value)
        return z

    cpdef ModuleElement _lmul_(self, RingElement right):
        cdef Vector_integer_dense z
        cdef Integer a
        a = right
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_mul(z._entries[i], self._entries[i], a.value)
        return z

    cpdef ModuleElement _neg_(self):
        cdef Vector_integer_dense z
        z = self._new_c()
        cdef Py_ssize_t i
        for i from 0 <= i < self._degree:
            mpz_init(z._entries[i])
            mpz_neg(z._entries[i], self._entries[i])
        return z

    def _singular_(self, singular=None):
        r"""
        Return \Singular representation of this integer vector.

        INPUT:

        - singular -- \Singular interface instance (default: None)

        EXAMPLES::

            sage: A = random_matrix(ZZ,1,3)
            sage: v = A.row(0)
            sage: vs = singular(v); vs
            -8,
            2,
            0
            sage: vs.type()
            'intvec'
        """
        if singular is None:
            from sage.interfaces.singular import singular as singular_default
            singular = singular_default

        name = singular._next_var_name()
        values = str(self.list())[1:-1]
        singular.eval("intvec %s = %s"%(name, values))

        from sage.interfaces.singular import SingularElement
        return SingularElement(singular, 'foobar', name, True)

def unpickle_v0(parent, entries, degree):
    # If you think you want to change this function, don't.
    # Instead make a new version with a name like
    #    make_FreeModuleElement_generic_dense_v1
    # and changed the reduce method below.
    cdef Vector_integer_dense v
    v = PY_NEW(Vector_integer_dense)
    v._init(degree, parent)
    cdef Integer z
    for i from 0 <= i < degree:
        z = Integer(entries[i])
        mpz_init_set(v._entries[i], z.value)
    return v

def unpickle_v1(parent, entries, degree, is_mutable):
    cdef Vector_integer_dense v
    v = PY_NEW(Vector_integer_dense)
    v._init(degree, parent)
    cdef Integer z
    for i from 0 <= i < degree:
        z = Integer(entries[i])
        mpz_init_set(v._entries[i], z.value)
    v._is_mutable = is_mutable
    return v
