/*
 * Mtx.h
 *
 * reads in 2D matrix "mtx" file where first two int32 are the
 * dimensions, and the rest is a column major matrix of
 * single precision.
 *
 *  Created on: Feb 15, 2012
 *      Author: godin
 */

#ifndef MTX_H_
#define MTX_H_

#include "data/I2D.h"
#include "logger/Logger.h"
#include "logger/Syslog.h"

#include <math.h>
#include <sys/types.h>

class MtxInstream;

class Mtx: public I2D<float>
{
public:
    Mtx( Logger& logger, int m = 0, int n = 0, float initVal = nanf( "" ), Syslog::Severity severity = Syslog::FAULT );
    Mtx( Logger& logger, int m, int n, float* ptr, Syslog::Severity severity = Syslog::FAULT );
    Mtx( const char* filename, Logger& logger, Syslog::Severity severity = Syslog::FAULT );
    Mtx( const char* filename, Logger& logger, int minN, int maxN, Syslog::Severity severity = Syslog::FAULT );
    Mtx( const Mtx& rhs );
    virtual ~Mtx();
    void setSize( int m, int n );
    void clear();
    Mtx& operator=( const Mtx& rhs );
    Mtx& operator*=( const float rhs );
    Mtx& operator+=( const Mtx& rhs );
    Mtx& operator+=( const float rhs );
    Mtx& operator-=( const Mtx& rhs );
    Mtx operator*( const Mtx& rhs ) const;

    int getM() const
    {
        return m_;
    }

    virtual int getN() const
    {
        return n_;
    }

    virtual float** getPtr2d( void )
    {
        return value_;
    }

    virtual float* getPtr1d( void )
    {
        return value_[0];
    }

    float* operator[]( int index )
    {
        prepareValueForModification();
        return value_[index];
    };

    void set( int indexM, int indexN, float value )
    {
        if( indexM >= m_ || m_ + indexM < 0 || indexN >= n_ || n_ + indexN < 0 )
        {
            return ;
        }
        value_[indexM][indexN] = value;
    }

    void setInt( int indexM, int indexN, int value )
    {
        if( indexM >= m_ || m_ + indexM < 0 || indexN >= n_ || n_ + indexN < 0 )
        {
            return ;
        }
        FloatInt floatInt;
        floatInt.int_ = value;
        value_[indexM][indexN] = floatInt.float_;
    }

    float get( int indexM, int indexN ) const
    {
        if( indexM >= m_ || m_ + indexM < 0 || indexN >= n_ || n_ + indexN < 0 )
        {
            return nanf( "" );
        }
        return value_[indexM][indexN];
    }

    int getInt( int indexM, int indexN ) const
    {
        if( indexM >= m_ || m_ + indexM < 0 || indexN >= n_ || n_ + indexN < 0 )
        {
            return 0;
        }
        FloatInt floatInt;
        floatInt.float_ = value_[indexM][indexN];
        return floatInt.int_;
    }

    void setFromPtr( const int m, const int n, const void* ptr )
    {
        setSize( m, n );
        memcpy( value_[0], ptr, m * n * sizeof( float ) ); // Don't usually like working at the level of memcpy, but it seems the most straightforward way to do this.

    }

    float operator()( int indexM, int indexN ) const
    {
        if( indexM >= m_ || m_ + indexM < 0 || indexN >= n_ || n_ + indexN < 0 )
        {
            return nanf( "" );
        }
        return value_[indexM >= 0 ? indexM : m_ + indexM][indexN >= 0 ? indexN : n_ + indexN];
    };

    Mtx subset( int minM = 0, int maxM = -1, int minN = 0, int maxN = -1 ) const;

    static bool CopyValues( const Mtx& src, int srcStartM, int srcStartN, const Mtx& dst, int dstStartM, int dstStartN, int numM, int numN );

    bool write( const char* filename, Logger& logger,
                int minM = 0, int maxM = -1, int minN = 0, int maxN = -1,
                Syslog::Severity severity = Syslog::FAULT );

    bool append( const char* filename, Logger& logger,
                 Syslog::Severity severity = Syslog::FAULT );

    bool valid() const
    {
        return m_ > 0 && n_ > 0;
    }

    bool isUnlimitedN() const
    {
        return unlimitedN_;
    }

    void setUnlimitedN( bool unlimitedN )
    {
        unlimitedN_ = unlimitedN;
    }

    /// Simple sort routine, invented by Donald Shell 1959.
    /// Returns true if the data is modified.
    bool shellSort( int ( &comp )( const Mtx&, int, const Mtx&, int ) );

    /// Comparison function for indexM = 0;
    static int Comp0( const Mtx& lhs, int lhsJ, const Mtx& rhs, int rhsJ );

    /// Comparison function for indexM = 1;
    static int Comp1( const Mtx& lhs, int lhsJ, const Mtx& rhs, int rhsJ );

    /// Comparison function for indexM = 2;
    static int Comp2( const Mtx& lhs, int lhsJ, const Mtx& rhs, int rhsJ );

    /// Comparison function for indexM = 0 AND indexM = 1;
    static int Comp01( const Mtx& lhs, int lhsJ, const Mtx& rhs, int rhsJ );

    /// Comparison function for indexM = 0 AND indexM = 2;
    static int Comp02( const Mtx& lhs, int lhsJ, const Mtx& rhs, int rhsJ );


protected:
    void prepareValueForModification();
    int m_;
    int n_;
    float** value_;
    Logger& logger_;
    Syslog::Severity severity_;
    bool unlimitedN_;
    union FloatInt
    {
        float float_;
        int32_t int_;
    };
};

#endif /* MTX_H_ */
