#include "init/init.h"
 #include "random/random.h"
 #include "visual/visual.h"
+#include "list/list.h"
 
 int moldyn_usage(char **argv) {
 
        return tau;     
 }
 
+/*
+ * numerical tricks
+ */
+
+/* verlet list */
+
+int verlet_list_init(t_moldyn *moldyn) {
+
+       int i,fd;
+
+       fd=open("/dev/null",O_WRONLY);
+
+       for(i=0;i<moldyn->count;i++)
+               list_init(&(moldyn->atom[i].verlet),fd);
+
+       moldyn->r_verlet=1.1*moldyn->cutoff;
+       /* +moldyn->tau*\
+               sqrt(3.0*K_BOLTZMANN*moldyn->t/moldyn->atom[0].mass); */
+
+       printf("debug: r verlet = %.15f\n",moldyn->r_verlet);
+       printf("       r cutoff = %.15f\n",moldyn->cutoff);
+       printf("       dim      = %.15f\n",moldyn->dim.x);
+
+       /* make sure to update the list in the beginning */
+       moldyn->dr_max1=moldyn->r_verlet;
+       moldyn->dr_max2=moldyn->r_verlet;
+
+       return 0;
+}
+
+int link_cell_init(t_moldyn *moldyn) {
+
+       t_linkcell *lc;
+
+       lc=&(moldyn->lc);
+
+       /* partitioning the md cell */
+       lc->nx=moldyn->dim.x/moldyn->cutoff;
+       lc->x=moldyn->dim.x/lc->nx;
+       lc->ny=moldyn->dim.y/moldyn->cutoff;
+       lc->y=moldyn->dim.y/lc->ny;
+       lc->nz=moldyn->dim.z/moldyn->cutoff;
+       lc->z=moldyn->dim.z/lc->nz;
+
+       lc->subcell=malloc(lc->nx*lc->ny*lc->nz*sizeof(t_list));
+
+       link_cell_update(moldyn);
+       
+       return 0;
+}
+
+int verlet_list_update(t_moldyn *moldyn) {
+
+       int i,j;
+       t_3dvec d;
+       t_atom *atom;
+
+       atom=moldyn->atom;
+
+       puts("debug: list update start");
+
+       for(i=0;i<moldyn->count;i++) {
+               list_destroy(&(atom[i].verlet));
+               for(j=0;j<moldyn->count;j++) {
+                       if(i!=j) {
+                               v3_sub(&d,&(atom[i].r),&(atom[j].r));
+                               v3_per_bound(&d,&(moldyn->dim));
+                               if(v3_norm(&d)<=moldyn->r_verlet)
+                                       list_add_immediate_ptr(&(atom[i].verlet),&(atom[j]));
+                       }
+               }
+       }
+
+       moldyn->dr_max1=0.0;
+       moldyn->dr_max2=0.0;
+
+       puts("debug: list update end");
+
+       return 0;
+}
+
+int link_cell_update(t_moldyn *moldyn) {
+
+       int count,i,j,k;
+       int nx,ny,nz;
+       t_atom *atom;
+
+       atom=moldyn->atom;
+       nx=moldyn->lc.nx; ny=moldyn->lc.ny; nz=moldyn->lc.nz;
+
+       for(i=0;i<nx*ny*nz;i++)
+               list_destroy(&(moldyn->lc.subcell[i]));
+       
+       for(count=0;count<moldyn->count;count++) {
+               for(i=0;i<nx;i++) {
+                       if((atom[count].r.x>=i*moldyn->dim.x) && \
+                          (atom[count].r.x<(i+1)*moldyn->dim.x))
+                               break;
+               }
+               for(j=0;j<ny;j++) {
+                       if((atom[count].r.y>=j*moldyn->dim.y) && \
+                          (atom[count].r.y<(j+1)*moldyn->dim.y))
+                               break;
+               }
+               for(k=0;k<nz;k++) {
+                       if((atom[count].r.z>=k*moldyn->dim.z) && \
+                          (atom[count].r.z<(k+1)*moldyn->dim.z))
+                               break;
+               }
+               list_add_immediate_ptr(&(moldyn->lc.subcell[i+j*nx+k*nx*ny]),
+                                      &(atom[count]));
+       }
+
+       return 0;
+}
+
+int verlet_list_shutdown(t_moldyn *moldyn) {
+
+       int i;
+
+       for(i=0;i<moldyn->count;i++)
+               list_shutdown(&(moldyn->atom[i].verlet));
+
+       return 0;
+}
+
+int link_cell_shutdown(t_moldyn *moldyn) {
+
+       int i;
+       t_linkcell *lc;
+
+       lc=&(moldyn->lc);
+
+       for(i=0;i<lc->nx*lc->ny*lc->nz;i++)
+               list_shutdown(&(moldyn->lc.subcell[i]));
+
+       return 0;
+}
 
 /*
  *
        int i;
        unsigned int e,m,s,d,v;
        t_3dvec p;
+       double rlc;
 
        int fd;
        char fb[128];
 
+       /* logging & visualization */
        e=moldyn->ewrite;
        m=moldyn->mwrite;
        s=moldyn->swrite;
        d=moldyn->dwrite;
        v=moldyn->vwrite;
 
