#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

typedef struct {
  double m; 
  double x, y;
  double vx, vy;
  double fx, fy;
} particle;

double LX, LY;
void evolve(particle *p, int N, double deltat, double *energy, double *K); 

int main() {
  int N; // numero di particelle
  particle * p;
  int t, T;
  int i, j;
  double deltat;
  double thre;
  FILE * gp; 
  double temp;
  int VIS;
  double energy, K;
 
  N = 100;
  T = 1000000;
  VIS = 100;
  deltat = 0.01;
  LX = 100; 
  LY = 100;
  thre = 0.2;
  temp = 4;
  
  p = calloc(N, sizeof(particle));
  
  gp = popen("gnuplot", "w");
  fprintf(gp, "set size square; unset key; set xrange [0:%f]; set yrange [0:%f]\n", LX, LY);
  // initial conditions
  srand48(time(NULL));
  
  for (i=0; i<N; i++) {
    double x, y, r2;
    int flag;
    
    p[i].m = 1;
    p[i].vx = temp * (2* drand48() - 1);
    p[i].vy = temp * (2* drand48() - 1);
    
    do {
      flag = 0;
      x = LX * drand48();
      y = LY * drand48();
      for (j=0; j<i; j++) {
        r2 = (x-p[j].x)*(x-p[j].x) + (y-p[j].y)*(y-p[j].y);
        if (r2 < thre) {
          flag = 1;
          break;
        }
      }
    } while (flag == 1);
    p[i].x = x;
    p[i].y = y;
  }
    
  
  // main loop;
  for (t=0; t<T; t++) {
   
    // va in una funzione
    evolve(p, N, deltat, &energy, &K); 

    
    // visualizzazione
    if (t % VIS == 0) {
      fprintf(gp, "set title 'energy=%f, K = %f'\n", energy, K);
      fprintf(gp, "plot '-' binary record=%d format='%%double' w p pt 6 \n", N);
      for (i=0; i<N; i++) {
        fwrite(&(p[i].x), sizeof(double),2, gp);
       // fwrite(&(p[i].y), sizeof(double),1, gp);
      }
      fflush (gp);
    }
  }
 
  
}

void evolve(particle *p, int N, double deltat, double *energy, double *K) {
    int i, j;
    double deltat2;
    double rx, ry, r2, rm2,rm6,ff;

    
    deltat2=deltat*deltat;
    
    *energy = *K = 0;
    //avanzo le posizioni
    for (i=0; i<N; i++) {
      p[i].x += p[i].vx * deltat + 0.5*p[i].fx*deltat2/p[i].m;
      p[i].y += p[i].vy * deltat + 0.5*p[i].fy*deltat2/p[i].m;
      // condizioni al bordo
      if (p[i].x < 0) {
        p[i].x *= -1;
        p[i].vx *= -1;
      }
      if (p[i].y < 0) {
        p[i].y *= -1;
        p[i].vy *= -1;
      } 
      if (p[i].x > LX) {
        p[i].x = 2 * LX - p[i].x;
        p[i].vx *= -1;
      }
      if (p[i].y > LX) {
        p[i].y = 2 * LY - p[i].y;
        p[i].vy *= -1;
      } 
   }
    // prima metà della velocità  
    for (i=0; i<N; i++) {
      p[i].vx += 0.5 * p[i].fx*deltat/p[i].m;
      p[i].vy += 0.5 * p[i].fy*deltat/p[i].m;
      p[i].fx = 0;
      p[i].fy = 0;
    }
    
    //calcolo delle forze
    for (i=0; i<N-1; i++) {    // loop su metà delle particelle
      for (j=i+1; j<N; j++) {
        rx = p[j].x - p[i].x; 
        ry = p[j].y - p[i].y; 
        r2 = rx*rx+ry*ry;
        rm2 = 1/r2;
        rm6 = rm2*rm2*rm2;
        ff = -24*rm6*(2*rm6-1)*rm2;
        p[i].fx += ff * rx;
        p[i].fy += ff * ry;
        p[j].fx -= ff * rx;
        p[j].fy -= ff * ry;
        *energy += 4*rm6*(rm6-1);
      }
    }
    //seconda metà della velocità 
    for (i=0; i<N; i++) {
      p[i].vx += 0.5 * p[i].fx*deltat/p[i].m;
      p[i].vy += 0.5 * p[i].fy*deltat/p[i].m;
      *K += 0.5*(p[i].vx*p[i].vx+p[i].vy*p[i].vy)*p[i].m;
    }
    *energy += *K;
    *energy /= N; // energia per particella
    *K /= N;
}