#include "lib_matriz.h"
#include "elim_gauss.h"
#include "string.h"



#define PROMPT "> "
/* Comandos */
#define AJUDA        "ajuda"
#define CRIA         "cria"
#define DESTROI      "destroi"
#define LER          "ler"
#define IMPRIME      "imprime"
#define SOMACONST    "somaconst"
#define MULTCONST    "multconst"
#define SOMA         "soma"
#define MULT         "mult"
#define PIVOTEIA     "pivoteia"
#define GAUSSELIM    "gausselim"
#define RESOLVE      "resolve"
#define HILBERT      "hilbert"
#define RANDOM       "random"
#define RESIDUO      "residuo"
#define SAIR         "sair"

#define CHI  "\033[0;1m"
#define CLO  "\033[0m"

#define GREETINGS "Projeto de MS211\nGrupo:\n\tGustavo Sverzut Barbieri\tRA: 008849\n\tIvens Prates Telles Alves\tRA: 008908\n\tDanilo ???????????\tRA:00????\n\nDigite " CHI AJUDA CLO " para obter ajuda\n\n"

typedef struct matarr {
  Matriz *m;
  struct matarr *prev;
  struct matarr *next;
} MatArr;

void Ajuda(void);
void Cria(MatArr **m);
void Imprime(MatArr **m);
void Ler(MatArr **m);
void SomaConst(MatArr **m);
void MultConst(MatArr **m);
void Soma(MatArr **m);
void Multiplica(MatArr **m);
void Destroi(MatArr **m);
void Hilbert(MatArr **m);
void Random(MatArr **m);
void Pivoteia(MatArr **m);
void Eliminacao(MatArr **m);
void Resolve(MatArr **m);
void ResiduoSis(MatArr **m);

MatArr *matarrCria();
void matarrDestroi(MatArr **m);
void matarrInsere(MatArr ***m, int nl, int nc, char nome[]); 
void matarrPosiciona(MatArr ***m, char nome[]);

void (*ImprimeMatriz)();

int main(void) {
  MatArr *matriz;
  char opcao[255];

  matriz = matarrCria();

  fprintf(stderr,GREETINGS);
  fprintf(stderr,"Tipo de terminal(ascii/latex): ");
  scanf("%s",opcao);
  
  if (strcmp(opcao,"latex")==0)
    ImprimeMatriz = matImprimeLatex;
  else 
    ImprimeMatriz = matImprime;


  strcpy(opcao,"");
  while (strcmp(opcao,SAIR) != 0) {

    if      (strcmp(opcao,AJUDA) == 0)
      Ajuda();
    else if (strcmp(opcao,CRIA) == 0)
      Cria(&matriz);
    else if (strcmp(opcao,IMPRIME) == 0)
      Imprime(&matriz);
    else if (strcmp(opcao,LER) == 0)
      Ler(&matriz);
    else if (strcmp(opcao,SOMACONST) == 0)
      SomaConst(&matriz);
    else if (strcmp(opcao,MULTCONST) == 0)
      MultConst(&matriz);
    else if (strcmp(opcao,SOMA) == 0)
      Soma(&matriz);
    else if (strcmp(opcao,MULT) == 0)
      Multiplica(&matriz);
    else if (strcmp(opcao,DESTROI) == 0)
      Destroi(&matriz);
    else if (strcmp(opcao,HILBERT) == 0)
      Hilbert(&matriz);
    else if (strcmp(opcao,RANDOM) == 0)
      Random(&matriz);
    else if (strcmp(opcao,PIVOTEIA) == 0)
      Pivoteia(&matriz);
    else if (strcmp(opcao,GAUSSELIM) == 0)
      Eliminacao(&matriz);
    else if (strcmp(opcao,RESOLVE) == 0)
      Resolve(&matriz);
    else if (strcmp(opcao,RESIDUO) == 0)
      ResiduoSis(&matriz);


    fprintf(stderr,PROMPT);
    scanf("%s",opcao);
  }

  matarrDestroi(&matriz);
  return 0;
}

