/***************************************************************************
 *   Copyright (C) 2004 by Armando Navarro-Vázquez                         *
 *   qoajnv@usc.es                                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef IARRAY_H
#define IARRAY_H

#include <assert.h>
#include <string.h>
#include <iostream>

#include "ccmath.h"

template <class X> class IMArray
{

private:
  X* m_data;
public:
  IMArray() : m_size(0) { m_data=NULL; }
  explicit IMArray(size_t i) : m_size(i) { m_data= new X[i]; }
  IMArray(size_t i,const X& x);
  IMArray(const IMArray& x);
  void Initialize(size_t i) { if(m_data) delete [] m_data; m_data = new X[i]; m_size=i; }
  void Initialize(size_t i, const X& x);
~IMArray() { if(m_data) delete [] m_data; }
  X& operator ()(size_t i);
  const X& operator() (size_t i) const;
  operator X*() { return m_data; }
  operator const X*() const { return m_data; }
  IMArray& operator=(const IMArray& x);
  IMArray& operator-=(const IMArray& x);
  IMArray& operator+=(const IMArray& x);
  size_t size() const { return m_size; }
  X norm() const;
  
private:
  size_t m_size;

};

template<class X> IMArray<X>::IMArray(size_t i, const X& x) : m_size(i)
{
  m_data = new X[i];
  for(size_t k=0;k<i;k++)
    m_data[k]=x;
}
template <class X> IMArray<X>::IMArray(const IMArray<X>& x)
{
  m_size=x.size();
  m_data= new X[m_size];

  memcpy(m_data,x,m_size*sizeof(X));

}

template <class X> IMArray<X>& IMArray<X>::operator=(const IMArray<X>& x)
{
  if(m_data) delete [] m_data;
  m_size=x.size();
  m_data= new X[m_size];
  memcpy(m_data,x,m_size*sizeof(X));
  return *this;

}
template<class X> void IMArray<X>::Initialize(size_t i, const X& x)
{
  if (m_data) delete [] m_data;
  m_size=i;
  m_data = new X[i];
  for(size_t k=0;k<i;k++)
    m_data[k]=x;
}

template <class X> X& IMArray<X>::operator ()(size_t i)
{
  assert( i < m_size );
  return m_data[i];
}

template <class X> const X& IMArray<X>::operator ()(size_t i) const
{
  assert( i < m_size );
  return m_data[i];
}

template <class X> X IMArray<X>::norm() const
{
  X sum=0;
  for(size_t i=0;i<m_size;++i)
  {
     sum+=m_data[i]*m_data[i];
  }
  return sqrt(sum);
}

template <class X> IMArray<X>& IMArray<X>::operator-=(const IMArray<X>& x)
{
  assert( this->size() == x.size() );
  for(size_t i=0;i<m_size;++i)
  {
    m_data[i]=m_data[i]-x(i);
  }
  
  return *this;
}

template <class X> IMArray<X>& IMArray<X>::operator+=(const IMArray<X>& x)
{
  assert( this->size() == x.size() );
  for(size_t i=0;i<m_size;++i)
  {
    m_data[i]=m_data[i]+x(i);
  }
  return *this;
}

template<class X> class IBArray
{
private:
  X* m_data;
public:
  IBArray() { m_rows=m_columns=0; m_data=NULL; }
  explicit IBArray(size_t i, size_t j) : m_rows(i), m_columns(j) { m_data = new X[i*j]; }
  IBArray(size_t i,size_t j, const X& x);
  IBArray(const IBArray& x);
  IBArray& operator=(const IBArray& x);
  void Initialize(size_t i, size_t j) { m_rows=i; m_columns=j; if(m_data) delete [] m_data; m_data= new X[i*j]; }
  void Initialize(size_t i, size_t j,const X& x);
  ~IBArray() { if(m_data) delete [] m_data; };
  X& operator() (size_t i, size_t j);
  const X& operator() (size_t i, size_t j) const;
  operator X*() { return m_data; }
  operator const X*() const { return m_data; }
  //arithmetic operators

  size_t rows() const { return m_rows; }
  size_t columns() const { return m_columns; }
  size_t size() const { return m_rows*m_columns; }
  void swaprows(size_t i, size_t j);
  void swapcolumns(size_t i, size_t j);
  IBArray<X> RemoveRow(size_t row);
  IBArray<X> RemoveColumn(size_t column);
  IMArray<X> GetRow(size_t row) const;
  IMArray<X> GetColumn(size_t column) const;
  IBArray<X> Transpose() const;


private:
  size_t m_rows;
  size_t m_columns;
};



template<class X> IBArray<X>::IBArray(size_t i, size_t j, const X& x) : m_rows(i), m_columns(j)
{
  m_data = new X[i*j];
  for(size_t k=0;k<i;k++)
    for(size_t n=0;n<j;n++)
      m_data[j*k+n]=x;
}

template<class X> void IBArray<X>::Initialize(size_t i, size_t j, const X& x)
{
  if(m_data) delete [] m_data;
  m_rows=i;
  m_columns=j;

  m_data = new X[i*j];
  for(size_t k=0;k<i;k++)
    for(size_t n=0;n<j;n++)
      m_data[j*k+n]=x;

}

template <class X> IBArray<X>::IBArray(const IBArray<X>& x)
{
  m_rows=x.rows();
  m_columns=x.columns();
  m_data= new X[m_rows*m_columns];
  memcpy(m_data,x,m_rows*m_columns*sizeof(X));
}

template <class X> IBArray<X>& IBArray<X>::operator=(const IBArray& x)
{
  if(m_data) delete [] m_data;
  m_rows=x.rows();
  m_columns=x.columns(); 
  m_data= new X[m_rows*m_columns];
  memcpy(m_data,x,m_rows*m_columns*sizeof(X));
  return *this; 
}
template <class X> X& IBArray<X>::operator() (size_t i, size_t j)
{
  return m_data[i*m_columns+j];
}

template <class X> const X& IBArray<X>::operator() (size_t i, size_t j) const
{
  return m_data[i*m_columns+j];
}

template <class X> void IBArray<X>::swaprows(size_t i, size_t j)
{

  for(size_t k=0;k<m_columns;k++)
  {
    X tmp=m_data[m_columns*j+k];
    m_data[m_columns*j+k]=m_data[m_columns*i+k];
    m_data[m_columns*i+k]=tmp;
  }
}

template <class X> void IBArray<X>::swapcolumns(size_t i, size_t j)
{

  for(size_t k=0;k<m_rows;k++)
  {
    X tmp=m_data[m_columns*k+i];
    m_data[m_columns*k+i]=m_data[m_columns*k+j];
    m_data[m_columns*k+j]=tmp;
  }
}


template <class X> IBArray<X> IBArray<X>::RemoveRow(size_t row)
{
  IBArray<X> nmat(m_rows-1,m_columns);
  size_t k=0;
  for(size_t i=0;i<m_rows;i++)
  {
    if( i != row )
    {
      for(size_t j=0;j<m_columns;j++)
        nmat(k,j)=(*this)(i,j);
      k++;
    }

  }

  return nmat;
}

template <class X> IBArray<X> IBArray<X>::RemoveColumn(size_t column)
{
  IBArray<X> nmat(m_rows,m_columns-1);

  for(size_t i=0;i<m_rows;i++)
  {
    size_t k=0;
    for(size_t j=0;j<m_columns;j++)
    {
      if(j!=column)
        nmat(i,k++)=(*this)(i,j);
    }
  }
  return nmat;
}

template <class X> IMArray<X> IBArray<X>::GetRow(size_t row) const
{
  IMArray<X> fila(m_columns);
  for(size_t i=0;i<m_columns;i++)
    fila(i)=(*this)(row,i);

  return fila;
}

template <class X> IMArray<X> IBArray<X>::GetColumn(size_t column) const
{
  IMArray<X> columna(m_rows);
  for(size_t i=0;i<m_rows;i++)
    columna(i)=(*this)(i,column);

  return columna;
}


template <class X> IBArray<X> IBArray<X>::Transpose() const 
{
  IBArray<X> transposed(this->columns(),this->rows());
  for(size_t i=0;i<this->rows();++i)
    for(size_t j=0;j<this->columns();++j)
      transposed(j,i)=(*this)(i,j);
}
typedef IMArray<float> sIMArray;
typedef IMArray<double> dIMArray;
typedef IBArray<float> sIBArray;
typedef IBArray<double> dIBArray;

/** specializations*/
inline IBArray<double> operator *(const IBArray<double>& a,const IBArray<double>& b)
{
  assert( a.columns() == b.rows() );
  IBArray<double> c(a.rows(),b.columns());

  ccmath::mmul(c,const_cast<double*>(static_cast<const double*>(a)),const_cast<double*>(static_cast<const double*>(b)),a.rows());
  return c;
}

inline IMArray<double> operator *(const IBArray<double>& a,const IMArray<double>& b)
{
  assert(a.rows() == b.size());

  IMArray<double> c(a.rows());

  ccmath::vmul(c,const_cast<double*>(static_cast<const double*>(a)),const_cast<double*>(static_cast<const double*>(b)),a.rows());



  return c;
}

template<class X> IMArray<X> operator *(const X& a, const IMArray<X>& x)
{
  IMArray<X> pr(x.size());
  for(size_t i=0;i<x.size();++i)
  {
    pr(i)=a*x(i);
  }
  return pr;
}

template<class X> X operator *(const IMArray<X>& a, const IMArray<X>& b)
{
  assert(a.size() == b.size() );
  X sum=0;
  for(size_t i=0;i<a.size();++i)
  {
    sum+=a(i)*b(i);
  }
  return sum;
}
template<class X> std::ostream& operator << (std::ostream& s,const IBArray<X>& x)
{
  for(size_t i=0;i<x.rows();++i)
    for(size_t j=0;j<x.columns();++j)
      {
         s << "X(" << i << "," << j << ")=" << x(i,j) << std::endl;
      }
    
  return s;
}


#endif //IARRAY_H