+       /* verlet list */       
+       rlc=moldyn->r_verlet-moldyn->cutoff;
+
        if(!(moldyn->lvstat&MOLDYN_LVSTAT_INITIALIZED)) {
                printf("[moldyn] warning, lv system not initialized\n");
                return -1;
        }
 
+       /* create the verlet list */
+       verlet_list_update(moldyn);
+
        /* calculate initial forces */
        moldyn->force(moldyn);
 
        for(i=0;i<moldyn->time_steps;i++) {
+               /* check for velet list update */
+               printf(".");
+               if(moldyn->dr_max1+moldyn->dr_max2>rlc) {
+                       printf("\n");
+                       verlet_list_update(moldyn);
+               }
+
                /* integration step */
                moldyn->integrate(moldyn);
 
 int velocity_verlet(t_moldyn *moldyn) {
 
        int i,count;
-       double tau,tau_square;
+       double tau,tau_square,dr;
        t_3dvec delta;
        t_atom *atom;
 
                /* new positions */
                v3_scale(&delta,&(atom[i].v),tau);
                v3_add(&(atom[i].r),&(atom[i].r),&delta);
+               v3_add(&(atom[i].dr),&(atom[i].dr),&delta);
                v3_scale(&delta,&(atom[i].f),0.5*tau_square/atom[i].mass);
                v3_add(&(atom[i].r),&(atom[i].r),&delta);
+               v3_add(&(atom[i].dr),&(atom[i].dr),&delta);
                v3_per_bound(&(atom[i].r),&(moldyn->dim));
 
+               /* set maximum dr (possible list update) */
+               dr=v3_norm(&(atom[i].dr));
+               if(dr>moldyn->dr_max1) {
+                       moldyn->dr_max2=moldyn->dr_max1;
+                       moldyn->dr_max1=dr;
+               }
+               else if(dr>moldyn->dr_max2) moldyn->dr_max2=dr;
+
                /* velocities */
                v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
                v3_add(&(atom[i].v),&(atom[i].v),&delta);
 double potential_lennard_jones(t_moldyn *moldyn) {
 
        t_lj_params *params;
-       t_atom *atom;
-       int i,j;
+       t_atom *atom,*btom;
+       int i;
        int count;
        t_3dvec distance;
        double d,help;
 
        u=0.0;
        for(i=0;i<count;i++) {
-               for(j=0;j<i;j++) {
-                       v3_sub(&distance,&(atom[i].r),&(atom[j].r));
+               list_reset(&(atom[i].verlet));
+               if(atom[i].verlet.current==NULL) continue;
+               while(1) {
+                       btom=atom[i].verlet.current->data;
+                       v3_sub(&distance,&(atom[i].r),&(btom->r));
+                       v3_per_bound(&distance,&(moldyn->dim));
                        d=1.0/v3_absolute_square(&distance);    /* 1/r^2 */
                        help=d*d;                               /* 1/r^4 */
                        help*=d;                                /* 1/r^6 */
                        d=help*help;                            /* 1/r^12 */
-                       u+=eps*(sig6*help-sig12*d);
+                       u+=eps*(sig12*d-sig6*help);
+                       if(list_next(&(atom[i].verlet))==L_NO_NEXT_ELEMENT)
+                               break;
                }
        }
        
 int force_lennard_jones(t_moldyn *moldyn) {
 
        t_lj_params *params;
-       int i,j,count;
-       t_atom *atom;
+       int i,count;
+       t_atom *atom,*btom;
        t_3dvec distance;
        t_3dvec force;
        double d,h1,h2;
        for(i=0;i<count;i++) v3_zero(&(atom[i].f));
 
        for(i=0;i<count;i++) {
-               for(j=0;j<i;j++) {
-                       v3_sub(&distance,&(atom[i].r),&(atom[j].r));
+               list_reset(&(atom[i].verlet));
+               if(atom[i].verlet.current==NULL) continue;
+               while(1) {
+                       btom=atom[i].verlet.current->data;
+                       v3_sub(&distance,&(atom[i].r),&(btom->r));
                        v3_per_bound(&distance,&(moldyn->dim));
                        d=v3_absolute_square(&distance);
                        if(d<=moldyn->cutoff_square) {
                                d*=eps;
                                v3_scale(&force,&distance,d);
                                v3_add(&(atom[i].f),&(atom[i].f),&force);
-                               v3_sub(&(atom[j].f),&(atom[j].f),&force);
+                               //v3_sub(&(atom[j].f),&(atom[j].f),&force);
                        }
+                       if(list_next(&(atom[i].verlet))==L_NO_NEXT_ELEMENT)
+                               break;
                }
        }
 
 
 
 #include "math/math.h"
 #include "random/random.h"
+#include "list/list.h"
 
 /* datatypes */
 
        t_3dvec r;      /* positions */
        t_3dvec v;      /* velocities */
        t_3dvec f;      /* forces */
+       t_3dvec dr;     /* delta r for verlet list updates */
        int element;    /* number of element in pse */
        double mass;    /* atom mass */
-       //t_list vicinity       /* verlet neighbour list */
+       t_list verlet;  /* verlet neighbour list */
 } t_atom;
 
+typedef struct s_linkcell {
+       int nx,ny,nz;
+       double x,y,z;
+       t_list *subcell;
+} t_linkcell;
+
 #include "visual/visual.h"
 
 typedef struct s_moldyn {
        double (*potential)(struct s_moldyn *moldyn);
        int (*force)(struct s_moldyn *moldyn);
        void *pot_params;
+       /* cut off radius, verlet list & co */
        double cutoff;
        double cutoff_square;
+       double r_verlet;
+       double dr_max1;
+       double dr_max2;
+       /* linked list / cell method */
+       t_linkcell lc;
        /* temperature */
        double t;
        /* integration of newtons equations */
 
 double estimate_time_step(t_moldyn *moldyn,double nn_dist,double t);
 
+int verlet_list_init(t_moldyn *moldyn);
+int link_cell_init(t_moldyn *moldyn);
+int verlet_list_update(t_moldyn *moldyn);
+int link_cell_update(t_moldyn *moldyn);
+int verlet_list_shutdown(t_moldyn *moldyn);
+int link_cell_shutdown(t_moldyn *moldyn);
+
 int moldyn_integrate(t_moldyn *moldyn);
 int velocity_verlet(t_moldyn *moldyn);
 
 
        t_random random;
 
        int a,b,c;
-       double e,u;
+       double e;
        double help;
        t_3dvec p;
        int count;
        md.force=force_lennard_jones;
        //md.potential=potential_harmonic_oscillator;
        //md.force=force_harmonic_oscillator;
-       md.cutoff=R_CUTOFF;
-       md.cutoff_square=(R_CUTOFF*R_CUTOFF);
+       md.cutoff=R_CUTOFF*LC_SI;
+       md.cutoff_square=md.cutoff*md.cutoff;
        md.pot_params=&lj;
        //md.pot_params=&ho;
        md.integrate=velocity_verlet;
        //md.tau=TAU;
        md.status=0;
        md.visual=&vis;
+       /* dimensions of the simulation cell */
+       md.dim.x=a*LC_SI;
+       md.dim.y=b*LC_SI;
+       md.dim.z=c*LC_SI;
+
+       /* verlet list init */
+       // later integrated in moldyn_init function!
+       verlet_list_init(&md);
 
        printf("setting thermal fluctuations (T=%f K)\n",md.t);
        thermal_init(&md,&random,count);
 
        e=get_e_kin(si,count);
        printf("kinetic energy: %.40f [J]\n",e);
-       printf("3/2 N k T = %.40f [J]\n",1.5*count*K_BOLTZMANN*md.t);
+       printf("3/2 N k T = %.40f [J] (T=%f [K])\n",
+              1.5*count*K_BOLTZMANN*md.t,md.t);
 
        /* check total momentum */
        p=get_total_p(si,count);
        printf("total momentum: %.30f [Ns]\n",v3_norm(&p));
 
-       /* check potential energy */
+       /* potential paramters */
        lj.sigma6=LJ_SIGMA_SI*LJ_SIGMA_SI;
        help=lj.sigma6*lj.sigma6;
        lj.sigma6*=help;
        ho.equilibrium_distance=0.25*sqrt(3.0)*LC_SI;
        ho.spring_constant=1.0;
 
-       u=get_e_pot(&md);
-
-       printf("potential energy: %.40f [J]\n",u);
-       printf("total energy (1): %.40f [J]\n",e+u);
-       printf("total energy (2): %.40f [J]\n",get_total_energy(&md));
-
-       md.dim.x=a*LC_SI;
-       md.dim.y=b*LC_SI;
-       md.dim.z=c*LC_SI;
-
        printf("estimated accurate time step: %.30f [s]\n",
               estimate_time_step(&md,3.0,md.t));
 
 
        /* close */
 
+       verlet_list_shutdown(&md);
+
        rand_close(&random);
 
        moldyn_shutdown(&md);