void Ajuda(void) {
  fprintf(stderr,"AJUDA:\n"
	  CHI CRIA " <NOME> <M> <N>" CLO " - Cria matriz nula <NOME> com <M> linhas e <N> colunas\n"
	  CHI DESTROI " <NOME>" CLO " - Destroi matriz <NOME>\n"
	  CHI LER " <NOME>" CLO " - Lê a matriz <NOME> da stdin\n"
	  CHI IMPRIME " <NOME>" CLO " - Imprime a matriz <NOME>\n"
	  CHI SOMA " <NOME> <A> <B>" CLO " - Faz <NOME> igual à soma as matrizes <A> e <B>\n"
	  CHI MULT " <NOME> <A> <B>" CLO " - Faz <NOME> igual à multiplicação das matrizes <A> e <B>\n"
	  CHI SOMACONST " <NOME> <A> <N>" CLO " - Faz <NOME> igual à soma da constante <N> à matriz <A>\n"
	  CHI MULTCONST " <NOME> <A> <N>" CLO " - Faz <NOME> igual à multiplicação da matriz <A> pelo constante <N>\n"
	  CHI SAIR CLO " - sai do programa\n"
	  CHI HILBERT " <NOME> <N>"  CLO " - cria matriz <NOME> de Hilbert de dimensões <N>x<N>\n"
	  CHI RANDOM " <NOME> <M> <N> <LO> <HI>" CLO " -  cria matriz <NOME> de dimensões <M>x<N> com elementos aleatórios entre <LO> e <HI>\n"
	  CHI PIVOTEIA " <NOME_A> <NOME_B>" CLO " - pivoteia as matrizes <NOME_A> e <NOME_B> simultaneamente, sendo <NOME_A> NxN e <NOME_B> Nx1\n"
	  CHI GAUSSELIM " <NOME_A> <NOME_B>" CLO " - faz a Eliminação de Gauss em <NOME_A> e <NOME_B> simultaneamente, sendo <NOME_A> NxN e <NOME_B> Nx1\n"
	  CHI RESOLVE " <NOME> <A> <B>" CLO " - Cria a matriz <NOME> que é a solução do Sistema <A>*<NOME>=<B>, sendo <A> e <B> previamente submetidas  à Eliminação de Gauss\n"
	  CHI RESIDUO " <A> <X> <B> " CLO " - retorna resíduo do sistema <A>*<X>=<B>\n"
	  "\nAtenção:\n\tOs nomes das matrizes devem ter, no máximo, 10 letras.\n");

} 


void Cria(MatArr **m) {
  int nl, nc;
  char nome[10];
  MatArr *tmp;

  scanf("%s",nome);
  scanf("%i",&nl);
  scanf("%i",&nc);

  if (*m != NULL) {
    matarrPosiciona(&m,nome);
    if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
      if ( (*m)->prev == (*m)->next ) {
	free(*m);
	*m = NULL;
      } else {
	((*m)->prev)->next = (*m)->next;
	((*m)->next)->prev = (*m)->prev;
	tmp = *m;
	*m = tmp->next;
	free(tmp);
	tmp = NULL;
      }
    }
  }
  matarrInsere(&m,nl,nc,nome);
  ImprimeMatriz((*m)->m,"% .3lf ");
}

void Hilbert(MatArr **m) {
  int n;
  char nome[10];
  MatArr *tmp;

  scanf("%s",nome);
  scanf("%i",&n);

  if (*m != NULL) {
    matarrPosiciona(&m,nome);
    if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
      if ( (*m)->prev == (*m)->next ) {
	free(*m);
	*m = NULL;
      } else {
	((*m)->prev)->next = (*m)->next;
	((*m)->next)->prev = (*m)->prev;
	tmp = *m;
	*m = tmp->next;
	free(tmp);
	tmp = NULL;
      }
    }
  }
  matarrInsere(&m,1,1,nome);
  free((*m)->m);
  (*m)->m = matHilbert(n,nome);
  ImprimeMatriz((*m)->m,"% .3lf ");
}

void Random(MatArr **m) {
  int nl, nc;
  double x0, x1;
  char nome[10];
  MatArr *tmp;

  scanf("%s",nome);
  scanf("%i",&nl);
  scanf("%i",&nc);
  scanf("%lf",&x0);
  scanf("%lf",&x1);

  if (*m != NULL) {
    matarrPosiciona(&m,nome);
    if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
      if ( (*m)->prev == (*m)->next ) {
	free(*m);
	*m = NULL;
      } else {
	((*m)->prev)->next = (*m)->next;
	((*m)->next)->prev = (*m)->prev;
	tmp = *m;
	*m = tmp->next;
	free(tmp);
	tmp = NULL;
      }
    }
  }
  matarrInsere(&m,1,1,nome);
  free((*m)->m);
  (*m)->m = matRandom(nl,nc,nome,x0,x1);
  ImprimeMatriz((*m)->m,"% .3lf ");
}



