#include <math.h>
#include <iomanip>
#include "Polinomio.h"
#include "Matriz.h"
 
using namespace std;
#ifndef _MATRIZ_H_
#define _MATRIZ_H_
 
 
 
 
class Matriz
    {
    double **a;          //array de punteros a array double, contiene los terminos de la matriz
protected:
    int length;     //orden de la matriz (n filas x ncolumnas)
    double det;
    Polinomio ct;   //Polinomio caracteristico de la matriz
public:
    Matriz (int n);   // reserva memoria, lenght=n: MatrizTridiag A(n);
    ~Matriz ();            // destructor
    bool redim(int n);
    void operator=(const Matriz& B);                // A=B;
    double operator () (int i,int j) const ;    // a=m(i,j);
    double& operator () (int i,int j);      // function: m(i,j)=a;
    Matriz operator + (const Matriz& B);        // C=A+B;
    void read();                    // A.read();
    void fill();                    // A.fill();
    void print () const;                // A.print();
    double menores(int i);
    void factdet();
    void ccaracteristico();
    void mcaracteristico();
    void cambiarfilas(int i,int j);
    double determinante() {return det;}
    int size () const {return length;}          // n = A.size();
    };
#endif
 
 
// Matriz A(n);
 
Matriz::Matriz(int n)
 
{
 
    int i,j;
 
    length = n;
    det=1;
    ct.q= new double [length];
    for(i=0;i<n;i++) ct.q[i]=0;
 
    // reserva memoria
 
    a = new double *[n];  // reserva memoria para el vector de punteros
 
    for (i=0;i<n;i++) a[i] = new double[n]; // reserva memoria para cada fila
 
    for(i=0;i<n;i++) for(j=0;j<n;j++) a[i][j]=0; // inicializa a cero
 
 
 
}
 
// destructor
 
Matriz::~Matriz()
 
{
 
    if( a != NULL)
 
    {       //  libera memoria
 
        for(int i=0;i<length;i++) delete [] a[i];
 
        delete [] a;
 
    }
 
    if( ct.q != NULL)
    {
        delete [] ct.q;
    }
 
 
 
}
 
// A = B
 
void Matriz::operator = (const Matriz& B)
 
{
 
    redim(B.size());
 
    for(int i=0;i<length;i++) for(int j=0;j<length;j++) a[i][j]=B.a[i][j];
 
}
 
//  function: a=m[i,j];
 
inline double Matriz::operator() (int i,int j) const
 
{ return a[i][j];}
 
// function: m[i,j]=a;
 
double& Matriz::operator() (int i,int j)
 
{ return a[i][j];}
 
// operador suma: C=A+B
 
Matriz Matriz::operator+(const Matriz& B)
 
{
 
    if(length != B.length) cout<< " error: matrices de distinta dimension "<<endl;
 
    Matriz temp(length);
 
    for(int i=0;i<length;i++) for(int j=0;j<length;i++) temp.a[i][j]=a[i][j]+B.a[i][j];
 
    return temp;
 
}
 
// producto matriz por vector: A.prod(v,w);
 
 
 
//  redimensionamiento de una matriz
 
bool Matriz::redim(int n)
 
{
 
    if (length == n)
 
        return false;  // no es necesario redimensionar
 
    else
 
    {
 
        if( a!= NULL)
 
        {
 
            // primero libera memoria
 
            for(int i=0;i<length;i++) delete [] a[i];
 
            delete [] a;
 
        }
 
        length = n;
 
        // reserva memoria
 
        a = new double *[n];  // reserva memoria para el vector de punteros
 
        for (int i=0;i<n;i++) a[i] = new double [n]; // reserva memoria para cada fila
 
        return true;
 
    }
 
}
 
// A.read()
 
void Matriz::read ()
 
{
 
    for(int i=0;i<length;i++)
 
    {
 
        cout << "fila "<<i<<"-esima" << "n";
 
        for(int j=0;j<length;j++)
 
        {
 
            cout << "termino a["<<i<<"]["<<j<<"] = ?" << "n";
 
            cin >> a[i][j];
 
        }
 
    }
 
}
 
// A.fill()
 
void Matriz::fill()
 
{
 
    for(int i=0;i<length;i++)
 
    {
 
        a[i][i]=4;
 
        if(i<length-1)
 
        {
 
            a[i+1][i]=-1;
 
            a[i][i+1]=-1;
 
        }
 
        if(i<length-3)
 
        {
 
            a[i+3][i]=-1;
 
            a[i][i+3]=-1;
 
        }
 
    }
 
}
 
// A.print()
 
void Matriz::print () const
 
{
 
    for(int i=0;i<length;i++)
 
    {
 
        for(int j=0;j<length;j++)
 
            cout << a[i][j]<<"  ";
 
            cout<<endl;
 
    }
 
}
 
// A.factLU()
 
void Matriz::factdet()
 {
     int permutaciones=0;
     int i,j;
 
 
    for(int cp=0;cp<length-1;cp++)
    {
 
 
        for(i=1;i<length-cp;i++){
        if(a[cp][cp]==0)    //si el pivote es cero cambiar filas
        {
            cambiarfilas(cp,cp+i);
            permutaciones++;
        }
        }
        if(a[cp][cp]==0)
            det= 0;//si el pivote sigue siendo cero, el det es cero.
 
        //ahora ya aplicar formula de ferragut
    for(i=cp+1;i<length;i++)
    {
        double pivote=a[i][cp]/a[cp][cp];
        for(j=0;j<length;j++)
        {
            a[i][j]=a[i][j]-pivote*a[cp][j];
        }
 
    }
    }
        //se calcula el determinante
    for(i=0;i<length;i++)
        {det=det*a[i][i];}
 
        det=det*pow(-1,permutaciones);
 
 
 }
 
 void Matriz::cambiarfilas(int i, int j){
     double tem;
     for(int l=0;l<length;l++){
         tem=a[i][l];
         a[i][l]=a[j][l];
         a[j][l]=tem;
 
     }
 }
double Matriz::menores(int i){
    double coef=0;
    int j,k,l;
    for (j=0;j<(length-i+1);j++);{  //recorre la diagonal en busca de menores
    Matriz menor(i);            //crea menores de orden deseado
        for (k=0;k<i;k++);
        {
            for (l=0;l<i;l++);      //los iguala a la matriz menor para no perturbar la principal
            menor.a[k+j][l+j]=a[k+j][l+j];
        }
    menor.factdet();        //calcula el determinante
    coef+=menor.det;        //los va sumando
    menor.~Matriz();
    }
    return coef;
}//en caso de pasar i=length devuelve el determinante de la matriz
void Matriz::ccaracteristico(){
 
    ct.q[length-1]=pow(-1,length);
 
        for(int i=0;i<length-1;i++){
            ct.q[i]=menores(length-i)*pow(-1,i);
        }
 
 
}
void Matriz::mcaracteristico(){
    ct.print1();
}
 
 
 
 
 
int main() {
 
    Matriz prueba(2);
    prueba.fill();
 
    Matriz p(2);
    p.fill();
    Matriz c(2);
    c=prueba+p; //al llegar aqui
    c.print();
    p.ccaracteristico(); //o aqui, sale del programa sin terminar de ejecutarlo
    p.mcaracteristico();
 
 
 
 
 
    return 0;
 
}