/*************************************************************************************
 *  Copyright (C) 2010 by Aleix Pol <aleixpol@kde.org>                               *
 *  Copyright (C) 2014 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com>      *
 *                                                                                   *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "matrix.h"
#include "abstractexpressionvisitor.h"
#include "vector.h"
#include "value.h"

using namespace Analitza;

Matrix::Matrix()
	: Object(matrix)
	, m_hasOnlyNumbers(true)
{}

Matrix::Matrix(int m, int n, const Cn* value)
	: Object(matrix)
	, m_hasOnlyNumbers(true)
{
	Q_ASSERT(m > 0);
	Q_ASSERT(n > 0);
	Q_ASSERT(value);
	
	for (int i = 0; i < m; ++i) {
		MatrixRow *row = new Analitza::MatrixRow(n);
		for (int j = 0; j < n; ++j) {
			row->appendBranch(value->copy());
		}
		appendBranch(row);
	}
}

Matrix::~Matrix()
{
	qDeleteAll(m_rows);
}

Matrix* Matrix::copy() const
{
	Matrix* nm = new Matrix;
	foreach(MatrixRow* r, m_rows) {
		nm->appendBranch(r->copy());
	}
	
	return nm;
}

bool Matrix::matches(const Object* exp, QMap< QString, const Object* >* found) const
{
	if(Object::matrix!=exp->type())
		return false;
	const Matrix* c=(const Matrix*) exp;
	if(m_rows.count()!=c->m_rows.count())
		return false;
	
	bool matching=true;
	Matrix::const_iterator it, it2, itEnd=m_rows.constEnd();
	for(it=m_rows.constBegin(), it2=c->m_rows.constBegin(); matching && it!=itEnd; ++it, ++it2) {
		matching &= (*it)->matches(*it2, found);
	}
	
	return matching;
}

QVariant Matrix::accept(AbstractExpressionVisitor* exp) const
{
	return exp->visit(this);
}

void Matrix::appendBranch(MatrixRow* o)
{
	Q_ASSERT(dynamic_cast<MatrixRow*>(o));
	Q_ASSERT(dynamic_cast<MatrixRow*>(o)->size()>0);
	Q_ASSERT(m_rows.isEmpty()?true:dynamic_cast<MatrixRow*>(o)->size() == dynamic_cast<MatrixRow*>(m_rows.last())->size()); // all rows are same size
	
	if (!o->hasOnlyNumbers() && m_hasOnlyNumbers)
		m_hasOnlyNumbers = false;
	
	m_rows.append(static_cast<MatrixRow*>(o));
}

bool Matrix::operator==(const Matrix& m) const
{
	bool eq = m.rowCount()==rowCount();
	
	for(int i=0; eq && i<m_rows.count(); ++i) {
		eq = eq && *static_cast<MatrixRow*>(m_rows[i])==*static_cast<MatrixRow*>(m.m_rows[i]);
	}
	
	return eq;
}

Object* Matrix::at(int i, int j) const
{
	return m_rows.at(i)->at(j);
}

int Matrix::columnCount() const
{
	return m_rows.isEmpty()? 0 : static_cast<const MatrixRow*>(m_rows.first())->size();
}

bool Matrix::isSquare() const
{
	return m_rows.isEmpty()? true : m_rows.size() == static_cast<const MatrixRow*>(m_rows.first())->size();
}

////// MatrixRow
MatrixRow::MatrixRow(int size)
	: Vector(Object::matrixrow, size)
{}

QVariant MatrixRow::accept(AbstractExpressionVisitor* e) const
{
	return e->visit(this);
}

MatrixRow* MatrixRow::copy() const
{
	MatrixRow *m=new MatrixRow(size());
	for(MatrixRow::const_iterator it=constBegin(); it!=constEnd(); ++it) {
		m->appendBranch((*it)->copy());
	}
	return m;
}

Matrix* Matrix::identity(int n)
{
	Q_ASSERT(n>0);
	
	Analitza::Matrix *ret = new Analitza::Matrix();
	
	for (int i = 0; i < n; ++i) {
		MatrixRow *row = new Analitza::MatrixRow(n);
		for (int j = 0; j < n; ++j) {
			if (i == j)
				row->appendBranch(new Analitza::Cn(1));
			else
				row->appendBranch(new Analitza::Cn(0));
		}
		ret->appendBranch(row);
	}
	
	return ret;
}

bool Matrix::isIdentity() const
{
	if (!hasOnlyNumbers())
		return false;
	
	const int nrows = m_rows.size();
	
	for (int row = 0; row < nrows; ++row)
		if (!m_rows.at(row)->isStandardBasisVector() || static_cast<const Cn*>(at(row, row))->value() != 1)
			return false;
	
	return true;
}

bool Matrix::isDiagonal() const
{
	if (!hasOnlyNumbers())
		return false;
	
	const int nrows = m_rows.size();
	
	for (int row = 0; row < nrows; ++row)
		if (!m_rows.at(row)->isDiagonalRow())
			return false;
	
	return true;
}

bool Matrix::isZero() const
{
	bool zero = false;
	foreach(const MatrixRow* o, m_rows) {
		zero |= o->isZero();
	}

	return zero;
}
