/*
 *  psychlops_m_matrix.h
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2006/03/30 by Kenchi HOSOKAWA
 *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */


#ifndef HEADER_PSYCHLOPS_MATH_MATRIX
#define HEADER_PSYCHLOPS_MATH_MATRIX


#include <iostream>
#include "../ApplicationInterfaces/psychlops_code.h"
#include "psychlops_m_interval.h"
#include "psychlops_m_function.h"
#include "psychlops_m_matrix.h"

#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif


namespace Psychlops {

	class Matrix;
	class MatrixExpression;
	std::ostream& operator<<(std::ostream& ost, const Matrix &mtx);
	MatrixExpression operator +(const Matrix &lhs, double rhs);
	MatrixExpression operator -(const Matrix &lhs, double rhs);
	MatrixExpression operator *(const Matrix &lhs, double rhs);
	MatrixExpression operator /(const Matrix &lhs, double rhs);
	MatrixExpression operator ^(const Matrix &lhs, double rhs);
	MatrixExpression operator +(const Matrix &lhs, const Matrix &rhs);
	MatrixExpression operator -(const Matrix &lhs, const Matrix &rhs);
	MatrixExpression operator *(const Matrix &lhs, const Matrix &rhs);
	//MatrixExpression operator /(const Matrix &lhs, const Matrix &rhs);
	MatrixExpression operator ~(const Matrix &lhs);

	//	these functions are alpha code
/*	Matrix & sin(Matrix &mtx);
	Matrix & cos(Matrix &mtx);
	Matrix & tan(Matrix &mtx);
	Matrix & exp(Matrix &mtx);
*/
	class Matrix {
		friend std::ostream& operator<<(std::ostream& ost, const Matrix &mtx);
		friend MatrixExpression operator +(const Matrix &lhs, double rhs);
		friend MatrixExpression operator -(const Matrix &lhs, double rhs);
		friend MatrixExpression operator *(const Matrix &lhs, double rhs);
		friend MatrixExpression operator /(const Matrix &lhs, double rhs);
		friend MatrixExpression operator ^(const Matrix &lhs, double rhs);
		friend MatrixExpression operator +(const Matrix &lhs, const Matrix &rhs);
		friend MatrixExpression operator -(const Matrix &lhs, const Matrix &rhs);
		friend MatrixExpression operator *(const Matrix &lhs, const Matrix &rhs);
	//	friend MatrixExpression operator /(const Matrix &lhs, const Matrix &rhs);
		friend MatrixExpression operator ~(const Matrix &lhs);

		public:
		enum OPERAND { NIL, INSTANCE, SLICE, ADD, SUB, MUL, DIV, ADD_SC, SUB_SC, MUL_SC, DIV_SC, MUL_MASK, POW, POW_MASK, MESH, MASK };

		protected:
		int rows_, cols_;
		int elementSize_;
		double *element_;
		bool has_instance_;
		OPERAND op_;

		protected:
		enum SIZE_DIFINITON { SIZE_UNDEFINED = -1 };
		struct elem_ptr;
		friend class elem_ptr;
		friend class MatrixExpression;

		Matrix & set_roughly(int rows, int cols);

		public:
		Matrix();
		Matrix(int rows, int cols);
		Matrix(const Matrix &mtx);
		Matrix & set(int rows, int cols);
		Matrix & set(Range row, double interval_rows, Range col, double interval_cols);
		Matrix & from(double *array);
		Matrix & from(double *array, int rows, int cols);
		virtual ~Matrix();

		void release();
			void destroy();	// obsolete
		virtual void track(int n) const;
		virtual void swap(Matrix &rhs);

		virtual bool has_instance() const;
		virtual Matrix* instance_ptr() const;
		virtual void get_element_ptr(double *&ptr_origin, double *&ptr_start, int &row_step) const;
		virtual bool track_self(const Matrix* self, int &cnt_in_add, int &cnt_in_mul, bool in_mul);
		virtual void track_and_calculate(Matrix& target, bool add_posi, bool mul_posi);
		virtual Matrix& operator =(double rhs);
		virtual Matrix& operator =(const Matrix &rhs);
		virtual Matrix& operator =(const MatrixExpression &rhs);
		virtual void substitute(double rhs);
		virtual void substitute(const Matrix &rhs);
		bool check_size_equal(const Matrix& rhs) const;
		bool check_size_mul(const Matrix& rhs) const;

		virtual double& xy(int x, int y) const;
		virtual double& operator ()(int row, int col) const;
		virtual MatrixExpression operator ()(int row, Range col);
		virtual MatrixExpression operator ()(Range row, int col);
		virtual MatrixExpression operator ()(Range row, Range col);