void Destroi(MatArr **m) {
  char nome[10];
  MatArr *tmp;

  scanf("%s",nome);

  if (*m != NULL) {
    matarrPosiciona(&m,nome);
    if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
      if ( (*m)->prev == (*m)->next ) {
	free(*m);
	*m = NULL;
      } else {
	((*m)->prev)->next = (*m)->next;
	((*m)->next)->prev = (*m)->prev;
	tmp = *m;
	*m = tmp->next;
	free(tmp);
	tmp = NULL;
      }
    }
  }
}


void Imprime(MatArr **m) {
  char nome[10];

  scanf("%s",nome);

  if (*m != NULL) {
    matarrPosiciona(&m,nome);
    if (strcmp((*m)->m->nome,nome)==0)
      ImprimeMatriz((*m)->m,"% .3lf ");
    else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nome);

}

void Ler(MatArr **m) {
  char nome[10];

  scanf("%s",nome);
  matarrPosiciona(&m,nome);
  if (*m != NULL) {
    if (strcmp((*m)->m->nome,nome)==0) {
      matLer((*m)->m);
      ImprimeMatriz((*m)->m,"% .3lf ");
    } else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome);
  }
}


void SomaConst(MatArr **m) {
  char nome[10];
  char nome2[10];
  Matriz *soma;
  MatArr *tmp;
  double cons;

  scanf("%s",nome);
  scanf("%s",nome2);  
  scanf("%lf",&cons);

  if (*m != NULL) {
    matarrPosiciona(&m,nome2);
    if (strcmp((*m)->m->nome,nome2)==0) {
      soma = matSomaEscalar((*m)->m,cons,nome);
      matarrPosiciona(&m,nome);
      if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
	if ( (*m)->prev == (*m) ) {
	  free(*m);
	  *m = NULL;
	} else {
	  ((*m)->prev)->next = (*m)->next;
	  ((*m)->next)->prev = (*m)->prev;
	  tmp = *m;
	  *m = tmp->next;
	  free(tmp);
	  tmp = NULL;
	}
      }
    matarrInsere(&m,0,0,nome);
    free((*m)->m);
    (*m)->m = soma;
    ImprimeMatriz((*m)->m,"% .3lf ");
    }  else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
    

}

void MultConst(MatArr **m) {
  char nome[10];
  char nome2[10];
  Matriz *mult;
  MatArr *tmp;
  double cons;

  scanf("%s",nome);
  scanf("%s",nome2);  
  scanf("%lf",&cons);

  if (*m != NULL) {
    matarrPosiciona(&m,nome2);
    if (strcmp((*m)->m->nome,nome2)==0) {
      mult = matMultiplicaEscalar((*m)->m,cons,nome);
      matarrPosiciona(&m,nome);
      if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
	if ( (*m)->prev == (*m) ) {
	  free(*m);
	  *m = NULL;
	} else {
	  ((*m)->prev)->next = (*m)->next;
	  ((*m)->next)->prev = (*m)->prev;
	  tmp = *m;
	  *m = tmp->next;
	  free(tmp);
	  tmp = NULL;
	}
      }
      matarrInsere(&m,0,0,nome);
      free((*m)->m);
      (*m)->m = mult;
      ImprimeMatriz((*m)->m,"% .3lf ");
    }  else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
    

}

void Soma(MatArr **m) {
  char nome[10];
  char nome2[10];
  char nome3[10];
  Matriz *soma;
  MatArr *tmp;

  scanf("%s",nome);
  scanf("%s",nome2);  
  scanf("%s",nome3);  

  if (*m != NULL) { 
    matarrPosiciona(&m,nome2);
    if (strcmp((*m)->m->nome,nome2)==0) {
      tmp = (*m);
      matarrPosiciona(&m,nome3);
      if (strcmp((*m)->m->nome,nome3)==0) {
	soma = matSoma(tmp->m,(*m)->m,nome);
	matarrPosiciona(&m,nome);
	if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
	  if ( (*m)->next == (*m) ) {
	    free(*m);
	    *m = NULL;
	  } else {
	    ((*m)->prev)->next = (*m)->next;
	    ((*m)->next)->prev = (*m)->prev;
	    tmp = *m;
	    *m = tmp->next;
	    free(tmp);
	    tmp = NULL;
	  }
	}
	if ( soma != NULL) {
	  matarrInsere(&m,0,0,nome);
	  free((*m)->m);
	  (*m)->m = soma;
	  ImprimeMatriz((*m)->m,"% .3lf ");
	}
      }  else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nome3);
    }  else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
    

}

