/* linbox/blackbox/nag-sparse.h
 * Copyright (C) 2002 Rich Seagraves
 *
 * Written by Rich Seagraves <seagrave@cis.udel.edu>
 * Modified by Zhendong Wan, -bds
 * ------------------------------------
 *
 *
 * ========LICENCE========
 * This file is part of the library LinBox.
 *
 * LinBox is free software: you can redistribute it and/or modify
 * it under the terms of the  GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ========LICENCE========
 *.
 */

/*! @file blackbox/zero-one.inl
 * @ingroup blackbox
 * @brief NO DOC
 */

#ifndef __LINBOX_bb_zero_one_INL
#define __LINBOX_bb_zero_one_INL

namespace LinBox
{
	/*! Raw iterator.
	 * @ingroup iterators
	 */
    template<class Field>
    class ZeroOne<Field>::Iterator {
    public:
        typedef Element value_type;

        Iterator(size_t pos, Element elem) :
                _pos(pos), _elem(elem) {}

        Iterator(const Iterator &In) :
                _pos(In._pos), _elem(In._elem) {}

        const Iterator& operator=(const Iterator& rhs)
            {
                _pos = rhs._pos;
                _elem = rhs._elem;
                return *this;
            }


        bool operator==(const Iterator &rhs)
            {
                return ( _pos == rhs._pos && _elem == rhs._elem);
            }

        bool operator!=(const Iterator &rhs)
            {
                return ( _pos != rhs._pos || _elem != rhs._elem );
            }

        Iterator & operator++()
            {
                ++_pos;
                return *this;
            }

        Iterator operator++(int)
            {
                Iterator tmp = *this;
                _pos++;
                return tmp;
            }

        value_type operator*() { return _elem; }

        const value_type operator*() const { return _elem; }

    private:
        value_type _elem;
        size_t _pos;
    };

	/* STL standard Begin and End functions.  Used to get
	 * the beginning and end of the data.  So that Iterator
	 * can be used in algorithms like a normal STL iterator.
	 */
    template<class Field> typename
    ZeroOne<Field>::Iterator ZeroOne<Field>::Begin()
    {
        return Iterator( 0, field().assign(_tmp, field().one) );
    }

    template<class Field> typename
    ZeroOne<Field>::Iterator ZeroOne<Field>::End()
    {
        return Iterator( _nnz, field().assign(_tmp, field().one) );
    }

    template<class Field>
    const typename ZeroOne<Field>::Iterator ZeroOne<Field>::Begin() const
    {
        return Iterator(0, field().assign(_tmp, field().one) );
    }

    template<class Field>
    const typename ZeroOne<Field>::Iterator ZeroOne<Field>::End() const
    {
        return Iterator(_nnz, field().assign(_tmp, field().one) );
    }

	/*! IndexIterator.
	 * @ingroup iterators
	 * Iterates through the i and j of the current element
	 * and when accessed returns an STL pair containing the coordinates
	 */
    template<class Field>
    class ZeroOne<Field>::IndexIterator {
    public:
        typedef std::pair<size_t, size_t> value_type;

        IndexIterator() {}

        IndexIterator(size_t* row, size_t* col):
                _row(row), _col(col) {}

        IndexIterator(const IndexIterator &In):
                _row(In._row), _col(In._col)
            {}

        const IndexIterator &operator=(const IndexIterator &rhs)
            {
                _row = rhs._row;
                _col = rhs._col;
                return *this;
            }

        bool operator==(const IndexIterator &rhs)
            {
                return _row == rhs._row && _col == rhs._col;
            }

        bool operator!=(const IndexIterator &rhs)
            {
                return _row != rhs._row || _col != rhs._col;
            }

        const IndexIterator& operator++()
            {
                ++_row; ++_col;
                return *this;
            }

        const IndexIterator operator++(int)
            {
                IndexIterator tmp = *this;
                ++_row; ++_col;
                return tmp;
            }

        value_type operator*()
            {
                return std::pair<size_t,size_t>(*_row, *_col);
            }

        const value_type operator*() const
            {
                return std::pair<size_t,size_t>(*_row, *_col);
            }
    private:
        size_t* _row, *_col;
    };

    template<class Field> typename
    ZeroOne<Field>::IndexIterator ZeroOne<Field>::indexBegin()
    {
        return IndexIterator(_rowP, _colP);
    }

    template<class Field>
    const typename ZeroOne<Field>::IndexIterator ZeroOne<Field>::indexBegin() const
    {
        return IndexIterator(_rowP, _colP);
    }

    template<class Field> typename
    ZeroOne<Field>::IndexIterator ZeroOne<Field>::indexEnd()
    {
        return IndexIterator(_rowP + _nnz, _colP + _nnz);
    }

