#include "Vect.h"

#include "Gauss.h"
#include "Solver.h"

static bool IsColumnNull(Matrix& mat, std::size_t column) {
	for (std::size_t i = 0; i < mat.GetRawCount(); i++) {
		if (!IsEqualZero(mat.at(i, column))) {
			return false;
		}
	}
	return true;
}

Vect::Vect(Matrix&& a_Matrix) : m_Data(std::move(a_Matrix)) {
	m_Data.Transpose();
	Gauss::GaussJordan(m_Data, false, true);
	m_Data.Transpose();
	Simplify();
}

void Vect::Simplify() {
	Matrix mat = m_Data;
	for (std::size_t j = 0; j < mat.GetColumnCount(); j++) {
		if (IsColumnNull(mat, j)) {
			m_Data = mat.SubMatrix(0, 0, mat.GetRawCount(), j);
			return;
		}
	}
	m_Data = mat;
}

Matrix Vect::GetVector(std::size_t a_Index) const {
	return m_Data.SubMatrix(0, a_Index, m_Data.GetRawCount(), 1);
}

std::size_t Vect::GetCardinal() const {
	return m_Data.GetColumnCount();
}

bool Vect::IsElementOf(const Matrix& a_Vector) const {
	Vect base = *this;
	base.AddVector(a_Vector);
	return base.GetCardinal() == GetCardinal();
}

bool Vect::operator==(const Vect& a_Other) const {
	if (GetDimension() != a_Other.GetDimension() || GetCardinal() != a_Other.GetCardinal())
		return false;

	// on vérifie si chaque vecteur de la deuxième base appartient à l'espace vectoriel engendré par la première base
	for (std::size_t i = 0; i < GetCardinal(); i++) {
		if (!IsElementOf(a_Other.GetVector(i)))
			return false;
	}
	return true;
}

void Vect::AddVector(const Matrix& a_Vector) {
	m_Data.Augment(a_Vector);
	m_Data.Transpose();
	Gauss::GaussJordan(m_Data, false, false);
	m_Data.Transpose();
	Simplify();
}

bool Vect::operator!=(const Vect& a_Other) const {
	return !(*this == a_Other);
}

Matrix Vect::GetLinearSystem() const {
	Matrix vect = m_Data;
	vect.Transpose();

	Solver solver;
	Matrix result = solver.Kernel(std::move(vect)).m_Data;
	result.Transpose();
	return result;
}

std::size_t Vect::GetDimension() const {
	return m_Data.GetRawCount();
}

VectAffine::VectAffine(const Vect& a_Base, const Matrix& a_Origin) :
	m_Base(a_Base), m_Origin(a_Origin.SubMatrix(0, 0, m_Base.GetDimension(), 1)) {}

bool VectAffine::IsElementOf(const Matrix& a_Vector) const {
	return m_Base.IsElementOf(a_Vector - m_Origin);
}

Matrix VectAffine::GetLinearSystem() const {
	Matrix result = m_Base.GetLinearSystem();

	result.Augment(m_Origin.SubMatrix(0, 0, result.GetRawCount(), 1));

	return result;
}