void Multiplica(MatArr **m) {
  char nome[10];
  char nome2[10];
  char nome3[10];
  Matriz *mult;
  MatArr *tmp;

  scanf("%s",nome);
  scanf("%s",nome2);  
  scanf("%s",nome3);  


  if ( *m != NULL) {
    matarrPosiciona(&m,nome2);
    if (strcmp((*m)->m->nome,nome2)==0) {
      tmp = (*m);
      matarrPosiciona(&m,nome3);
      if (strcmp((*m)->m->nome,nome3)==0) {
	mult = matMultiplica(tmp->m,(*m)->m,nome);
	matarrPosiciona(&m,nome);
	if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
	  if ( (*m)->next == (*m) ) {
	    free(*m);
	    *m = NULL;
	  } else {
	    ((*m)->prev)->next = (*m)->next;
	    ((*m)->next)->prev = (*m)->prev;
	    tmp = *m;
	    *m = tmp->next;
	    free(tmp);
	    tmp = NULL;
	  }
	}
	if (mult != NULL) {
	  matarrInsere(&m,0,0,nome);
	  free((*m)->m);
	  (*m)->m = mult;
	  ImprimeMatriz((*m)->m,"% .3lf ");
	}
      }  else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nome3);
    }  else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
  } else 
        fprintf(stderr,"Matriz '%s' não existe!\n",nome2);
    
}

void Pivoteia(MatArr **m) {
  char nomeA[10];
  char nomeB[10];
  MatArr *tmp;

  scanf("%s",nomeA);
  scanf("%s",nomeB);

  if ( *m != NULL) {
    matarrPosiciona(&m,nomeA);
    if (strcmp((*m)->m->nome,nomeA)==0) {
      tmp = (*m);
      matarrPosiciona(&m,nomeB);
      if (strcmp((*m)->m->nome,nomeB)==0) {
	if ( ((*m)->m->nc == 1) && ((*m)->m->nl == tmp->m->nl) ) {
	  PivoteamentoParcial(tmp->m,(*m)->m);
	  ImprimeMatriz(tmp->m,"% .3lf ");
	  ImprimeMatriz((*m)->m,"% .3lf ");
	} else
	  fprintf(stderr,"Matrizes '%s' e '%s' incompatíveis para Pivoteamento!\n",nomeA,nomeB);
      } else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nomeB);
    } else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);

}

void Eliminacao(MatArr **m) {
  char nomeA[10];
  char nomeB[10];
  MatArr *tmp;

  scanf("%s",nomeA);
  scanf("%s",nomeB);

  if ( *m != NULL) {
    matarrPosiciona(&m,nomeA);
    if (strcmp((*m)->m->nome,nomeA)==0) {
      tmp = (*m);
      matarrPosiciona(&m,nomeB);
      if (strcmp((*m)->m->nome,nomeB)==0) {
	if ( ((*m)->m->nc == 1) && ((*m)->m->nl == tmp->m->nl) ) {
	  EliminacaoGauss(tmp->m,(*m)->m);
	  ImprimeMatriz(tmp->m,"% .3lf ");
	  ImprimeMatriz((*m)->m,"% .3lf ");
	} else
	  fprintf(stderr,"Matrizes '%s' e '%s' incompatíveis para Eliminacao!\n",nomeA,nomeB);
      } else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nomeB);
    } else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);

}