    template<class Field>
    const typename ZeroOne<Field>::IndexIterator ZeroOne<Field>::indexEnd() const
    {
        return IndexIterator(_rowP + _nnz, _colP + _nnz);
    }

    template<class Field>
    ZeroOne<Field>::ZeroOne(const Field& F) :
            _field(&F)
    {
        srand(static_cast<unsigned int>(std::time(nullptr)));
        dynamic = false;
    }

    template<class Field>
    ZeroOne<Field>::ZeroOne(Field& F, Index* rowP, Index* colP,
                            Index rows, Index cols, Index NNz, bool RowSort, bool ColSort):
            _field(&F), _rows(rows), _cols(cols), _nnz(NNz), _rowP(rowP), _colP(colP), _rowSort(RowSort), _colSort(ColSort) , dynamic(false)
    {
        srand(static_cast<unsigned>(std::time(nullptr)));
    }

    template<class Field>
    ZeroOne<Field>::~ZeroOne()
    {
        if(dynamic) {
            delete [] _rowP;
            delete [] _colP;
        }
    }

    template<class Field>
    void ZeroOne<Field>::rowSort() const
    {
        if( _rowSort) return;  // Already sorted, we're done
        else {
            int mode = 0;
            _qsort( (size_t) 0, _nnz, mode);
        }
        _rowSort = true;
        return;
    }

    template<class Field>
    void ZeroOne<Field>::colSort() const
    {
        if( _colSort) return; // Already sorted, good to go
        else {
            int mode = 1;
            _qsort( (size_t) 0, _nnz, mode);
        }
        _colSort = true; _rowSort = false;
        return;
    }

    template<class Field>
    void ZeroOne<Field>::_qsort(size_t p, size_t e, int &mode) const
    {
        if( (e - p) <= 1) ;
        else
        {
            int i;
            i = 1 + (int)_part(p, e, mode);
            _qsort(p, (size_t)i, mode);
            _qsort((size_t)i, e, mode);
        }
    }

    template<class Field>
    size_t ZeroOne<Field>::_part(size_t p, size_t e, int &mode) const
    {
        size_t rtemp, ctemp, rowval, colval;
        int i = int(p +(size_t) rand() % (e - p)), j =(int) e;
        rtemp = _rowP[p];
        ctemp = _colP[p];
        _rowP[p] = _rowP[i];
        _colP[p] = _colP[i];
        _rowP[i] = rtemp;
        _colP[i] = ctemp;
        rowval = _rowP[p];
        colval = _colP[p];
        i = (int)p - 1;

        if(mode == 0)
        { // Row mode, go by row order, then column
            while(true)
            {
                do j--; while( _rowP[j] > rowval || ( _rowP[j] == rowval && _colP[j] > colval ));
                do i++; while( _rowP[i] < rowval || ( _rowP[i] == rowval && _colP[i] < colval ));
                if( i < j)
                {
                    rtemp = _rowP[j];
                    ctemp = _colP[j];
                    _rowP[j] = _rowP[i];
                    _colP[j] = _colP[i];
                    _rowP[i] = rtemp;
                    _colP[i] = ctemp;
                }
                else return (size_t)j;
            }
        }
        else
        { // Col mode, go by col order, then row
            while(true)
            {
                do j--; while( _colP[j] > colval || ( _colP[j] == colval && _rowP[j] > rowval ));
                do i++; while( _colP[i] < colval || ( _colP[i] == colval && _rowP[i] < rowval ));
                if( i < j) {
                    rtemp = _rowP[j];
                    ctemp = _colP[j];
                    _rowP[j] = _rowP[i];
                    _colP[j] = _colP[i];
                    _rowP[i] = rtemp;
                    _colP[i] = ctemp;
                }
                else return (size_t)j;
            }
        }
    }

    template<class Field>
    template<class OutVector, class InVector>
    OutVector & ZeroOne<Field>::applySpecialization(OutVector & y, const InVector & x, const NormField& n) const
    {
            //std::cout<<"Call general case\n";
        linbox_check((y.size()==rowdim())&&(x.size()==coldim()));
        typename OutVector::iterator yp;
        typename InVector::const_iterator xp;
        Index* ip, *jp;

            // 0 out y.  Note, this implementation assumes a dense vector.
        for(yp = y.begin(); yp != y.end(); ++yp)
            field().assign(*yp , field().zero);

        rowSort();

        yp=y.begin();
        xp=x.begin();
        ip=_rowP;
        jp=_colP;
        size_t rowI =0;

        auto ipEnd = _rowP + nnz();
        for(; ip < ipEnd; ++ip,++jp)
        {
            if( *ip == rowI)
                field().addin(*yp,*(xp +(ptrdiff_t) *jp));
            else
            {
                if((*ip-rowI)==1)
                    ++yp;
                else
                    yp=y.begin()+(ptrdiff_t)*ip;

                rowI=*ip;
                field().addin(*yp,*(xp +(ptrdiff_t) *jp));
            }
        }
        return y;
    }

