/**
   * Az Ax=b egyenletrendszer megoldasahoz a Jacobi iteráció.
   * x* az Ax*=b megoldása.
   */
   
   /*
   * Bemenet:
   * n
   * A
   * b
   * x0
   * maxit
   * e
   */
   
   /* Ahol
   * n       : nxn -es mátrix.
   * A       : mátrix
   * b       : Ax=b egyenletrendszer jobb oldala.
   * x0      : kezdővektor
   * maxit   : maximális iteráció száma.
   * e       : küszöbszám. xk és xk+1 közötti különbség ha ettől kisebb, akkor vége.
   */

typedef struct sor
{
       int oszlop;
       double ertek;
       struct sor *kov;
} SOR;

void Jacobi_iteracio(char in[], char out[]);
void beolvas2 ( char in[], SOR ** A, double* b, double *x0, double *xk, int* maxit, double * e );
void rendez( SOR ** f, int n);
void BESZUR_ELEJE ( SOR ** f, int o, double e );
void Beszuro_rendezes(double *A, int *oszlop, int hossz);

void BESZUR_VEGE ( SOR ** f, int o, double e );
double ELEM ( SOR ** f, int j );
void VEKTORmulVEKTORjacobi  ( SOR **sorok, double *vektor, int n, double *b );
void TOROL_LISTA ( SOR ** f );
void Eredmeny_kiiras( char out[], int n, double * vektor, int it );
double Ellenorzes_legnagyobb_kulonbseg( double * x, int n);
double Euklideszi_norma_vektorra( double * vektor, int o );
void Ellenorzes(double *A, int n, double * b, double * x);

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys\timeb.h>
/*#include "leak_detector_c.h"*/

int hiba=0;