void Resolve(MatArr **m) {
  char nome[10];
  char nomeA[10];
  char nomeB[10];
  Matriz *res;
  MatArr *tmp;

  
  scanf("%s",nome);
  scanf("%s",nomeA);
  scanf("%s",nomeB);

  if ( *m != NULL) {
    matarrPosiciona(&m,nomeA);
    if (strcmp((*m)->m->nome,nomeA)==0) {
      tmp = (*m);
      matarrPosiciona(&m,nomeB);
      if (strcmp((*m)->m->nome,nomeB)==0) {
	if ( ((*m)->m->nc == 1) && ((*m)->m->nl == tmp->m->nl) ) {
	  res = ResolveSistemaTriangularSuperior(tmp->m,(*m)->m,nome);
	  matarrPosiciona(&m,nome);
	  if (strcmp((*m)->m->nome,nome)==0) { /* Matriz já existe */
	    if ( (*m)->prev == (*m)->next ) {
	      free(*m);
	      *m = NULL;
	    } else {
	      ((*m)->prev)->next = (*m)->next;
	      ((*m)->next)->prev = (*m)->prev;
	      tmp = *m;
	      *m = tmp->next;
	      free(tmp);
	      tmp = NULL;
	    }
	  }
	  if (res != NULL) {
	    matarrInsere(&m,0,0,nome);
	    free((*m)->m);
	    (*m)->m = res;
	    ImprimeMatriz((*m)->m,"% .3lf ");
	  }
	} else
	  fprintf(stderr,"Matrizes '%s' e '%s' incompatíveis para Resolução!\n",nomeA,nomeB);
      } else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nomeB);
    } else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);

}


void ResiduoSis(MatArr **m) {
  char nomeA[10];
  char nomeX[10];
  char nomeB[10];
  MatArr *tmp1;
  MatArr *tmp2;
  

  scanf("%s",nomeA);
  scanf("%s",nomeX);
  scanf("%s",nomeB);

  if ( *m != NULL) {
    matarrPosiciona(&m,nomeA);
    if (strcmp((*m)->m->nome,nomeA)==0) {
      tmp1 = (*m);
      matarrPosiciona(&m,nomeX);
      if (strcmp((*m)->m->nome,nomeX)==0) {
	tmp2 = (*m);
	matarrPosiciona(&m,nomeB);
	if (strcmp((*m)->m->nome,nomeB)==0) {

	  if ( ((*m)->m->nc == 1) && (tmp2->m->nc ==1) && ((*m)->m->nl == tmp1->m->nl) && (tmp2->m->nl == tmp1->m->nl) ) {
	    printf("Resíduo: %.3e\n",Residuo(tmp1->m,tmp2->m,(*m)->m));
	  } else
	    fprintf(stderr,"Matrizes '%s' e '%s' incompatíveis para Resíduo!\n",nomeA,nomeB);
	} else 
	  fprintf(stderr,"Matriz '%s' não existe!\n",nomeB);
      } else 
	fprintf(stderr,"Matriz '%s' não existe!\n",nomeX);
    } else 
      fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);
  } else 
    fprintf(stderr,"Matriz '%s' não existe!\n",nomeA);
}



/*---- Funcoes para Manipulaçao da Lista de Matrizes ------------------------------------------------------*/

MatArr *matarrCria() {
  return NULL;
}

void matarrDestroi(MatArr **m) {
  MatArr *tmp1, *tmp2;

  if (*m != NULL) {
    tmp1 = (*m)->next;
    while ( tmp1 != (*m) ) {
      tmp2 = tmp1;
      tmp1 = tmp1->next;
      free(tmp2);
    }
    free(*m);
  }
}

void matarrInsere(MatArr ***m,int nl, int nc, char nome[]) {
  MatArr *tmp = NULL;
  MatArr *aux;

  if ((tmp=(MatArr *) calloc(1,sizeof(MatArr))) == NULL) {
    fprintf(stderr,MSG0 "matarrInsereOk");
    exit;
  } else {
    tmp->m = matCria(nl,nc,nome);
    if ( **m == NULL) {
      tmp->next = tmp;
      tmp->prev = tmp;
    } else {
      aux = (**m)->next;
      (**m)->next = tmp;
      tmp->next = aux;
      tmp->next->prev = tmp;
      tmp->prev = (**m);
    }
    **m = tmp;
  } 
}

/* Posiciona o ponteiro m para o noh com a matriz 'nome' */
void matarrPosiciona(MatArr ***m, char nome[]) {
  MatArr *inicio;
  if (**m != NULL) {
    inicio = (**m)->prev;
    while ( (**m != inicio) && (strcmp((**m)->m->nome,nome)!=0) )
     (**m)=(**m)->next;
  }
}