    template<class Field>
    template<class OutVector, class InVector>
    OutVector & ZeroOne<Field>::applySpecialization(OutVector & y, const InVector & x, const Mod32Field& m) const
    {
            //std::cout<<"Called specialization\n";
        linbox_check((y.size()==rowdim())&&(x.size()==coldim()));

        typename OutVector::iterator yp;
        typename InVector::const_iterator xp;
        typedef typename OutVector::value_type val_t ;
        Index* ip, *jp;

        for(yp = y.begin(); yp != y.end(); ++yp)
            field().assign(*yp , field().zero);

        rowSort();

        yp=y.begin();
        xp=x.begin();
        ip=_rowP;
        jp=_colP;
        size_t rowI =0;
        integer _prime;

        field().characteristic(_prime);

        uint32_t prime = static_cast<uint32_t>(_prime);

        uint64_t accum =0;

        for(; ip <_rowP+nnz(); ++ip,++jp)
        {
            if( *ip == rowI)
                accum=accum+*(xp +(ptrdiff_t) *jp);
            else
            {
                *yp= (val_t)(accum % prime);
                if((*ip-rowI)==1)
                    ++yp;
                else
                    yp=y.begin()+(ptrdiff_t)*ip;

                rowI=*ip;
                accum=*(xp+(ptrdiff_t)*jp);
            }
        }
        if(rowI)
            *yp= val_t(accum % prime);

        return y;
    }


    template<class Field>
    template<class OutVector, class InVector>
    OutVector & ZeroOne<Field>::applyTransposeSpecialization(OutVector & y, const InVector & x, const NormField& n) const
    {
            //std::cout<<"Call general case\n";
        linbox_check((y.size()==coldim())&&(x.size()==rowdim()));
        typename OutVector::iterator yp;
        typename InVector::const_iterator xp;
        Index* ip, *jp;

            // 0 out y.  Note, this implementation assumes a dense vector.
        for(yp = y.begin(); yp != y.end(); ++yp)
            field().assign(*yp , field().zero);

        rowSort();

        yp=y.begin();
        xp=x.begin();
        ip=_rowP;
        jp=_colP;
        size_t rowI =0;

        for(; ip <_rowP+nnz(); ++ip,++jp)
        {
            if( *ip == rowI)
                field().addin(*(yp+(ptrdiff_t)*jp),*xp);
            else
            {
                if((*ip-rowI)==1)
                    ++xp;
                else
                    xp=x.begin()+(ptrdiff_t)*ip;

                rowI=*ip;
                field().addin(*(yp+(ptrdiff_t)*jp),*xp);
            }
        }

        return y;
    }


    template<class Field>
    template<class OutVector, class InVector>
    OutVector & ZeroOne<Field>::applyTransposeSpecialization(OutVector & y, const InVector & x, const Mod32Field& m) const
    {
            //std::cout<<"Called specialization\n";
        linbox_check((y.size()==coldim())&&(x.size()==rowdim()));

        std::vector<uint64_t> y_c (y.size(),0);

        typename OutVector::iterator         yp;
        typename InVector::const_iterator    xp;
        typedef typename OutVector::value_type   val_t ;
        Index* ip, *jp;

        rowSort();

        xp=x.begin();
        ip=_rowP;
        jp=_colP;
        size_t rowI =0;
        std::vector<uint64_t>::iterator y_cp;
        y_cp=y_c.begin();

        for(; ip <_rowP+nnz(); ++ip,++jp)
        {
            if( *ip == rowI) {
                *(y_cp+(ptrdiff_t)*jp) += *xp;
            }
            else {
                if((*ip-rowI)==1)
                    ++xp;
                else
                    xp=x.begin()+(ptrdiff_t)*ip;

                rowI=*ip;
                *(y_cp+(ptrdiff_t)*jp) += *xp;
            }
        }

        integer _prime;
        field().characteristic(_prime);
        uint32_t prime = static_cast<uint32_t>(_prime);

        yp=y.begin();
        y_cp=y_c.begin();
        for(;yp!=y.end();++yp,++y_cp)
            *yp =(val_t)( (*y_cp) % prime );

        return y;
    }

}//End of LinBox

#endif // __LINBOX_bb_zero_one_INL

// Local Variables:
// mode: C++
// tab-width: 4
// indent-tabs-mode: nil
// c-basic-offset: 4
// End:
// vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