		void add(double rhs, bool add_posi);
		void add(const Matrix &rhs, bool add_posi);
		void mul(const Matrix &lhs, double rhs, bool add_posi);
		void mul(const Matrix &lhs, const Matrix &rhs, bool add_posi);
		void mul_mask(const Matrix &lhs, const Matrix &rhs, bool add_posi);
		void div(const Matrix &lhs, double rhs, bool add_posi);
		void pow(const Matrix &lhs, double rhs, bool add_posi);
		void pow_mask(const Matrix &lhs, double rhs, bool add_posi);

		Matrix & each(double (*f)(double));	// this method only for test. Don't use for your code.
		Matrix & each(double (*f)(double , double), double);	// this method only for test. Don't use for your code.

		virtual Matrix & slide(int drow, int dcol);
		virtual Matrix & transpose();
		virtual Matrix & rot90(int times);
		virtual Matrix & reshape(int rows, int cols);
		virtual Matrix & catRow(Matrix &mtx);

		virtual double max();
		virtual double min();

		virtual double trace();

		int getRows() const;
		int getCols() const;
			double* getElementPtr();

		void copy(double *target, Interval init_col, int row);


		void load(std::string filename); // this function is so limited, should be replaced by major CSV reader
		void save(std::string filename);


		//	Mesh
		public:
//		static MatrixExpression mesh(Wave &rows, Wave &cols);
//		static Matrix mesh(const Wave &roww, int rows, const Wave &colw, int cols);
//		static Matrix mesh(const Wave &roww, const Range rowrng, const Wave &colw, const Range colrng);
		static void   mesh(const Range rowrng, Matrix &rowmtx, const Range colrng, Matrix &colmtx);
		static Matrix mesh(int rows, const Range colrng);
		static Matrix mesh(const Range rowrng, int cols);
	};

	class MatrixExpression : public Matrix {
		public:
		static char OPERAND_SYMBOL[10];
		Matrix *lhs_;
		Matrix *rhs_;
		Wave *meshrow_, *meshcol_;
		Range rowrng_, colrng_;
		double scholar_;


		MatrixExpression();
		MatrixExpression(int rows, int cols, const Matrix &f, const Matrix &f2, const OPERAND op);
		MatrixExpression(int rows, int cols, const Matrix &f, double f2, OPERAND op);
		MatrixExpression(int rows, int cols, const Matrix &f, Range& rowrng, Range& colrng, OPERAND op);
		MatrixExpression(int rows, int cols, Wave &roww, Wave &colw, OPERAND op);
		MatrixExpression(const MatrixExpression &mtxc);

		virtual void track(int n) const;
		virtual ~MatrixExpression();
		virtual void swap(Matrix &rhs);

		virtual bool has_instance() const;
		virtual Matrix* instance_ptr() const;
		virtual void get_element_ptr(double *&ptr_origin, double *&ptr_start, int &row_step) const;
		void evaluate(Matrix& target);
		void evaluate(MatrixExpression& target);
		virtual bool track_self(const Matrix* self, int &cnt_in_add, int &cnt_in_mul, bool in_mul);
		virtual void track_and_calculate(Matrix& target, bool add_posi, bool mul_posi);
		//virtual void substitute(double rhs);
		//virtual void substitute(const Matrix &mtx);

		virtual double& xy(int x, int y) const;
		virtual double& operator ()(int row, int col) const;
		virtual MatrixExpression operator ()(int row, Range col);
		virtual MatrixExpression operator ()(Range row, int col);
		virtual MatrixExpression operator ()(Range row, Range col);
		virtual Matrix& operator =(double rhs);
		virtual Matrix& operator =(const Matrix &rhs);
		virtual Matrix& operator =(const MatrixExpression &rhs);
		//virtual double& operator ()(int row, int col);

		private:
		virtual Matrix & slide(int drow, int dcol);
		virtual Matrix & transpose();

	};

namespace MatrixOverload {
Matrix max(const Matrix& a, const Matrix b);
Matrix min(const Matrix& a, const Matrix b);

Matrix sin(const Matrix &mtx);
Matrix cos(const Matrix &mtx);
Matrix tan(const Matrix &mtx);
Matrix asin(const Matrix &mtx);
Matrix acos(const Matrix &mtx);
Matrix atan(const Matrix &mtx);

Matrix exp(const Matrix &mtx);
Matrix log(const Matrix &mtx);
Matrix log10(const Matrix &mtx);
Matrix pow(const Matrix &mtx, double ex);
Matrix sqrt(const Matrix &mtx);
Matrix abs(const Matrix &mtx);
}

}	/*	<- namespace Psycholops 	*/


#endif