int main (int argc, char* argv[]){
    if (argc<3){
      puts("Hiba! Kevés argumentumot adott meg.");
      puts("A program helyes futtatása:\nprogramnév bemeneti_fájl_neve_kiterjesztéssel kimeneti_fájl_neve_kiterjesztéssel\n");
      return 1;
     }
     else if (argc>3){
       puts("Hiba! Túl sok argumentumot adott meg.");
       puts("A program helyes futtatása:\nprogramnév bemeneti_fájl_neve_kiterjesztéssel kimeneti_fájl_neve_kiterjesztéssel\n");
       return 1;
     }

    struct timeb t_start, t_current;
    int t_diff_in_sec, t_diff_in_ms;
    ftime(&t_start);
    
    Jacobi_iteracio(argv[1], argv[2]);
    
    ftime(&t_current);
      t_diff_in_sec = (int)  (t_current.time - t_start.time);
      t_diff_in_ms = (int ) (t_current.millitm - t_start.millitm);

    FILE *fout;
    fout = fopen(argv[2], "a");    
    printf("\nEltelt idő: %d ms",t_diff_in_sec*1000+t_diff_in_ms);
    fprintf(fout, "\nEltelt idő: %d ms",t_diff_in_sec*1000+t_diff_in_ms);
    fclose(fout);
    
    /*atexit(report_mem_leak);*/
    return 0;
}
/*____________________________________________________________________________*/
void Jacobi_iteracio(char in[], char out[]){
    int n, i, j, k, l, maxit;
    n = i = j = k = l = maxit = 0;
    
    FILE *fin, *fout;
    
    fin = fopen(in, "r");
    fout = fopen(out, "w");
    
    fscanf(fin, "%d\n",&n);
    /*double (*A)[n] = NULL;
    A = calloc (n, sizeof (*A));*/
    
    double *b = (double *) calloc( n, ( sizeof ( double ) ) );
    double *x0 = (double *) calloc( n, ( sizeof ( double ) ) );
    double *xk = (double *) calloc( n, ( sizeof ( double ) ) );
    double e;
    
    SOR *sorok[n];
    /*SOR *sorok = (SOR*) calloc(n, sizeof(SOR));*/
    
    /*----------------------------------------*/
    /* A matrix beolvasasa */
	fclose(fin);
  beolvas2 ( in, sorok, b, x0, xk, &maxit, &e );
	/*----------------------------------------*/
	for ( k=0; k<maxit; k++ ){      
      VEKTORmulVEKTORjacobi ( sorok, xk, n, b ); /* xk-ban lesz az eredmeny. b-re szükség van a számolásnál. */
      if ( hiba == 1 ) {
        puts("hiba"); fputs("hiba", fout);
        fflush(fout);
        fclose(fout);
         free(b); free(x0); free(xk);
        for( i=0; i<n; i++ ) TOROL_LISTA(&sorok[i]);
        return;
      }
      double y[n];
      for( i=0; i<n; i++ ){
        y[i]=x0[i]-xk[i];
      }
      double temp2 = Euklideszi_norma_vektorra( y, n );
      temp2 = sqrt(temp2);
      if ( temp2 <=e  ){
        fflush(fout);
        Eredmeny_kiiras( out, n, x0, k );
        /*Ellenorzes( (double *)A, n, b, x0 );*/
        fflush(fout);
        fclose(fout);
        /*free(A);*/ free(b); free(x0); free(xk);
        for( i=0; i<n; i++ ) TOROL_LISTA(&sorok[i]);
        return;
      }
      
      for ( j=0; j<n; j++ ) x0[j] = xk[j];
      
      printf("%d.\n", k);

      if( k+1 ==maxit ){
    puts("A következő iterációs lépésben véget ér a program!\n");
    printf("A legnagyobb külünbség: kezdővektor-kapottmegoldás: \n");
    puts("Szeretné folytatni az iterációt, annak érdkében, hogy jobb eredményt kapjon?\n");
    int jo=0;
    char valasz[10];
    
    while( jo == 0 ){
      puts("Parancsok: [i|n], [igen|nem], [y|n], [yes|no]");
      scanf("%s", valasz);
      if( strncmp(valasz, "i", strlen("i"))==0 || strncmp(valasz, "igen", strlen("igen"))==0 || 
          strncmp(valasz, "y", strlen("y"))==0 || strncmp(valasz, "yes", strlen("yes"))==0){
        int ujmaxit=0;
        printf("uj maxit: "); scanf("%d", &ujmaxit);
        while( ujmaxit<=maxit ){
          puts("HIBA! A megadott új maximális iterációszám kisebb, mint a jelenlegi!\n");
          printf("Adjon %d -tól/-től nagyobb számot!\n", maxit);
          printf("uj maxit: "); scanf("%d", &ujmaxit);
        }
        maxit=ujmaxit;
        jo=1;
      }
      else if( strncmp(valasz, "n", strlen("n"))==0 || strncmp(valasz, "nem", strlen("nem"))==0 || 
                strncmp(valasz, "no", strlen("no"))==0)
                jo=1;
      else      
        puts("HIBA! Rossz parancsot adott meg!");
                
    }
  }
    }
    
    fflush(fout);
    Eredmeny_kiiras( out, n, x0, k );
    /*Ellenorzes( (double *)A, n, b, x0 );*/
    fflush(fout);
    fclose(fout);
    free(b); free(x0); free(xk);
    for( i=0; i<n; i++ ) TOROL_LISTA(&sorok[i]);
     /*Ellenorzes( (double *)A, n, b, x0 );*/
     
return ;
}
/*____________________________________________________________________________*/
void beolvas2 ( char in[], SOR ** A, double* b, double *x0, double *xk, int* maxit, double * e ){
  int n, i, j, nnz, k;
  
  FILE *fin;    
  fin = fopen(in, "r");
  
  fscanf(fin, "%d\n",&n);
  fscanf(fin, "%d\n",&nnz);
   
  for ( i=0; i<n; i++ ){
    A[i]=NULL;
  }
  for ( k=0; k<nnz; k++ ){
    double temp;
    fscanf(fin, "%d %d %lf", &i, &j, &temp);
    BESZUR_ELEJE(&A[i], j, temp);
    if ( i!=j )
      BESZUR_ELEJE(&A[j], i, temp);
  }  
  
  rendez(A, n);
  
  for ( i=0; i<n; i++ ){
    fscanf(fin, "%lf", &b[i] );
  }
  for ( i=0; i<n; i++ ){
    fscanf(fin, "%lf", &x0[i] );
    xk[i] = x0[i];
  }
  fscanf(fin, "%d", maxit);
  fscanf(fin, "%lf", e);
}
/*____________________________________________________________________________*/
void BESZUR_ELEJE ( SOR ** f, int o, double e ){
  SOR *uj;
  uj = (SOR *) malloc (sizeof (SOR));
  uj->oszlop=o;
  uj->ertek=e;
    
  uj->kov=*f;
  *f=uj;
  return ;
}
/*______________________________________________________________________________________*/
void rendez( SOR ** f, int n){
  int i;
  
  for( i=0; i<n; i++){
    SOR *x=f[i];
    int sorhossz;
    for( sorhossz=0; x!=NULL; sorhossz++ ) x=x->kov;
    if ( sorhossz > 1 && sorhossz>0){
      int *rendezett_oszlop = (int *) calloc( sorhossz, ( sizeof ( int ) ) );
      double *rendezett_ertek = (double *) calloc( sorhossz, ( sizeof ( double ) ) );
      for( x=f[i], sorhossz=0; x!=NULL; sorhossz++, x=x->kov ){
        rendezett_ertek[sorhossz]=x->ertek;
        rendezett_oszlop[sorhossz]=x->oszlop;
      }
      Beszuro_rendezes(rendezett_ertek, rendezett_oszlop, sorhossz);
      for( x=f[i], sorhossz=0; x!=NULL; sorhossz++, x=x->kov ){
        x->ertek=rendezett_ertek[sorhossz];
        x->oszlop=rendezett_oszlop[sorhossz];
      }
      free(rendezett_ertek);
      free(rendezett_oszlop);
    }    
  }
}
/*______________________________________________________________________________________*/
void Beszuro_rendezes(double *A, int *oszlop, int hossz){
  if(hossz<2) return; /* nem kell rendezni */
  int i,j;
        
  double kulcs_e;
  int kulcs_o;
  for( j=1; j<hossz; j++ ){
    kulcs_e=A[j];
    kulcs_o=oszlop[j];
    i=j-1;
    /*puts("while");*/
    while ( i>=0 && oszlop[i]>kulcs_o ){      
      A[i+1]=A[i];
      oszlop[i+1]=oszlop[i];
      i=i-1;
    }
    A[i+1]=kulcs_e;
    oszlop[i+1]=kulcs_o;
  }
}
/*____________________________________________________________________________*/
void BESZUR_VEGE ( SOR ** f, int o, double e ){
  SOR *uj,*x;
  uj = (SOR *) malloc (sizeof (SOR));
  uj->oszlop=o;
  uj->ertek=e;
    
  uj->kov=NULL;
  if ( *f==NULL ){ *f=uj; }
  else
  {
     x=*f;
     while ( x->kov!=NULL )
           x=x->kov;
     x->kov=uj;
  }
  return ;
}
/*____________________________________________________________________________*/
double ELEM ( SOR ** f, int j ){
  SOR *x;
  x = *f;
  if( x->oszlop > j ) return 0.0;
  while ( x != NULL ){
    if ( x->oszlop == j )
      return x->ertek;
    else if( x->oszlop >j )
      return 0.0;
    x=x->kov;
  }
  return 0.0;
}
/*____________________________________________________________________________*/
void VEKTORmulVEKTORjacobi  ( SOR **sorok, double *vektor, int n, double *b ){
  /*puts("mul");*/
  int i, j;
  double aii=0.0;
    
  for(i=0; i<n; i++){
    /*SOR *x=&sorok[i];*/
    SOR *x=*(sorok+i);
    double sum=0.0;
    aii=0.0;
    for ( j=0; j<n && x!=NULL; j++ ){
      if ( i==j && x->oszlop==j) aii=x->ertek;
            
      if ( i!=j && x->oszlop==j && vektor[j]!=0.0 ){
        sum += x->ertek * vektor[j];
        x=x->kov;
      }
      else if ( x->oszlop==j )
        x=x->kov;
    }
    if ( fabs( aii ) <= pow ( 10,-15 ) ) {
      hiba=1;
      return;
    }    
    vektor[i] = (1/aii)*( sum * -1.0 + b[i] );
  }
  return ;
}
/*____________________________________________________________________________*/
void TOROL_LISTA ( SOR ** f ){
  SOR *temp;
  
  while ( *f != NULL)
  {  
     temp = (*f) -> kov;
     free (*f);
     *f = temp;
  }
  return;
}
/*____________________________________________________________________________*/
void Eredmeny_kiiras( char out[], int n, double * vektor, int it ){
  
  FILE *fout;

  fout = fopen(out, "a");
  
  int i;
  for ( i=0; i<n; i++ ){
    if (i<n-1)
      fprintf(fout, "%.5lf ", vektor[i] );
     else
      fprintf(fout, "%.5lf", vektor[i] );
  }
  fputs("", fout);
  printf("A legnagyobb külünbség: ||kezdővektor-kapottmegoldás||= %.16lf\n", Ellenorzes_legnagyobb_kulonbseg(vektor, n) );
  fprintf(fout, "\n\nA legnagyobb külünbség: ||kezdővektor-kapottmegoldás||= %.16lf\n", Ellenorzes_legnagyobb_kulonbseg(vektor, n) );
  printf("Lépések száma: %d", it);
  fprintf(fout, "Lépések száma: %d", it);
  fflush(fout);
  fclose(fout);
return;
}
/*____________________________________________________________________________*/
double Ellenorzes_legnagyobb_kulonbseg( double * x, int n){
  double *temp=(double *) calloc( n, ( sizeof ( double ) ) );
  int i=0;
  for( ; i<n; i++ )
    temp[i] = 1-x[i];
    
  return sqrt( Euklideszi_norma_vektorra(temp, n) );
  
}
/*____________________________________________________________________________*/
double Euklideszi_norma_vektorra( double * vektor, int o ){
       double temp=0.0;
       int szamlal;
       for ( szamlal=0; szamlal<o; szamlal++ ){
           temp = temp + (vektor[szamlal]*vektor[szamlal]);
       }
       return temp;
}
/*____________________________________________________________________________*/
void Ellenorzes(double *A, int n, double * b, double * x){
  int i, j;
  double temp[n];
  puts("\n\nEllenorzes: Ax=b");    
  puts("b:\t\t\tAx:");
    for ( i=0; i<n; i++ ){
      temp[i]=0;
      for ( j=0; j<n; j++ ){
        temp[i] += A[i*n+j]*x[j];
        /*printf("\n%lf += %lf*%lf ", temp[i], A[i*n+j], x[j]);*/
      }
      printf( "b[%d]=%lf\t\t%lf\t\t\n",i, b[i], temp[i] );
    }
}
