//
// lu2lib.c
//
// J. Makino
//    Time-stamp: <2019-05-06 22:08:30 makino>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <timerlib.h>
#ifndef NOBLAS
#ifdef MKL
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#endif
#ifdef USEGDR
#include "gdrdgemm.h"
#endif

#define FTYPE double
#include <emmintrin.h>
typedef double v2df __attribute__((vector_size(16)));
typedef union {v2df v; double s[2];}v2u;

#ifndef USEGDR
void  gdrsetboardid(int boardid)
{}
#endif

void matmul2_host(int n,
		  FTYPE a[n][n],
		  FTYPE b[n][n],
		  FTYPE c[n][n])

{
    int i, j, k;
    for(i=0;i<n;i++){
	for(j=0;j<n;j++){
	    c[i][j]=0.0e0;
	    for(k=0;k<n;k++) c[i][j]+= a[i][k]*b[k][j];
	}
    }
}

// simplest version
void matmul_for_small_nk_0(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{
    // simplest version
    int i,j,k;
    for(j=0;j<n;j++)
	for(i=0;i<m;i++)
	    for(k=0;k<kk;k++)
		c[i][j] -= a[i][k]*b[k][j];
}

// make copy of B
void matmul_for_small_nk_1(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    double bcopy[n][kk];
    for(k=0;k<kk;k++)
	for(j=0;j<n;j++)
		bcopy[j][k] = b[k][j];
    for(i=0;i<m;i++){
	for(j=0;j<n;j++){
	    register double tmp=0.0;
	    for(k=0;k<kk;k++){
		tmp += a[i][k]*bcopy[j][k];
	    }
	    c[i][j] -= tmp;
	}
    }
}
// hand-unroll innermost loop
void matmul_for_small_nk_2(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    double bcopy[n][kk];
    for(j=0;j<n;j++)
	    for(k=0;k<kk;k++)
		bcopy[j][k] = b[k][j];
    for(i=0;i<m;i++){
	double *ap=a[i];
	for(j=0;j<n;j++){
	    double *bp = bcopy[j];
	    double tmp=0.0;
	    for(k=0;k<kk;k+=8)
		tmp += ap[k]*bp[k]
		    + ap[k+1]*bp[k+1]
		    + ap[k+2]*bp[k+2]
		    + ap[k+3]*bp[k+3]
		    + ap[k+4]*bp[k+4]
		    + ap[k+5]*bp[k+5]
		    + ap[k+6]*bp[k+6]
		    + ap[k+7]*bp[k+7];
	    c[i][j]-=tmp;
	}
    }
}
// hand-unroll mid-loop
void matmul_for_small_nk_3(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    double bcopy[n][kk];
    for(j=0;j<n;j++)
	    for(k=0;k<kk;k++)
		bcopy[j][k] = b[k][j];
    for(i=0;i<m;i++){
	double *ap=a[i];
	for(j=0;j<n;j+=4){
	    double *bp = bcopy[j];
	    double *bpp = bcopy[j+1];
	    double *bp2 = bcopy[j+2];
	    double *bp3 = bcopy[j+3];
	    double tmp=0.0;
	    double tmp1=0.0;
	    double tmp2=0.0;
	    double tmp3=0.0;
	    for(k=0;k<kk;k+=8){
		tmp += ap[k]*bp[k]
		    + ap[k+1]*bp[k+1]
		    + ap[k+2]*bp[k+2]
		    + ap[k+3]*bp[k+3]
		    + ap[k+4]*bp[k+4]
		    + ap[k+5]*bp[k+5]
		    + ap[k+6]*bp[k+6]
		    + ap[k+7]*bp[k+7];
		tmp1 += ap[k]*bpp[k]
		    + ap[k+1]*bpp[k+1]
		    + ap[k+2]*bpp[k+2]
		    + ap[k+3]*bpp[k+3]
		    + ap[k+4]*bpp[k+4]
		    + ap[k+5]*bpp[k+5]
		    + ap[k+6]*bpp[k+6]
		    + ap[k+7]*bpp[k+7];
		tmp2 += ap[k]*bp2[k]
		    + ap[k+1]*bp2[k+1]
		    + ap[k+2]*bp2[k+2]
		    + ap[k+3]*bp2[k+3]
		    + ap[k+4]*bp2[k+4]
		    + ap[k+5]*bp2[k+5]
		    + ap[k+6]*bp2[k+6]
		    + ap[k+7]*bp2[k+7];
		tmp3 += ap[k]*bp3[k]
		    + ap[k+1]*bp3[k+1]
		    + ap[k+2]*bp3[k+2]
		    + ap[k+3]*bp3[k+3]
		    + ap[k+4]*bp3[k+4]
		    + ap[k+5]*bp3[k+5]
		    + ap[k+6]*bp3[k+6]
		    + ap[k+7]*bp3[k+7];
	    }
	    c[i][j]-=tmp;
	    c[i][j+1]-=tmp1;
	    c[i][j+2]-=tmp2;
	    c[i][j+3]-=tmp3;
	}
    }
}
// hand-unroll mid-loop by 2
void matmul_for_small_nk_4(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    double bcopy[n][kk];
    for(j=0;j<n;j++)
	    for(k=0;k<kk;k++)
		bcopy[j][k] = b[k][j];
    for(i=0;i<m;i++){
	double *ap=a[i];
	for(j=0;j<n;j+=2){
	    double *bp = bcopy[j];
	    double *bpp = bcopy[j+1];
	    double tmp=0.0;
	    double tmp1=0.0;
	    for(k=0;k<kk;k+=8){
		tmp += ap[k]*bp[k]
		    + ap[k+1]*bp[k+1]
		    + ap[k+2]*bp[k+2]
		    + ap[k+3]*bp[k+3]
		    + ap[k+4]*bp[k+4]
		    + ap[k+5]*bp[k+5]
		    + ap[k+6]*bp[k+6]
		    + ap[k+7]*bp[k+7];
		tmp1 += ap[k]*bpp[k]
		    + ap[k+1]*bpp[k+1]
		    + ap[k+2]*bpp[k+2]
		    + ap[k+3]*bpp[k+3]
		    + ap[k+4]*bpp[k+4]
		    + ap[k+5]*bpp[k+5]
		    + ap[k+6]*bpp[k+6]
		    + ap[k+7]*bpp[k+7];
	    }
	    c[i][j]-=tmp;
	    c[i][j+1]-=tmp1;
	}
    }
}
// use sse2 for dot product
void matmul_for_small_nk_5(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    int nh = n/2;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    double acopyd[kk];
    for(j=0;j<nh;j++)
	    for(k=0;k<kk;k++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	double *ap=a[i];
	double *acp = (double*) acopy;
	register v2df tmp= (v2df){0.0,0.0};
	v2df * cp = (v2df*) (&(c[i][0]));
	for(k=0;k<kk;k+=4){
	     __builtin_prefetch((double*)a[i+4]+k,0);
	}
	for(j=0;j<n;j+=4){
	     __builtin_prefetch(c[i+4]+j,0);
	}

	for(k=0;k<kk;k+=2){
	    //	    v2df aa = *((v2df*)(ap+k));
	    //	    acopy[k]=__builtin_ia32_shufpd(aa,aa,0x0);
	    //	    acopy[k+1]= __builtin_ia32_shufpd(aa,aa,0x5);
	    acp[k*2]=acp[k*2+1]=ap[k];
	    acp[k*2+2]=acp[k*2+3]=ap[k+1];
	}
	for(j=0;j<nh;j++){
	    tmp = (v2df){0.0,0.0};
	    v2df * bp = bcopy2[j];
	    for(k=0;k<kk;k+=2){
		tmp += acopy[k]*bp[k]
		    +acopy[k+1]*bp[k+1]
#if 0
		    +acopy[k+2]*bp[k+2]
		    +acopy[k+3]*bp[k+3]
		    +acopy[k+4]*bp[k+4]
		    +acopy[k+5]*bp[k+5]
		    +acopy[k+6]*bp[k+6]
		    +acopy[k+7]*bp[k+7]
#endif
		    ;

	    }
	    cp[j] -= tmp;
	}
    }
}
// use sse2 for dot product
void matmul_for_small_nk_6(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j,k;
    int nh = n/2;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    v2df acopy3[kk];
    v2df acopy4[kk];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    double *acp3 = (double*) acopy3;
    double *acp4 = (double*) acopy4;
    for(j=0;j<nh;j++)
	    for(k=0;k<kk;k++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i+=4){
	double *ap=a[i];
	double *ap2=a[i+1];
	double *ap3=a[i+2];
	double *ap4=a[i+3];
	register v2df tmp, tmp2, tmp3, tmp4;
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df * cp2 = (v2df*) (&(c[i+1][0]));
	v2df * cp3 = (v2df*) (&(c[i+2][0]));
	v2df * cp4 = (v2df*) (&(c[i+3][0]));
	for(k=0;k<kk;k+=4){
	     __builtin_prefetch((double*)a[i+4]+k,0);
	     __builtin_prefetch((double*)a[i+5]+k,0);
	     __builtin_prefetch((double*)a[i+6]+k,0);
	     __builtin_prefetch((double*)a[i+7]+k,0);
	     __builtin_prefetch(c[i+4]+j,0);
	     __builtin_prefetch(c[i+5]+j,0);
	     __builtin_prefetch(c[i+6]+j,0);
	     __builtin_prefetch(c[i+7]+j,0);
	}

	for(k=0;k<kk;k+=2){
	    v2df * aa = (v2df*)(ap+k);
	    acopy [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    aa = (v2df*)(ap2+k);
	    acopy2 [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy2[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    //   acp[k*2]=acp[k*2+1]=ap[k];
	    //	    acp[k*2+2]=acp[k*2+3]=ap[k+1];
	    //	    acp2[k*2]=acp2[k*2+1]=ap2[k];
	    //	    acp2[k*2+2]=acp2[k*2+3]=ap2[k+1];
	}
	for(k=0;k<kk;k+=2){
	    acp3[k*2]=acp3[k*2+1]=ap3[k];
	    acp3[k*2+2]=acp3[k*2+3]=ap3[k+1];
	    acp4[k*2]=acp4[k*2+1]=ap4[k];
	    acp4[k*2+2]=acp4[k*2+3]=ap4[k+1];
	}
	for(j=0;j<nh;j++){
	    tmp = tmp2= tmp3= tmp4= (v2df){0.0,0.0};
	    v2df * bp = bcopy2[j];
#if 0
	    for(k=0;k<kk;k+=4){
		tmp += acopy[k]*bp[k]
		    +acopy[k+1]*bp[k+1]
		    +acopy[k+2]*bp[k+2]
		    +acopy[k+3]*bp[k+3];
		tmp2 += acopy2[k]*bp[k]
		    +acopy2[k+1]*bp[k+1]
		    +acopy2[k+2]*bp[k+2]
		    +acopy2[k+3]*bp[k+3];
		tmp3 += acopy3[k]*bp[k]
		    +acopy3[k+1]*bp[k+1]
		    +acopy3[k+2]*bp[k+2]
		    +acopy3[k+3]*bp[k+3];
		tmp4 += acopy4[k]*bp[k]
		    +acopy4[k+1]*bp[k+1]
		    +acopy4[k+2]*bp[k+2]
		    +acopy4[k+3]*bp[k+3];

	    }
#endif
	    for(k=0;k<kk;k+=2){
		tmp += acopy[k]*bp[k]
		    +acopy[k+1]*bp[k+1];
		tmp2 += acopy2[k]*bp[k]
		    +acopy2[k+1]*bp[k+1];
		tmp3 += acopy3[k]*bp[k]
		    +acopy3[k+1]*bp[k+1];
		tmp4 += acopy4[k]*bp[k]
		    +acopy4[k+1]*bp[k+1];

	    }
	    cp[j] -= tmp;
	    cp2[j] -= tmp2;
	    cp3[j] -= tmp3;
	    cp4[j] -= tmp4;
	}
    }
}
void matmul_for_small_nk7(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    //    BEGIN_TSC;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    //    END_TSC(bpcount);
    for(i=0;i<m;i+=2){
	//	BEGIN_TSC;
	double *ap=a[i];
	double *ap2=a[i+1];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df * cp2 = (v2df*) (&(c[i+1][0]));

	for(k=0;k<kk;k+=2){
	    v2df * aa = (v2df*)(ap+k);
	     __builtin_prefetch((double*)a[i+4]+k,0);
	     __builtin_prefetch((double*)a[i+5]+k,0);
	    acopy [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    aa = (v2df*)(ap2+k);
	    acopy2 [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy2[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    //   acp[k*2]=acp[k*2+1]=ap[k];
	    //	    acp[k*2+2]=acp[k*2+3]=ap[k+1];
	    //	    acp2[k*2]=acp2[k*2+1]=ap2[k];
	    //	    acp2[k*2+2]=acp2[k*2+3]=ap2[k+1];
	}
	//	END_TSC(apcount);
	//	BEGIN_TSC;
	for(j=0;j<nh;j++){
	    tmp = tmp2=  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];
	    v2df ctmp2 = cp2[j] ;

	    v2df * bp = bcopy2[j];
	    __builtin_prefetch(c[i+4]+j,0);
	    __builtin_prefetch(c[i+5]+j,0);
	    for(k=0;k<kk;k+=8){
		int k2 = k+4;
		v2df *avp = acopy+k;
		v2df *avp2 = acopy2+k;
		v2df *bvp = bp+k;
		tmp += avp[0]*bvp[0];
		tmp2 += avp2[0]*bvp[0];
		tmp +=avp[1]*bvp[1];
		tmp2+=avp2[1]*bvp[1];
		tmp +=avp[2]*bvp[2];
		tmp2+=avp2[2]*bvp[2];
		tmp +=avp[3]*bvp[3];
		tmp2+=avp2[3]*bvp[3];
		tmp += avp[4]*bvp[4];
		tmp2 += avp2[4]*bvp[4];
		tmp +=avp[5]*bvp[5];
		tmp2+=avp2[5]*bvp[5];
		tmp +=avp[6]*bvp[6];
		tmp2+=avp2[6]*bvp[6];
		tmp +=avp[7]*bvp[7];
		tmp2+=avp2[7]*bvp[7];
	    }

#if 0
	    for(k=0;k<kk;k+=8){
		int k2 = k+4;
		tmp += acopy[k]*bp[k];
		tmp2 += acopy2[k]*bp[k];
		tmp +=acopy[k+1]*bp[k+1];
		tmp2+=acopy2[k+1]*bp[k+1];
		tmp +=acopy[k+2]*bp[k+2];
		tmp2+=acopy2[k+2]*bp[k+2];
		tmp +=acopy[k+3]*bp[k+3];
		tmp2+=acopy2[k+3]*bp[k+3];
		tmp += acopy[k2]*bp[k2];
		tmp2 += acopy2[k2]*bp[k2];
		tmp +=acopy[k2+1]*bp[k2+1];
		tmp2+=acopy2[k2+1]*bp[k2+1];
		tmp +=acopy[k2+2]*bp[k2+2];
		tmp2+=acopy2[k2+2]*bp[k2+2];
		tmp +=acopy[k2+3]*bp[k2+3];
		tmp2+=acopy2[k2+3]*bp[k2+3];
	    }
#endif
#if 0
	    for(k=0;k<kk;k+=2){
		tmp += acopy[k]*bp[k];
		tmp 	    __builtin_prefetch(c[i+4+(j&1)]+j,0);
+=acopy[k+1]*bp[k+1];
		tmp2 += acopy2[k]*bp[k];
		tmp2+=acopy2[k+1]*bp[k+1];
	    }
#endif
	    cp[j] = ctmp -tmp;
	    cp2[j] = ctmp2 -tmp2;

	}
	//	END_TSC(dotcount);

    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);

}

// XMM registers
#define X0	"%xmm0"
#define X1	"%xmm1"
#define X2	"%xmm2"
#define X3	"%xmm3"
#define X4	"%xmm4"
#define X5	"%xmm5"
#define X6	"%xmm6"
#define X7	"%xmm7"
#define X8		"%xmm8"
#define X9		"%xmm9"
#define X10		"%xmm10"
#define X11		"%xmm11"
#define X12		"%xmm12"
#define X13		"%xmm13"
#define X14		"%xmm14"
#define X15		"%xmm15"

#define LOADPD(mem, reg) asm("movapd %0, %"reg::"m"(mem));
#define STORPD(reg, mem) asm("movapd %"reg " , %0"::"m"(mem));
#define MOVNTPD(reg, mem) asm("movntpd %"reg " , %0"::"m"(mem));
#define MOVAPD(src, dst) asm("movapd " src "," dst);
#define MOVQ(src, dst) asm("movq " src "," dst);
#define BCAST0(reg) asm("shufpd $0x00, " reg ","  reg);
#define BCAST1(reg) asm("shufpd $0xff, " reg ","  reg);
#define MULPD(src, dst) asm("mulpd " src "," dst);
#define ADDPD(src, dst) asm("addpd " src ","  dst);
#define SUBPD(src, dst) asm("subpd "  src "," dst);

void matmul_for_nk8_0(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 8;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
#define PREFETCHL 32
    for(i=0;i<PREFETCHL;i++){
	__builtin_prefetch((double*)a[i],0,0);
	__builtin_prefetch(c[i+8],1,0);
    }

    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	//	v2df acopy[8];
	v2df *ap = (v2df*) a[i];
	v2df * cp = (v2df*) (&(c[i][0]));

	__builtin_prefetch((double*)a[i+PREFETCHL],0,0);

	int k;

	for(j=0;j<nh;j+=4){
	    __builtin_prefetch(c[i+PREFETCHL]+j,1,3);
	    v2df * bvp0 = bcopy2[j];
	    v2df * bvp1 = bcopy2[j+1];
	    v2df * bvp2 = bcopy2[j+2];
	    v2df * bvp3 = bcopy2[j+3];
	    LOADPD(cp[j],X12);
	    LOADPD(cp[j+1],X13);
	    LOADPD(cp[j+2],X14);
	    LOADPD(cp[j+3],X15);

	    LOADPD(ap[0],X0);
	    LOADPD(bvp0[0],X4);
	    LOADPD(bvp1[0],X5);
	    LOADPD(bvp2[0],X6);
	    LOADPD(bvp3[0],X7);
	    LOADPD(bvp0[1],X8);
	    LOADPD(bvp1[1],X9);
	    LOADPD(bvp2[1],X10);
	    LOADPD(bvp3[1],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);
	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[1],X0);

	    LOADPD(bvp0[2],X4);
	    LOADPD(bvp1[2],X5);
	    LOADPD(bvp2[2],X6);
	    LOADPD(bvp3[2],X7);
	    LOADPD(bvp0[3],X8);
	    LOADPD(bvp1[3],X9);
	    LOADPD(bvp2[3],X10);
	    LOADPD(bvp3[3],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);
	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[2],X0);
	    LOADPD(bvp0[4],X4);
	    LOADPD(bvp1[4],X5);
	    LOADPD(bvp2[4],X6);
	    LOADPD(bvp3[4],X7);
	    LOADPD(bvp0[5],X8);
	    LOADPD(bvp1[5],X9);
	    LOADPD(bvp2[5],X10);
	    LOADPD(bvp3[5],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);
	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[3],X0);

	    LOADPD(bvp0[6],X4);
	    LOADPD(bvp1[6],X5);
	    LOADPD(bvp2[6],X6);
	    LOADPD(bvp3[6],X7);
	    LOADPD(bvp0[7],X8);
	    LOADPD(bvp1[7],X9);
	    LOADPD(bvp2[7],X10);
	    LOADPD(bvp3[7],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);
	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);
	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);
	    STORPD(X12,cp[j+0]);
	    STORPD(X13,cp[j+1]);
	    STORPD(X14,cp[j+2]);
	    STORPD(X15,cp[j+3]);
	}
    }
}

void matmul_for_nk16_0a(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 16;
    int nh = n/2;
    register int k;
    v2df bcopy2[nh][kk];
#ifdef     PREFETCHL
#undef PREFETCHL
#endif
#define PREFETCHL 32

    for(i=0;i<PREFETCHL;i++){
	__builtin_prefetch((double*)a[i],0,0);
	__builtin_prefetch((double*)a[i]+8,0,0);
	__builtin_prefetch(c[i+8],1,0);
	__builtin_prefetch(c[i+8]+8,1,0);
    }
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i+=2){
	//	BEGIN_TSC;
	v2df *ap = (v2df*) a[i];
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df *app = (v2df*) a[i+1];
	v2df * cpp = (v2df*) (&(c[i+1][0]));

	__builtin_prefetch((double*)a[i+PREFETCHL],0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+8,0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL+1],0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL+1]+8,0,0);

	int k;

	for(j=0;j<nh;j+=2){
	    __builtin_prefetch(c[i+PREFETCHL]+j,1,0);
	    v2df * bvp0 = bcopy2[j];
	    v2df * bvp1 = bcopy2[j+1];
	    LOADPD(cp[j],X12);
	    LOADPD(cp[j+1],X13);
	    LOADPD(cpp[j],X14);
	    LOADPD(cpp[j+1],X15);

	    for(k=0;k<8;k++){

		LOADPD(ap[k],X0);
		LOADPD(app[k],X2);
		MOVAPD(X0,X1);
		BCAST0(X0);
		BCAST1(X1);
		MOVAPD(X2,X3);
		BCAST0(X2);
		BCAST1(X3);
		LOADPD(bvp0[k*2],X4);
		MOVAPD(X4,X6);
		MULPD(X0,X4);
		SUBPD(X4,X12);
		LOADPD(bvp1[k*2],X5);
		MOVAPD(X5,X7);
		MULPD(X0,X5);
		SUBPD(X5,X13);
		LOADPD(bvp0[k*2+1],X8);
		MOVAPD(X8,X10);
		MULPD(X1,X8);
		SUBPD(X8,X12);
		LOADPD(bvp1[k*2+1],X9);
		MOVAPD(X9,X11);
		MULPD(X1,X9);
		SUBPD(X9,X13);
		MULPD(X2,X6);
		SUBPD(X6,X14);
		MULPD(X2,X7);
		SUBPD(X7,X15);
		MULPD(X3,X10);
		SUBPD(X10,X14);
		MULPD(X3,X11);
		SUBPD(X11,X15);
	    }

	    STORPD(X12,cp[j+0]);
	    STORPD(X13,cp[j+1]);
	    STORPD(X14,cpp[j+0]);
	    STORPD(X15,cpp[j+1]);
	}
    }
}
void matmul_for_nk16_0c(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 16;
    int nh = n/2;
    register int k;
    v2df bcopy2[nh][kk];
#ifdef     PREFETCHL
#undef PREFETCHL
#endif
#define PREFETCHL 16

    for(i=0;i<PREFETCHL;i++){
	__builtin_prefetch((double*)a[i],0,0);
	__builtin_prefetch((double*)a[i]+8,0,0);
	__builtin_prefetch(c[i+8],1,0);
	__builtin_prefetch(c[i+8]+8,1,0);
    }
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	v2df *ap = (v2df*) a[i];
	v2df * cp = (v2df*) (&(c[i][0]));

	__builtin_prefetch((double*)a[i+PREFETCHL],0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+8,0,0);

	int k;

	for(j=0;j<nh;j+=4){
	    __builtin_prefetch(c[i+PREFETCHL]+j,1,0);
	    v2df * bvp0 = bcopy2[j];
	    v2df * bvp1 = bcopy2[j+1];
	    v2df * bvp2 = bcopy2[j+2];
	    v2df * bvp3 = bcopy2[j+3];
	    LOADPD(cp[j],X12);
	    LOADPD(cp[j+1],X13);
	    LOADPD(cp[j+2],X14);
	    LOADPD(cp[j+3],X15);

	    for(k=0;k<8;k++){

		LOADPD(ap[k],X0);
		LOADPD(bvp0[k*2],X4);
		LOADPD(bvp1[k*2],X5);
		LOADPD(bvp2[k*2],X6);
		LOADPD(bvp3[k*2],X7);
		MOVAPD(X0,X1);
		BCAST0(X0);
		BCAST1(X1);
		LOADPD(bvp0[k*2+1],X8);
		LOADPD(bvp1[k*2+1],X9);
		LOADPD(bvp2[k*2+1],X10);
		LOADPD(bvp3[k*2+1],X11);
		MULPD(X0,X4);
		MULPD(X0,X5);
		MULPD(X0,X6);
		MULPD(X0,X7);
		MULPD(X1,X8);
		MULPD(X1,X9);
		MULPD(X1,X10);
		MULPD(X1,X11);
		SUBPD(X4,X12);
		SUBPD(X5,X13);
		SUBPD(X6,X14);
		SUBPD(X7,X15);
		SUBPD(X8,X12);
		SUBPD(X9,X13);
		SUBPD(X10,X14);
		SUBPD(X11,X15);
	    }

	    STORPD(X12,cp[j+0]);
	    STORPD(X13,cp[j+1]);
	    STORPD(X14,cp[j+2]);
	    STORPD(X15,cp[j+3]);
	}
    }
}
void matmul_for_nk32_0(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 32;
    int nh = n/2;
    register int k;
    v2df bcopy2[nh][kk];
#ifdef     PREFETCHL
#undef PREFETCHL
#endif
#define PREFETCHL 8

    for(i=0;i<PREFETCHL;i++){
	__builtin_prefetch((double*)a[i],0,0);
	__builtin_prefetch((double*)a[i]+8,0,0);
	__builtin_prefetch((double*)a[i]+16,0,0);
	__builtin_prefetch((double*)a[i]+24,0,0);
	__builtin_prefetch(c[i+8],1,0);
	__builtin_prefetch(c[i+8]+8,1,0);
	__builtin_prefetch(c[i+8]+16,1,0);
	__builtin_prefetch(c[i+8]+24,1,0);
    }
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	v2df *ap = (v2df*) a[i];
	v2df * cp = (v2df*) (&(c[i][0]));

	__builtin_prefetch((double*)a[i+PREFETCHL],0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+8,0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+16,0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+24,0,0);

	int k;

	for(j=0;j<nh;j+=4){
	    __builtin_prefetch(c[i+PREFETCHL]+j,1,0);
	    v2df * bvp0 = bcopy2[j];
	    v2df * bvp1 = bcopy2[j+1];
	    v2df * bvp2 = bcopy2[j+2];
	    v2df * bvp3 = bcopy2[j+3];
	    LOADPD(cp[j],X12);
	    LOADPD(cp[j+1],X13);
	    LOADPD(cp[j+2],X14);
	    LOADPD(cp[j+3],X15);

	    for(k=0;k<16;k++){

		LOADPD(ap[k],X0);
		LOADPD(bvp0[k*2],X4);
		LOADPD(bvp1[k*2],X5);
		LOADPD(bvp2[k*2],X6);
		LOADPD(bvp3[k*2],X7);
		MOVAPD(X0,X1);
		BCAST0(X0);
		BCAST1(X1);
		MULPD(X0,X4);
		MULPD(X0,X5);
		MULPD(X0,X6);
		MULPD(X0,X7);
		LOADPD(bvp0[k*2+1],X8);
		LOADPD(bvp1[k*2+1],X9);
		LOADPD(bvp2[k*2+1],X10);
		LOADPD(bvp3[k*2+1],X11);
		MULPD(X1,X8);
		MULPD(X1,X9);
		MULPD(X1,X10);
		MULPD(X1,X11);
		SUBPD(X4,X12);
		SUBPD(X5,X13);
		SUBPD(X6,X14);
		SUBPD(X7,X15);
		SUBPD(X8,X12);
		SUBPD(X9,X13);
		SUBPD(X10,X14);
		SUBPD(X11,X15);
	    }

	    STORPD(X12,cp[j+0]);
	    STORPD(X13,cp[j+1]);
	    STORPD(X14,cp[j+2]);
	    STORPD(X15,cp[j+3]);
	}
    }
}

void matmul_for_nk16_0b(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 16;
    int nh = n/2;
    register int k;
    v2df bcopy2[nh][kk];
#ifdef     PREFETCHL
#undef PREFETCHL
#endif
#define PREFETCHL 16

    for(i=0;i<PREFETCHL;i++){
	__builtin_prefetch((double*)a[i],0,0);
	__builtin_prefetch((double*)a[i]+8,0,0);
	__builtin_prefetch(c[i+8],1,0);
	__builtin_prefetch(c[i+8]+8,1,0);
    }
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	v2df *ap = (v2df*) a[i];
	v2df * cp = (v2df*) (&(c[i][0]));

	__builtin_prefetch((double*)a[i+PREFETCHL],0,0);
	__builtin_prefetch((double*)a[i+PREFETCHL]+8,0,0);

	int k;

	for(j=0;j<nh;j+=4){
	    __builtin_prefetch(c[i+PREFETCHL]+j,1,0);
	    v2df * bvp0 = bcopy2[j];
	    v2df * bvp1 = bcopy2[j+1];
	    v2df * bvp2 = bcopy2[j+2];
	    v2df * bvp3 = bcopy2[j+3];
	    LOADPD(cp[j],X12);
	    LOADPD(cp[j+1],X13);
	    LOADPD(cp[j+2],X14);
	    LOADPD(cp[j+3],X15);

	    LOADPD(ap[0],X0);
	    LOADPD(bvp0[0],X4);
	    LOADPD(bvp1[0],X5);
	    LOADPD(bvp2[0],X6);
	    LOADPD(bvp3[0],X7);
	    LOADPD(bvp0[1],X8);
	    LOADPD(bvp1[1],X9);
	    LOADPD(bvp2[1],X10);
	    LOADPD(bvp3[1],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[1],X0);

	    LOADPD(bvp0[2],X4);
	    LOADPD(bvp1[2],X5);
	    LOADPD(bvp2[2],X6);
	    LOADPD(bvp3[2],X7);
	    LOADPD(bvp0[3],X8);
	    LOADPD(bvp1[3],X9);
	    LOADPD(bvp2[3],X10);
	    LOADPD(bvp3[3],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[2],X0);
	    LOADPD(bvp0[4],X4);
	    LOADPD(bvp1[4],X5);
	    LOADPD(bvp2[4],X6);
	    LOADPD(bvp3[4],X7);
	    LOADPD(bvp0[5],X8);
	    LOADPD(bvp1[5],X9);
	    LOADPD(bvp2[5],X10);
	    LOADPD(bvp3[5],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[3],X0);
	    LOADPD(bvp0[6],X4);
	    LOADPD(bvp1[6],X5);
	    LOADPD(bvp2[6],X6);
	    LOADPD(bvp3[6],X7);
	    LOADPD(bvp0[7],X8);
	    LOADPD(bvp1[7],X9);
	    LOADPD(bvp2[7],X10);
	    LOADPD(bvp3[7],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[4],X0);

	    LOADPD(bvp0[8],X4);
	    LOADPD(bvp1[8],X5);
	    LOADPD(bvp2[8],X6);
	    LOADPD(bvp3[8],X7);
	    LOADPD(bvp0[9],X8);
	    LOADPD(bvp1[9],X9);
	    LOADPD(bvp2[9],X10);
	    LOADPD(bvp3[9],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);
	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[5],X0);
	    LOADPD(bvp0[10],X4);
	    LOADPD(bvp1[10],X5);
	    LOADPD(bvp2[10],X6);
	    LOADPD(bvp3[10],X7);
	    LOADPD(bvp0[11],X8);
	    LOADPD(bvp1[11],X9);
	    LOADPD(bvp2[11],X10);
	    LOADPD(bvp3[11],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[6],X0);
	    LOADPD(bvp0[12],X4);
	    LOADPD(bvp1[12],X5);
	    LOADPD(bvp2[12],X6);
	    LOADPD(bvp3[12],X7);
	    LOADPD(bvp0[13],X8);
	    LOADPD(bvp1[13],X9);
	    LOADPD(bvp2[13],X10);
	    LOADPD(bvp3[13],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    LOADPD(ap[7],X0);
	    LOADPD(bvp0[14],X4);
	    LOADPD(bvp1[14],X5);
	    LOADPD(bvp2[14],X6);
	    LOADPD(bvp3[14],X7);
	    LOADPD(bvp0[15],X8);
	    LOADPD(bvp1[15],X9);
	    LOADPD(bvp2[15],X10);
	    LOADPD(bvp3[15],X11);
	    MOVAPD(X0,X1);
	    BCAST0(X0);
	    BCAST1(X1);

	    MULPD(X0,X4);
	    MULPD(X0,X5);
	    MULPD(X0,X6);
	    MULPD(X0,X7);
	    MULPD(X1,X8);
	    MULPD(X1,X9);
	    MULPD(X1,X10);
	    MULPD(X1,X11);
	    SUBPD(X4,X12);
	    SUBPD(X5,X13);
	    SUBPD(X6,X14);
	    SUBPD(X7,X15);

	    SUBPD(X8,X12);
	    SUBPD(X9,X13);
	    SUBPD(X10,X14);
	    SUBPD(X11,X15);

	    STORPD(X12,cp[j+0]);
	    STORPD(X13,cp[j+1]);
	    STORPD(X14,cp[j+2]);
	    STORPD(X15,cp[j+3]);
	}
    }
}

void matmul_for_nk8_0d(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 8;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    v2df awork[4];
    v2df awork2[4];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	double *ap=a[i];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));

	v2df * aa = (v2df*)(ap);
	__builtin_prefetch((double*)a[i+8],0,0);

	v2df acopy0=(v2df){a[i][0], a[i][0]};
	v2df acopy1=(v2df){a[i][1], a[i][1]};
	v2df acopy2=(v2df){a[i][2], a[i][2]};
	v2df acopy3=(v2df){a[i][3], a[i][3]};
	v2df acopy4=(v2df){a[i][4], a[i][4]};
	v2df acopy5=(v2df){a[i][5], a[i][5]};
	v2df acopy6=(v2df){a[i][6], a[i][6]};
	v2df acopy7=(v2df){a[i][7], a[i][7]};
	v2df zero=(v2df){0.0, 0.0};

	LOADPD(acopy0,X0);
	LOADPD(acopy1,X1);
	LOADPD(acopy2,X2);
	LOADPD(acopy3,X3);
	LOADPD(acopy4,X4);
	LOADPD(acopy5,X5);
	LOADPD(acopy6,X6);
	LOADPD(acopy7,X7);

	for(j=0;j<nh;j++){
	    __builtin_prefetch(c[i+8]+j,1,0);
	    v2df * bvp = bcopy2[j];
	    LOADPD(cp[j],X14);
	    LOADPD(bvp[0],X8);
	    LOADPD(bvp[1],X9);
	    MULPD(X0,X8);
	    MULPD(X1,X9);
	    LOADPD(bvp[2],X10);
	    LOADPD(bvp[3],X11);
	    ADDPD(X9,X8);
	    MULPD(X2,X10);
	    MULPD(X3,X11);
	    ADDPD(X11,X10);
	    LOADPD(bvp[4],X9);
	    LOADPD(bvp[5],X11);
	    LOADPD(bvp[6],X12);
	    LOADPD(bvp[7],X13);
	    MULPD(X4,X9);
	    MULPD(X5,X11);
	    ADDPD(X10,X8);
	    ADDPD(X11,X9);
	    MULPD(X6,X12);
	    MULPD(X7,X13);
	    ADDPD(X13,X12);
	    ADDPD(X9,X8);
	    ADDPD(X12,X8);
	    SUBPD(X8,X14);
	    STORPD(X14,cp[j]);
	}
    }
}

void matmul_for_nk8_0c(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 8;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    v2df awork[4];
    v2df awork2[4];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	double *ap=a[i];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));

	v2df * aa = (v2df*)(ap);
	__builtin_prefetch((double*)a[i+8],0,0);

	register v2df acopy0=(v2df){a[i][0], a[i][0]};
	register v2df acopy1=(v2df){a[i][1], a[i][1]};
	register v2df acopy2=(v2df){a[i][2], a[i][2]};
	register v2df acopy3=(v2df){a[i][3], a[i][3]};
	register v2df acopy4=(v2df){a[i][4], a[i][4]};
	register v2df acopy5=(v2df){a[i][5], a[i][5]};
	register v2df acopy6=(v2df){a[i][6], a[i][6]};
	register v2df acopy7=(v2df){a[i][7], a[i][7]};

	for(j=0;j<nh;j++){
	    tmp =  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];

	    v2df * bp = bcopy2[j];
	    __builtin_prefetch(c[i+4]+j,1,0);
	    v2df *bvp = bp;
	    tmp += acopy0*bvp[0];
	    tmp +=acopy1*bvp[1];
	    tmp +=acopy2*bvp[2];
	    tmp +=acopy3*bvp[3];
	    tmp +=acopy4*bvp[4];
	    tmp +=acopy5*bvp[5];
	    tmp +=acopy6*bvp[6];
	    tmp +=acopy7*bvp[7];
	    cp[j] = ctmp -tmp;
	}
    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);

}
void matmul_for_nk8_0b(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 8;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    v2df awork[4];
    v2df awork2[4];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i++){
	//	BEGIN_TSC;
	double *ap=a[i];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));

	v2df * aa = (v2df*)(ap);
	__builtin_prefetch((double*)a[i+8],0,0);

	acopy[0]=(v2df){a[i][0], a[i][0]};
	acopy[1]=(v2df){a[i][1], a[i][1]};
	acopy[2]=(v2df){a[i][2], a[i][2]};
	acopy[3]=(v2df){a[i][3], a[i][3]};
	acopy[4]=(v2df){a[i][4], a[i][4]};
	acopy[5]=(v2df){a[i][5], a[i][5]};
	acopy[6]=(v2df){a[i][6], a[i][6]};
	acopy[7]=(v2df){a[i][7], a[i][7]};

	for(j=0;j<nh;j++){
	    tmp = tmp2=  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];

	    v2df * bp = bcopy2[j];
	    __builtin_prefetch(c[i+4]+j,1,0);
	    v2df *avp = acopy;
	    v2df *bvp = bp;
	    tmp += avp[0]*bvp[0];
	    tmp +=avp[1]*bvp[1];
	    tmp +=avp[2]*bvp[2];
	    tmp +=avp[3]*bvp[3];
	    tmp += avp[4]*bvp[4];
	    tmp +=avp[5]*bvp[5];
	    tmp +=avp[6]*bvp[6];
	    tmp +=avp[7]*bvp[7];
	    cp[j] = ctmp -tmp;
	}
    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);

}
void matmul_for_nk8_0a(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j;
    int kk = 8;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    v2df awork[4];
    v2df awork2[4];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    for(i=0;i<m;i+=2){
	//	BEGIN_TSC;
	double *ap=a[i];
	double *ap2=a[i+1];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df * cp2 = (v2df*) (&(c[i+1][0]));

	v2df * aa = (v2df*)(ap);
	__builtin_prefetch((double*)a[i+8],0,0);
	__builtin_prefetch((double*)a[i+9],0,0);

	acopy[0]=(v2df){a[i][0], a[i][0]};
	acopy[1]=(v2df){a[i][1], a[i][1]};
	acopy[2]=(v2df){a[i][2], a[i][2]};
	acopy[3]=(v2df){a[i][3], a[i][3]};
	acopy[4]=(v2df){a[i][4], a[i][4]};
	acopy[5]=(v2df){a[i][5], a[i][5]};
	acopy[6]=(v2df){a[i][6], a[i][6]};
	acopy[7]=(v2df){a[i][7], a[i][7]};

	aa = (v2df*)(ap2);
	acopy2[0]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	acopy2[1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	aa++;
	acopy2[2]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	acopy2[3]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	aa++;
	acopy2[4]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	acopy2[5]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	aa++;
	acopy2[6]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	acopy2[7]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	aa++;
	for(j=0;j<nh;j++){
	    tmp = tmp2=  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];
	    v2df ctmp2 = cp2[j] ;

	    v2df * bp = bcopy2[j];
	    __builtin_prefetch(c[i+4]+j,1,0);
	    __builtin_prefetch(c[i+5]+j,1,0);
	    v2df *avp = acopy;
	    v2df *avp2 = acopy2;
	    v2df *bvp = bp;
	    tmp += avp[0]*bvp[0];
	    tmp2 += avp2[0]*bvp[0];
	    tmp +=avp[1]*bvp[1];
	    tmp2+=avp2[1]*bvp[1];
	    tmp +=avp[2]*bvp[2];
	    tmp2+=avp2[2]*bvp[2];
	    tmp +=avp[3]*bvp[3];
	    tmp2+=avp2[3]*bvp[3];
	    tmp += avp[4]*bvp[4];
	    tmp2 += avp2[4]*bvp[4];
	    tmp +=avp[5]*bvp[5];
	    tmp2+=avp2[5]*bvp[5];
	    tmp +=avp[6]*bvp[6];
	    tmp2+=avp2[6]*bvp[6];
	    tmp +=avp[7]*bvp[7];
	    tmp2+=avp2[7]*bvp[7];
	    cp[j] = ctmp -tmp;
	    cp2[j] = ctmp2 -tmp2;

	}

    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);

}
void matmul_for_nk8_1(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j,k;
    const int kk = 8;
    const int kh = kk/2;
    int nh = n/2;
    v2df bcopy[n][kh];
    v2df acopy[kk][kh];
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(j=0;j<n;j++)
	for(k=0;k<kh;k++)
	    bcopy[j][k] = (v2df){b[k*2][j],b[k*2+1][j]};
    //    printf("copy b end\n");
    for(i=0;i<m;i+=kk){
	for(k=0;k<kk;k++){
	    v2df  *ak = (v2df*)(a[i+k]);
	    v2df * awp =acopy+k;
	    awp[0]=ak[0];
	    awp[1]=ak[1];
	    awp[2]=ak[2];
	    awp[3]=ak[3];
	}
	//	printf("copy a end\n");
	for(k=0;k<kk;k++){
	    v2u tmp, tmp1;
	    v2df * ap = acopy[k];
	    for(j=0;j<n;j+=2){
		tmp.v = ap[0]*bcopy[j][0]
		    + ap[1]*bcopy[j][1]
		    + ap[2]*bcopy[j][2]
		    + ap[3]*bcopy[j][3];
		tmp1.v = ap[0]*bcopy[j+1][0]
		    + ap[1]*bcopy[j+1][1]
		    + ap[2]*bcopy[j+1][2]
		    + ap[3]*bcopy[j+1][3];
		c[k+i][j] -= tmp.s[0]+tmp.s[1];
		c[k+i][j+1] -= tmp1.s[0]+tmp1.s[1];
	    }
	}
	//	printf("calc c end\n");
    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);
}
void matmul_for_nk8_2(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j,k;
    const int kk = 8;
    const int kh = kk/2;
    int nh = n/2;
    v2df bcopy[nh][kk];
    v2df acopy[kk][kh];
    v2df ccopy[kk][kh];
    v2df acopy2[kk][kk];
    unsigned long bpcount, apcount, dotcount;
    bpcount= apcount= dotcount=0;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy[j][k] = *((v2df*)(b[k]+j+j));
    //    printf("copy b end\n");
    for(i=0;i<m;i+=kk){
	for(k=0;k<kk;k++){
	    __builtin_prefetch(a+i+k+8,0,0);
	    __builtin_prefetch(c+i+k+8,1,0);
	    v2df  *ak = (v2df*)(a[i+k]);
	    v2df * awp = (v2df*)(acopy+k);
	    v2df  *ck = (v2df*)(c[i+k]);
	    v2df * cwp = (v2df*)(ccopy+k);
	    awp[0]=ak[0];
	    awp[1]=ak[1];
	    awp[2]=ak[2];
	    awp[3]=ak[3];
	    cwp[0]=ck[0];
	    cwp[1]=ck[1];
	    cwp[2]=ck[2];
	    cwp[3]=ck[3];
	}
	for (j=0;j<n;j++){
	    double * ap = (double*)( acopy+j);
	    for (k=0;k<kk;k++){
		acopy2[j][k]=(v2df){ap[k],ap[k]};
	    }
	}
	//	printf("copy a end\n");
	for(k=0;k<kk;k++){
	    v2df * cp = (v2df*) ccopy[k];
	    v2df * ap = acopy2[k];
	    for(j=0;j<nh;j++){
		v2df * bp = bcopy[j];
		cp[j] -= ap[0]*bp[0]
		    + ap[1]*bp[1]
		    + ap[2]*bp[2]
		    + ap[3]*bp[3]
		    + ap[4]*bp[4]
		    + ap[5]*bp[5]
		    + ap[6]*bp[6]
		    + ap[7]*bp[7];
	    }
	}
	for(k=0;k<kk;k++){
	    v2df  *ck = (v2df*)(c[i+k]);
	    v2df * cwp = (v2df*)(ccopy+k);
#if 0
	    ck[0] = cwp[0];
	    ck[1] = cwp[1];
	    ck[2] = cwp[2];
	    ck[3] = cwp[3];
#endif
	    __builtin_ia32_movntpd((double*)(ck),cwp[0]);
	    __builtin_ia32_movntpd((double*)(ck+1),cwp[1]);
	    __builtin_ia32_movntpd((double*)(ck+2),cwp[2]);
	    __builtin_ia32_movntpd((double*)(ck+3),cwp[3]);
	}
	//	printf("calc c end\n");
    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);
}
void matmul_for_nk8(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{

    int i,j,k;
    const int kk = 8;
    const int kh = kk/2;
    int nh = n/2;
    v2df bcopy[nh][kk];
    v2df acopy2[kk][kk];
    //    unsigned long bpcount, apcount, dotcount;
    //    bpcount= apcount= dotcount=0;
    BEGIN_TSC;

    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy[j][k] = *((v2df*)(b[k]+j+j));
    //    END_TSC(bpcount);
    //    printf("copy b end\n");
#pragma omp parallel for private(i,j,k,acopy2)	 schedule(static)
    for(i=0;i<m;i+=kk){
	//	BEGIN_TSC;
	for(k=0;k<kk;k++){
	    __builtin_prefetch(a+i+k+16,0,0);
	    __builtin_prefetch(c+i+k+16,1,0);
	}
	for (j=0;j<n;j++){
	    double * ap = (double*)( a[i+j]);
	    for (k=0;k<kk;k++){
		acopy2[j][k]=(v2df){ap[k],ap[k]};
	    }
	}
	//	END_TSC(apcount);
	//	printf("copy a end\n");
	//	BEGIN_TSC;
	for(k=0;k<kk;k++){
	    v2df * cp = (v2df*) (c[i+k]);
	    v2df * ap = acopy2[k];
	    for(j=0;j<nh;j++){
		v2df * bp = bcopy[j];
		cp[j] -= ap[0]*bp[0] + ap[1]*bp[1]
		    + ap[2]*bp[2]    + ap[3]*bp[3]
		    + ap[4]*bp[4]    + ap[5]*bp[5]
		    + ap[6]*bp[6]    + ap[7]*bp[7];
	    }
	}
	//	printf("calc c end\n");
	//	END_TSC(dotcount);
    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //   (double)bpcount, (double)apcount, (double)dotcount);
    END_TSC(t,10);
}

void matmul_for_nk8_3(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{
    int ii;
    int dm = (m+31)/32;
    dm*= 8;
#pragma omp parallel for private(ii)	 schedule(static)
    for(ii=0;ii<4;ii++){
	int ifirst, iend;
	ifirst = ii*dm;
	iend = ifirst+dm;
	if (iend > m) iend = m;
	//	fprintf(stderr, "m, i, ifirst, iend = %d %d %d %d\n", m, ii, ifirst, iend);
	if (ifirst < m){
	    matmul_for_nk8_0(n1, a[ifirst], n2, b, n3, c[ifirst], iend-ifirst, n);
	}
    }

}

void matmul_for_nk16_0(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{
    int i;
    int mm=64;
    for(i=0;i<m;i+=mm){
	if (i+mm >m) mm=m-i;
	matmul_for_nk8_0(n1, (double(*)[]) (a[i]), n2, b,
			 n3, (double(*)[]) (c[i]), mm, 16);
	matmul_for_nk8_0(n1, (double(*)[]) (&a[i][8]), n2,(double(*)[])(b[8]),
			 n3, (double(*)[]) (c[i]), mm, 16);
    }

}
void matmul_for_nk16(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{
    if (m < 64){
	matmul_for_nk16_0c(n1, a, n2, b, n3, c, m, n);
	return;
    }
    int ii;
    int dm = (m+63)/64;
    dm*= 16;
#pragma omp parallel for private(ii)	 schedule(static)
    for(ii=0;ii<4;ii++){
	int ifirst, iend;
	ifirst = ii*dm;
	iend = ifirst+dm;
	if (iend > m) iend = m;
	//	fprintf(stderr, "m, i, ifirst, iend = %d %d %d %d\n", m, ii, ifirst, iend);
	if (ifirst < m){
	    matmul_for_nk16_0c(n1, a[ifirst], n2, b, n3, c[ifirst], iend-ifirst, n);
	}
    }

}

void matmul_for_nk32(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int n)
{
    int ii;
    int dm = (m+127)/128;
    dm*= 32;
#pragma omp parallel for private(ii)	 schedule(static)
    for(ii=0;ii<4;ii++){
	int ifirst, iend;
	ifirst = ii*dm;
	iend = ifirst+dm;
	if (iend > m) iend = m;
	//	fprintf(stderr, "m, i, ifirst, iend = %d %d %d %d\n", m, ii, ifirst, iend);
	if (ifirst < m){
	    matmul_for_nk32_0(n1, a[ifirst], n2, b, n3, c[ifirst], iend-ifirst, n);
	}
    }

}

void matmul_for_small_nk_7(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,j;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    unsigned long bpcount, apcount, dotcount;
    if (kk == 8){
	matmul_for_nk8(n1,  a, n2, b, n3, c, m, n);
	return;
    }
    BEGIN_TSC;
    bpcount= apcount= dotcount=0;
    //    BEGIN_TSC;
    for(k=0;k<kk;k++)
	for(j=0;j<nh;j++)
		bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    //    END_TSC(bpcount);
    for(i=0;i<m;i+=2){
	//	BEGIN_TSC;
	double *ap=a[i];
	double *ap2=a[i+1];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df * cp2 = (v2df*) (&(c[i+1][0]));

	for(k=0;k<kk;k+=2){
	    v2df * aa = (v2df*)(ap+k);
	     __builtin_prefetch((double*)a[i+4]+k,0);
	     __builtin_prefetch((double*)a[i+5]+k,0);
	    acopy [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    aa = (v2df*)(ap2+k);
	    acopy2 [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy2[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    //   acp[k*2]=acp[k*2+1]=ap[k];
	    //	    acp[k*2+2]=acp[k*2+3]=ap[k+1];
	    //	    acp2[k*2]=acp2[k*2+1]=ap2[k];
	    //	    acp2[k*2+2]=acp2[k*2+3]=ap2[k+1];
	}
	//	END_TSC(apcount);
	//	BEGIN_TSC;
	for(j=0;j<nh;j++){
	    tmp = tmp2=  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];
	    v2df ctmp2 = cp2[j] ;

	    v2df * bp = bcopy2[j];
	    __builtin_prefetch(c[i+4]+j,0);
	    __builtin_prefetch(c[i+5]+j,0);
	    for(k=0;k<kk;k+=8){
		int k2 = k+4;
		v2df *avp = acopy+k;
		v2df *avp2 = acopy2+k;
		v2df *bvp = bp+k;
		tmp += avp[0]*bvp[0];
		tmp2 += avp2[0]*bvp[0];
		tmp +=avp[1]*bvp[1];
		tmp2+=avp2[1]*bvp[1];
		tmp +=avp[2]*bvp[2];
		tmp2+=avp2[2]*bvp[2];
		tmp +=avp[3]*bvp[3];
		tmp2+=avp2[3]*bvp[3];
		tmp += avp[4]*bvp[4];
		tmp2 += avp2[4]*bvp[4];
		tmp +=avp[5]*bvp[5];
		tmp2+=avp2[5]*bvp[5];
		tmp +=avp[6]*bvp[6];
		tmp2+=avp2[6]*bvp[6];
		tmp +=avp[7]*bvp[7];
		tmp2+=avp2[7]*bvp[7];
	    }

	    cp[j] = ctmp -tmp;
	    cp2[j] = ctmp2 -tmp2;

	}
	//	END_TSC(dotcount);

    }
    //    printf("m, kk, n = %d %d %d counts = %g %g  %g\n", m,kk,n,
    //	   (double)bpcount, (double)apcount, (double)dotcount);
    END_TSC(t,11);

}

void matmul_for_small_nk(int n1, double a[][n1],
			 int n2, double b[][n2],
			 int n3, double c[][n3],
			 int m,
			 int kk,
			 int n)
{

    int i,ii;
    int nh = n/2;
    register int k;
    double bcopy[n][kk];
    v2df bcopy2[nh][kk];
    v2df acopy[kk];
    v2df acopy2[kk];
    double *acp = (double*) acopy;
    double *acp2 = (double*) acopy2;
    if (kk == 8){
	matmul_for_nk8_3(n1,  a, n2, b, n3, c, m, n);
	return;
    }
    if (kk == 16){
	matmul_for_nk16(n1,  a, n2, b, n3, c, m, n);
	return;
    }
    if (kk == 32){
	matmul_for_nk32(n1,  a, n2, b, n3, c, m, n);
	return;
    }
    BEGIN_TSC;
    for(k=0;k<kk;k++){
	int j;
	for(j=0;j<nh;j++)
	    bcopy2[j][k] = *((v2df*)(b[k]+j+j));
    }
#pragma omp parallel for private(i,k,acopy,acopy2)  schedule(static)
    for(i=0;i<m;i+=2){
	int j;
	double *ap=a[i];
	double *ap2=a[i+1];
	register v2df tmp, tmp2;
	v2df * cp = (v2df*) (&(c[i][0]));
	v2df * cp2 = (v2df*) (&(c[i+1][0]));
	for(k=0;k<kk;k+=2){
	    v2df * aa = (v2df*)(ap+k);
	    acopy [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	    aa = (v2df*)(ap2+k);
	    acopy2 [k]= (v2df)  __builtin_ia32_shufpd(*aa,*aa,0x0);
	    acopy2[k+1]=(v2df) __builtin_ia32_shufpd(*aa,*aa,0xff);
	}
	__builtin_prefetch(a[i+4],0,3);
	__builtin_prefetch(c[i+4],1);
	__builtin_prefetch(a[i+5],0,3);
	__builtin_prefetch(c[i+5],1);
	__builtin_prefetch(a[i+20],0,3);
	__builtin_prefetch(c[i+20],1);
	__builtin_prefetch(a[i+21],0,3);
	__builtin_prefetch(c[i+21],1);
	for(j=0;j<nh;j++){
	    tmp = tmp2=  (v2df){0.0,0.0};
	    v2df ctmp= cp[j];
	    v2df ctmp2 = cp2[j] ;
	    v2df * bp = bcopy2[j];
	    for(k=0;k<kk;k+=8){
		int k2 = k+4;
		v2df *avp = acopy+k;
		v2df *avp2 = acopy2+k;
		v2df *bvp = bp+k;
		tmp += avp[0]*bvp[0];
		tmp2 += avp2[0]*bvp[0];
		tmp +=avp[1]*bvp[1];
		tmp2+=avp2[1]*bvp[1];
		tmp +=avp[2]*bvp[2];
		tmp2+=avp2[2]*bvp[2];
		tmp +=avp[3]*bvp[3];
		tmp2+=avp2[3]*bvp[3];
		tmp += avp[4]*bvp[4];
		tmp2 += avp2[4]*bvp[4];
		tmp +=avp[5]*bvp[5];
		tmp2+=avp2[5]*bvp[5];
		tmp +=avp[6]*bvp[6];
		tmp2+=avp2[6]*bvp[6];
		tmp +=avp[7]*bvp[7];
		tmp2+=avp2[7]*bvp[7];
	    }
	    cp[j] = ctmp -tmp;
	    cp2[j] = ctmp2 -tmp2;
	}
    }
    END_TSC(t,11);
}

void mydgemm(int m,
	     int n,
	     int k,
	     double alpha,
	     double * a,
	     int na,
	     double * b,
	     int nb,
	     double beta,
	     double * c,
	     int nc)
{
    double t0, t1, t2;
    //    printf("mydgemm called %d %d %d\n", m, n, k);
    if (k>= 512){
	get_cputime(&t0,&t1);
    }
    BEGIN_TSC;
    BEGIN_TIMER(timer);
#ifdef USEGDR
    if ((k>512)  || ((k==512) && ((n>=1024)||(m>=1024)))){
	//    if (k>=2048){
	mygdrdgemm(m, n, k, alpha, a, na, b, nb, beta, c, nc);
    }else{
	if ((k<=16) && (alpha == -1.0) && (beta == 1.0)){
	    matmul_for_small_nk(na, a,  nb, b, nc, c,  m, n, k);
	}else{

	    cblas_dgemm( CblasRowMajor, CblasNoTrans, CblasNoTrans,
			 m,n, k, alpha, a, na, b, nb, beta, c, nc);
	}
    }
#else
#ifdef GEMMTEST
    mytestdgemm(m, n, k, na, nb, nc,alpha, a, b, beta, c);
#else
    cblas_dgemm( CblasRowMajor, CblasNoTrans, CblasNoTrans,
	         m,n, k, alpha, a, na, b, nb, beta, c, nc);
#endif
#endif
    //    cblas_dgemm( CblasRowMajor, CblasNoTrans, CblasNoTrans,
    //		 m,n, k, alpha, a, na, b, nb, beta, c, nc);
    if (k==2048){
	END_TIMER(timer,31,((double)(m))*n*k*2);
	END_TSC(t,14);
    }else if (k==1024){
	END_TIMER(timer,32,((double)(m))*n*k*2);
	END_TSC(t,15);
    }else if (k==512){
	END_TIMER(timer,33,((double)(m))*n*k*2);
	END_TSC(t,17);
    }else{
	END_TIMER(timer,34,((double)(m))*n*k*2);
	END_TSC(t,18);
    }

    if (k>= 512){
	get_cputime(&t0,&t1);
	dprintf(10,"dgemm M=%d N=%d K=%d time=%10.4g  %g Gflops\n",
		m,n,k,t0, ((double)m)*n*k*2/t0/1e9);
    }
}

void reset_gdr(int m, double a[][m], int nb, double awork[][nb], int n)
{
#ifdef USEGDR
    double aw2[nb][nb];
    if (nb < 2048){
	fprintf(stderr,"reset_gdr nb = %d <2048 not supported\n", nb);
	exit(-1);
    }
    gdr_check_and_restart(a, awork, aw2);

    int i,j;
    dprintf(9,"reset_gdr clear awork\n");
    for (i=0;i<nb;i++){
	for (j=0;j<n;j++){
	    awork[j][i]=0;
	}
    }
    dprintf(9,"reset_gdr clear aw2\n");
    for (i=0;i<nb;i++){
	for (j=0;j<nb;j++){
	    aw2[j][i]=0;
	}
    }
    dprintf(9,"reset_gdr try_dgemm\n");
    gdrsetforceswapab();
    cblas_dgemm( CblasRowMajor, CblasNoTrans, CblasNoTrans,
	         n,nb, nb, 1.0, awork, nb, aw2, nb, 0.0, a, m);
    mydgemm(n,nb,nb,1.0,awork,nb,aw2,nb,0.0,a,m);

#endif
}

#ifndef USEGDR
void gdrsetforceswapab(){}
void gdrresetforceswapab(){}
void gdrsetskipsendjmat(){};
void gdrresetskipsendjmat(){}
void gdrsetnboards(){}
void set_matmul_msg_level(int level){}
void gdrdgemm_set_stress_factor(int x){}
#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_blas.h>

#define RESOLUTION 28  // bmp image 28*28
#define LINE_LEN (RESOLUTION * RESOLUTION * 4) // All the pixel data stored within a line
#define DATA_NUM_WITHOUT_LABEL (RESOLUTION * RESOLUTION) // Data length without label
#define DATA_NUM_WITH_LABEL ((RESOLUTION * RESOLUTION) + 1) // For training, the first number is label, so +1
#define HIDDEN_NODE 250 // node number in hidden layer
#define OUTPUT_NODE 10  // output node number
#define LEARN_RATE 0.1
#define TRAIN_SET_SIZE 5000
#define TEST_SET_SIZE 1000
#define EPOCH 8

#define SIGMOID(x) (1.0 / (1.0 + (pow(M_E, -x))))
#define NOR_DIS_DEV(x) (pow(x, -0.5))  // For generating init weight

FILE* openfile(char* file_name);
void read_parse(FILE* fp, int input_data[], int* label, char* mode);
gsl_matrix* scale_input(int input_data[]);
gsl_matrix* vectorize_target(int label);
gsl_matrix* init_weight(int row, int col);
double get_output_error(gsl_matrix* target, gsl_matrix* output, gsl_matrix* output_error);
void training(gsl_matrix* better_input, gsl_matrix* target, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_o, gsl_matrix* output_x, gsl_matrix* output_error, gsl_matrix* hidden_error, double* error);
void store_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight);
void load_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight);
int recognize(gsl_matrix* better_input, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_x, gsl_matrix* output_o);

int main(int argc, char **argv)
{
	int label;
	double error;
	double error_acc = 0.0;
	char* file_name;
	char* mode;
	FILE* fp;
	int input_data[DATA_NUM_WITHOUT_LABEL];
	gsl_matrix* better_input;
	gsl_matrix* target;
	gsl_matrix* output_error = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* hidden_error = gsl_matrix_alloc(HIDDEN_NODE, 1);
	gsl_matrix* output_x = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* output_o = gsl_matrix_alloc(OUTPUT_NODE, 1);
    gsl_matrix* hidden_x = gsl_matrix_alloc(HIDDEN_NODE, 1);
    gsl_matrix* hidden_o = gsl_matrix_alloc(HIDDEN_NODE, 1);
	gsl_matrix* input_weight;
	gsl_matrix* hidden_weight;

	if(argc < 3){
		fprintf(stderr, "Usage: %s [-t(raining), -r(cognizing), -e(poch)] [file_name]\n", argv[0]);
		exit(EXIT_FAILURE);
	}else{
		mode = argv[1];
		file_name = argv[2];
	}

	fp = openfile(file_name);

	if(!strcmp(mode, "-t")){
		//training
		input_weight = init_weight(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL); // initiate the input-hidden weight
    	hidden_weight = init_weight(OUTPUT_NODE, HIDDEN_NODE);  // initiate the hidden-output weight

    	int training_times = 0;
    	puts("Training started\n");
        for (int i = 0; i < EPOCH; i++){

			do{
				read_parse(fp, input_data, &label, mode);
				better_input = scale_input(input_data);
				target = vectorize_target(label);
				training(better_input, target, input_weight, hidden_weight, hidden_x, hidden_o, output_o, output_x, output_error, hidden_error, &error);
				training_times++;
                error_acc += error;

			}while(training_times < TRAIN_SET_SIZE);

			rewind(fp);  // back to the start of the input file, start epoch
			if(i == 0){
				printf("In the first training, Accumulative Error = %f\n", error_acc);
				puts("========Start Epoch=========");
			}else{
				printf("Epoch %d, Accumulative Error = %f\n", i, error_acc);
			}
			error_acc = 0.0;
            training_times = 0;

        }

        gsl_matrix_free(better_input), gsl_matrix_free(target), gsl_matrix_free(output_error), gsl_matrix_free(hidden_error), gsl_matrix_free(output_x), gsl_matrix_free(output_o);
        gsl_matrix_free(hidden_x), gsl_matrix_free(hidden_o);

        store_weight(input_weight, hidden_weight);
        puts("Training weight has been stored!");
        gsl_matrix_free(input_weight), gsl_matrix_free(hidden_weight);

	}

	if(!strcmp(mode, "-r")){
		input_weight = gsl_matrix_alloc(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL);
		hidden_weight = gsl_matrix_alloc(OUTPUT_NODE, HIDDEN_NODE);

		load_weight(input_weight, hidden_weight);

		int test_times = 0;
		int wrong_times = 0;
		int anwser;

		do{
            read_parse(fp, input_data, &label, mode);
			better_input = scale_input(input_data);
			anwser = recognize(better_input, input_weight, hidden_weight, hidden_x, hidden_o, output_x, output_o);

			printf("Label: %d, Guess: %d\n", label, anwser);

			if(anwser != label) wrong_times++;

			test_times++;

		}while(test_times < TEST_SET_SIZE);
		printf("Performance: %2f\n", (1 - ((float)wrong_times/(float)test_times)) * 100);
		gsl_matrix_free(better_input), gsl_matrix_free(target), gsl_matrix_free(output_error), gsl_matrix_free(hidden_error), gsl_matrix_free(output_x), gsl_matrix_free(output_o);
		gsl_matrix_free(hidden_x), gsl_matrix_free(hidden_o);
		gsl_matrix_free(input_weight), gsl_matrix_free(hidden_weight);

	}
        fclose(fp);
		return 0;
}

FILE* openfile(char* file_name)
{
	FILE* fp;
	if(!(fp = fopen(file_name, "r")))
		fprintf(stderr, "Can't open the file\n");
	return fp;
}

void read_parse(FILE* fp, int input_data[], int* label, char* mode)
{
	char line[LINE_LEN];
	char temp;
	int line_c = 0;
	int input_c = 0;
	char* token;

	while((temp = fgetc(fp)) != '\n'){
        if(temp == EOF) break;
        line[line_c++] = temp;
	}
    line[line_c] = '\0';
    line_c = 0;
	if(!strcmp(mode, "-t")){
		token = strtok(line, ",");
		*label = atoi(token);
		while(token != NULL){
			token = strtok(NULL, ",");
			if(token == NULL) break;
			input_data[input_c++] = atoi(token);
		}

	}
	if(!strcmp(mode, "-r")){
		token = strtok(line, ",");
		*label = atoi(token);
		while(token != NULL){
			token = strtok(NULL, ",");
			if(token == NULL) break;
			input_data[input_c++] = atoi(token);
		}
	}
}

gsl_matrix* scale_input(int input_data[])
{
    gsl_matrix* better_input = gsl_matrix_alloc(DATA_NUM_WITHOUT_LABEL, 1);
	for(int i = 0; i < DATA_NUM_WITHOUT_LABEL; i++){
		gsl_matrix_set(better_input, i, 0, ((double)input_data[i]/255.0*0.99)+0.01);
	}
    return better_input;
}

gsl_matrix* vectorize_target(int label)
{
	gsl_matrix* target = gsl_matrix_alloc(10, 1);

	for(int i = 0; i < 10; i++){
		if(i == label){
			gsl_matrix_set(target, i, 0, 0.99);
		}else{
			gsl_matrix_set(target, i, 0, 0.01);
		}
	}

	return target;
}

gsl_matrix* init_weight(int row, int col)
{
    gsl_matrix* input_weight = gsl_matrix_alloc(row, col);

    const gsl_rng_type * T;
    gsl_rng * r;
    double sigma = NOR_DIS_DEV(col);
    r = gsl_rng_alloc (gsl_rng_mt19937);
    for(int i = 0; i < row; i++){

        for(int j = 0; j < col; j++){
            gsl_matrix_set(input_weight, i, j, gsl_ran_gaussian(r, sigma));
        }
    }
    gsl_rng_free(r);
    return input_weight;
}

double get_output_error(gsl_matrix* target, gsl_matrix* output, gsl_matrix* output_error)
{
	double error = 0.0;
	for (int i = 0; i < OUTPUT_NODE; i++){
		gsl_matrix_set(output_error, i, 0, (gsl_matrix_get(target, i, 0) - gsl_matrix_get(output, i, 0)));
	}

	for (int i = 0; i < OUTPUT_NODE; i++){
		error += pow(gsl_matrix_get(output_error, i, 0), 2);
	}

	return error;
}

void training(gsl_matrix* better_input, gsl_matrix* target, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_o, gsl_matrix* output_x, gsl_matrix* output_error, gsl_matrix* hidden_error, double* error)
{
	gsl_matrix* delta_hidden_weight = gsl_matrix_alloc(OUTPUT_NODE, HIDDEN_NODE);
	gsl_matrix* delta_input_weight = gsl_matrix_alloc(HIDDEN_NODE, DATA_NUM_WITHOUT_LABEL);
	gsl_matrix* temp_hidden = gsl_matrix_alloc(OUTPUT_NODE, 1);
	gsl_matrix* temp_input = gsl_matrix_alloc(HIDDEN_NODE, 1);

	 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, input_weight, better_input, 0.0, hidden_x);  // X = W*I
	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(hidden_o, i, 0, SIGMOID(gsl_matrix_get(hidden_x, i, 0)));
	 }

	 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, hidden_weight, hidden_o, 0.0, output_x);
	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(output_o, i, 0, SIGMOID(gsl_matrix_get(output_x, i, 0)));
	 }

	 *error = get_output_error(target, output_o, output_error);
	 gsl_blas_dgemm(CblasTrans, CblasNoTrans, 1.0, hidden_weight, output_error, 0.0, hidden_error); // Error hidden

	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(temp_hidden, i, 0, (LEARN_RATE * gsl_matrix_get(output_error, i, 0) * gsl_matrix_get(output_o, i, 0) * (1.0 - gsl_matrix_get(output_o, i, 0))));
	 }
	 gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, temp_hidden, hidden_o, 0.0, delta_hidden_weight);

	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(temp_input, i, 0, (LEARN_RATE * gsl_matrix_get(hidden_error, i, 0) * gsl_matrix_get(hidden_o, i, 0) * (1.0 - gsl_matrix_get(hidden_error, i, 0))));
	 }
	 gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, temp_input, better_input, 0.0, delta_input_weight);

	 gsl_matrix_add(hidden_weight, delta_hidden_weight);
	 gsl_matrix_add(input_weight, delta_input_weight);

	 gsl_matrix_free(delta_input_weight);
	 gsl_matrix_free(delta_hidden_weight);
	 gsl_matrix_free(temp_input);
	 gsl_matrix_free(temp_hidden);

}

void store_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight)
{
	FILE* fp = fopen("Good_Weight", "w+");

	for(int i = 0; i < HIDDEN_NODE; i++){
		for(int j = 0; j < DATA_NUM_WITHOUT_LABEL; j++){

			fprintf(fp, "%f,", gsl_matrix_get(input_weight, i, j));
		}
	}

	fseek(fp, -1, SEEK_CUR);
	fputc('\n', fp);

	for(int i = 0; i < OUTPUT_NODE; i++){
		for(int j = 0; j < HIDDEN_NODE; j++){

			fprintf(fp, "%f,", gsl_matrix_get(hidden_weight, i, j));
		}
	}
	fseek(fp, -1, SEEK_CUR);
	fputc('\n', fp);

	fclose(fp);
}

void load_weight(gsl_matrix* input_weight, gsl_matrix* hidden_weight)
{
	FILE* fp = fopen("Good_Weight", "r");
	char num_temp[20];
	double array[HIDDEN_NODE*DATA_NUM_WITHOUT_LABEL];
	char c;
	int j = 0, k;

	if(fp == NULL){
		fprintf(stderr, "No Good_Weight here, Please training first\n");
		exit(EXIT_FAILURE);
	}

	while((c = fgetc(fp)) != '\n'){

		if(c == ','){
			k = 0;
			array[j++] = atof(num_temp);
			bzero(num_temp, sizeof(num_temp));
			continue;
		}
		num_temp[k++] = c;
	}

	array[j] = atof(num_temp);

	j = 0;

	for(int n = 0; n < HIDDEN_NODE; n++){
		for(int m = 0; m < DATA_NUM_WITHOUT_LABEL; m++){
			gsl_matrix_set(input_weight, n, m, array[j++]);
		}
	}

	j = 0;
	k = 0;
	while((c = fgetc(fp)) != '\n'){

		if(c == ','){
			k = 0;
			array[j++] = atof(num_temp);
			bzero(num_temp, sizeof(num_temp));
			continue;
		}
		num_temp[k++] = c;
	}

	array[j] = atof(num_temp);

	j = 0;

	for(int n = 0; n < OUTPUT_NODE; n++){
		for(int m = 0; m < HIDDEN_NODE; m++){
			gsl_matrix_set(hidden_weight, n, m, array[j++]);
		}
	}

	fclose(fp);
}

int recognize(gsl_matrix* better_input, gsl_matrix* input_weight, gsl_matrix* hidden_weight, gsl_matrix* hidden_x, gsl_matrix* hidden_o, gsl_matrix* output_x, gsl_matrix* output_o)
{
	size_t row, col;
	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, input_weight, better_input, 0.0, hidden_x);  // X = W*I
	 for(int i = 0; i < HIDDEN_NODE; i++){
	 	gsl_matrix_set(hidden_o, i, 0, SIGMOID(gsl_matrix_get(hidden_x, i, 0)));
	 }

	gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, hidden_weight, hidden_o, 0.0, output_x);
	 for(int i = 0; i < OUTPUT_NODE; i++){
	 	gsl_matrix_set(output_o, i, 0, SIGMOID(gsl_matrix_get(output_x, i, 0)));
	 }

	 gsl_matrix_max_index(output_o, &row, &col);

	 return (int)row;
}


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

#include <gsl/gsl_math.h>
#include <gsl/gsl_interp2d.h>
#include <gsl/gsl_spline2d.h>

int
main()
{
  const gsl_interp2d_type *T = gsl_interp2d_bilinear;
  const size_t N = 100;             /* number of points to interpolate */
  const double xa[] = { 0.0, 1.0 }; /* define unit square */
  const double ya[] = { 0.0, 1.0 };
  const size_t nx = sizeof(xa) / sizeof(double); /* x grid points */
  const size_t ny = sizeof(ya) / sizeof(double); /* y grid points */
  double *za = malloc(nx * ny * sizeof(double));
  gsl_spline2d *spline = gsl_spline2d_alloc(T, nx, ny);
  gsl_interp_accel *xacc = gsl_interp_accel_alloc();
  gsl_interp_accel *yacc = gsl_interp_accel_alloc();
  size_t i, j;

  /* set z grid values */
  gsl_spline2d_set(spline, za, 0, 0, 0.0);
  gsl_spline2d_set(spline, za, 0, 1, 1.0);
  gsl_spline2d_set(spline, za, 1, 1, 0.5);
  gsl_spline2d_set(spline, za, 1, 0, 1.0);

  /* initialize interpolation */
  gsl_spline2d_init(spline, xa, ya, za, nx, ny);

  /* interpolate N values in x and y and print out grid for plotting */
  for (i = 0; i < N; ++i)
    {
      double xi = i / (N - 1.0);

      for (j = 0; j < N; ++j)
        {
          double yj = j / (N - 1.0);
          double zij = gsl_spline2d_eval(spline, xi, yj, xacc, yacc);

          printf("%f %f %f\n", xi, yj, zij);
        }
      printf("\n");
    }

  gsl_spline2d_free(spline);
  gsl_interp_accel_free(xacc);
  gsl_interp_accel_free(yacc);
  free(za);

  return 0;
}


#include <gsl/gsl_rng.h>
#include <stdio.h>

/*
gsl_rng_mt19937
gsl_rng_ranlxs0
gsl_rng_ranlxs1
gsl_rng_ranlxs2
*/

void print_algos(void) {

    const gsl_rng_type **t, **t0;

    t0 = gsl_rng_types_setup();

    printf("Available generators:\n");

    for (t = t0; *t != 0; t++) {
        printf("%s\n", (*t)->name);
    }

}

int main(void) {
    int seed = 102;
    int n = 10;

    gsl_rng_env_setup();

    gsl_rng* r = gsl_rng_alloc(gsl_rng_mt19937);
    gsl_rng_set(r, seed);

    for (int i = 0; i < n; i++) {
        double u = gsl_rng_uniform(r);
        printf("%.5f\n", u);
    }

    gsl_rng_free(r);

    return 0;
}


// -*- C++ -*-  Copyright (c) Microsoft Corporation; see license.txt
#ifndef MESH_PROCESSING_LIBHH_MY_LAPACK_H_
#define MESH_PROCESSING_LIBHH_MY_LAPACK_H_
#include "libHh/Hh.h"  // HH_REFERENCE_LIB()

#if defined(HH_HAVE_LAPACK)

#if defined(HH_USE_LAPACK_INTEGER)  // Google

#include <lapack.h>
using lapack_int = lapack::integer;

#elif !(defined(_WIN32) || defined(__CYGWIN__))  // unix

#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wredundant-decls"
#endif
#include <lapacke.h>  // in either /usr/include/ or /usr/include/lapacke/

#else  // cygwin or WIN32

#if defined(__CYGWIN__)

// cygwin: c:/cygwin/usr/src/lapack-3.5.0.tgz
//  lapacke_config.h
// #ifndef lapack_int
// #if defined(LAPACK_ILP64)
// #define lapack_int long
// #else
// #define lapack_int int
// #endif
// #endif
//
//  lapacke.h:  "#define lapack_int int"
// #define LAPACK_sgelss LAPACK_GLOBAL(sgelss, SGELSS)
// void LAPACK_sgelss( lapack_int* m, lapack_int* n, lapack_int* nrhs, float* a,
//                     lapack_int* lda, float* b, lapack_int* ldb, float* s,
//                     float* rcond, lapack_int* rank, float* work,
//                     lapack_int* lwork, lapack_int* info );

//  c:/cygwin/usr/src/lapack-3.5.0.tgz!lapack-3.5.0/lapacke/example/example_DGELS_colmajor.c
// #include <lapacke.h>
// {
//    double A[5][3] = {1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3};
//    double b[5][2] = {-10, 12, 14, 16, 18, -3, 14, 12, 16, 16};
//    lapack_int m = 5, n = 3, lda = 5, ldb = 5, nrhs = 2;
//    lapack_int info = LAPACKE_dgels(LAPACK_COL_MAJOR, 'N', m, n, nrhs, *A, lda, *b, ldb);
// }

using lapack_int = int;

#else  // _WIN32

using lapack_int = long;  // was defined as "integer" in f2c.h

#endif

// Continue: cygwin or WIN32

// clapack routines in liblapack
extern "C" {

// http://www.netlib.org/lapack/explore-html/d0/db8/group__real_g_esolve.html#ga206e3084597d088b31dc054a69aec93f
// SGELSS solves overdetermined or underdetermined systems for GE matrices, using SVD.
// ~/git/lib_src/CLAPACK/SRC/sgelss.c
lapack_int sgelss_(lapack_int* m, lapack_int* n, lapack_int* nrhs, float* a, lapack_int* lda, float* b,
                   lapack_int* ldb, float* s, float* rcond, lapack_int* irank, float* work, lapack_int* lwork,
                   lapack_int* info);

// same for double-precision
lapack_int dgelss_(lapack_int* m, lapack_int* n, lapack_int* nrhs, double* a, lapack_int* lda, double* b,
                   lapack_int* ldb, double* s, double* rcond, lapack_int* irank, double* work, lapack_int* lwork,
                   lapack_int* info);

// http://www.netlib.org/lapack/explore-html/d0/d2a/sgelsx_8f.html
// SGELSX solves overdetermined or underdetermined systems for GE matrices, using QR.
// This routine is deprecated and has been replaced by routine SGELSY.
// ~/git/lib_src/CLAPACK/SRC/sgelsx.c
// lapack_int sgelsx_(lapack_int* m, lapack_int* n, lapack_int* nrhs, float* a, lapack_int* lda,
//                   float* b, lapack_int* ldb, lapack_int* jpvt, float* rcond, lapack_int* irank,
//                   float* work, lapack_int* info);

// http://www.netlib.org/lapack/explore-html/dc/db6/sgelsy_8f.html
lapack_int sgelsy_(lapack_int* m, lapack_int* n, lapack_int* nrhs, float* a, lapack_int* lda, float* b,
                   lapack_int* ldb, lapack_int* jpvt, float* rcond, lapack_int* irank, float* work, lapack_int* lwork,
                   lapack_int* info);

// http://www.netlib.org/lapack/explore-html/d4/dca/group__real_g_esing.html
// subroutine SGESVD (JOBU, JOBVT, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, INFO)
// SGESVD computes the singular value decomposition (SVD) for GE matrices
// ~/git/lib_src/CLAPACK/SRC/sgesvd.c
lapack_int sgesvd_(char* jobu, char* jobvt, lapack_int* m, lapack_int* n, float* a, lapack_int* lda, float* s,
                   float* u, lapack_int* ldu, float* vt, lapack_int* ldvt, float* work, lapack_int* lwork,
                   lapack_int* info);

// http://www.netlib.org/lapack/explore-html/d8/ddc/group__real_g_ecomputational.html#ga7cb54fa1727bf0166523036f4948bc56
// subroutine SGEQRF (M, N, A, LDA, TAU, WORK, LWORK, INFO)
// SGEQRF computes a QR factorization of a real M-by-N matrix A:
// A = Q * R.
// ~/git/lib_src/CLAPACK/SRC/sgeqrf.c
lapack_int sgeqrf_(lapack_int* m, lapack_int* n, float* a, lapack_int* lda, float* tau, float* work, lapack_int* lwork,
                   lapack_int* info);
}

#if defined(_MSC_VER)
HH_REFERENCE_LIB("liblapack.lib");
HH_REFERENCE_LIB("libBLAS.lib");
#if 1
HH_REFERENCE_LIB("libf2c.lib");  // CLAPACK
#else
HH_REFERENCE_LIB("libF77.lib");  // liblapack
HH_REFERENCE_LIB("libI77.lib");
#endif
#endif  // defined(_MSC_VER)

#endif  // !(defined(_WIN32) || defined(__CYGWIN__))

#endif  // defined(HAVE_LAPACK)

#endif  // MESH_PROCESSING_LIBHH_MY_LAPACK_H_


#include <math.h>
#include <gsl/gsl_sf_gamma.h>
#include <galpy_potentials.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
//PowerSphericalPotentialwCutoff
//3  arguments: amp, alpha, rc
double mass(double r2,double alpha, double rc){
  return 2. * M_PI * pow ( rc , 3. - alpha ) * ( gsl_sf_gamma ( 1.5 - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1.5 - 0.5 * alpha , r2 / rc / rc ) );
}
double PowerSphericalPotentialwCutoffEval(double R,double Z, double phi,
					  double t,
					  struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  double r= sqrt(r2);
  return amp * 2. * M_PI * pow(rc,3.-alpha) / r * ( r / rc * ( gsl_sf_gamma ( 1. - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1. - 0.5 * alpha , r2 / rc / rc ) ) - ( gsl_sf_gamma ( 1.5 - 0.5 * alpha ) - gsl_sf_gamma_inc ( 1.5 - 0.5 * alpha , r2 / rc / rc) ) );
}
double PowerSphericalPotentialwCutoffRforce(double R,double Z, double phi,
					    double t,
					    struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) * R / pow(r2,1.5);
}
double PowerSphericalPotentialwCutoffPlanarRforce(double R,double phi,
						  double t,
						  struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) / r2;
}
double PowerSphericalPotentialwCutoffzforce(double R,double Z,double phi,
					    double t,
					    struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R+Z*Z;
  //Calculate Rforce
  return - amp * mass (r2,alpha,rc) * Z / pow(r2,1.5);
}
double PowerSphericalPotentialwCutoffPlanarR2deriv(double R,double phi,
						   double t,
						   struct potentialArg * potentialArgs){
  double * args= potentialArgs->args;
  //Get args
  double amp= *args++;
  double alpha= *args++;
  double rc= *args;
  //Radius
  double r2= R*R;
  //Calculate R2deriv
  return amp * ( 4. * M_PI * pow(r2,- 0.5 * alpha) * exp(-r2/rc/rc) - 2. * mass(r2,alpha,rc)/pow(r2,1.5) );
}


/* gsl_sf_hermite.h
 *
 * Copyright (C) 2011-2014 Konrad Griessinger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*----------------------------------------------------------------------*
 * (konradg(at)gmx.net)                                                 *
 *----------------------------------------------------------------------*/

#ifndef __GSL_SF_HERMITE_H__
#define __GSL_SF_HERMITE_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <gsl/gsl_sf_result.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

GSL_FUN int gsl_sf_hermite_prob_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob(const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_deriv_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_deriv(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite(const int n, const double x);
GSL_FUN int gsl_sf_hermite_deriv_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_deriv(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_func_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func(const int n, const double x);
GSL_FUN int gsl_sf_hermite_func_fast_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_fast(const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_array_deriv(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_deriv_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_array_deriv(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_deriv_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_func_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_func_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_func_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_der(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_prob_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_zero(const int n, const int s);
GSL_FUN int gsl_sf_hermite_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_zero(const int n, const int s);
GSL_FUN int gsl_sf_hermite_func_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_func_zero(const int n, const int s);

#ifndef GSL_DISABLE_DEPRECATED

GSL_FUN int gsl_sf_hermite_phys_e(const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys(const int n, const double x);
GSL_FUN int gsl_sf_hermite_phys_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_der(const int m, const int n, const double x);
GSL_FUN int gsl_sf_hermite_phys_array(const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_series_e(const int n, const double x, const double * a, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_series(const int n, const double x, const double * a);
GSL_FUN int gsl_sf_hermite_phys_array_der(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_der_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_phys_zero_e(const int n, const int s, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_phys_zero(const int n, const int s);

GSL_FUN int gsl_sf_hermite_prob_array_der(const int m, const int nmax, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_der_array(const int mmax, const int n, const double x, double * result_array);
GSL_FUN int gsl_sf_hermite_prob_der_e(const int m, const int n, const double x, gsl_sf_result * result);
GSL_FUN double gsl_sf_hermite_prob_der(const int m, const int n, const double x);

#endif /* !GSL_DISABLE_DEPRECATED */

__END_DECLS

#endif /* __GSL_SF_HERMITE_H__ */


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <time.h>
#define USAGE "./mcmc_loktavolterra.x n_steps n_burn"

void rg4th(float *resultado, float x, float y, float a, float b, float c, float d, float h);
void x_prime(float *valor, float x, float y, float alpha, float beta);
void y_prime(float *valor, float x, float y, float gamma, float delta);

void my_model(float *mine_presas,float *mine_depredadores,float *tiempo,float ini_presas, float ini_depredadores,float a, float b, float c, float d, int n_puntos);
void likelihood(float *chi,float *presas,float *depredadores,float *mine_presas,float *mine_depredadores,int n_puntos);
void mcmc(int n_steps,float *a,float *b,float *c,float *d,float *l,float *tiempo,float *presas,float *depredadores,int n_puntos,float *mine_presas,float *mine_depredadores, float ini_presas, float ini_depredadores);
void min(float *mine_presas,float *mine_depredadores,float ini_presas,float ini_depredadores,float *tiempo,float *a,float *b,float *c,float *d,float *l,int n_steps,int n_puntos);

void leer(float *tiempo, float *presas, float *depredadores, int n_puntos);
float *reserva(int n_puntos);
void print_array(float *a, float *b, float *c, float *d , float *l, int n_steps, int n_burn);

int main(int argc, char **argv){

	srand48(time(NULL));

	//Valores que entran
	int n_steps=atof(argv[1]);
	int n_burn=atof(argv[2]);
	//Inicializacion de los arrays que guardan los datos
	int n_puntos= 96;
	float *tiempo;
	float *presas;
	float *depredadores;
	tiempo=reserva(n_puntos);
	presas=reserva(n_puntos);
	depredadores=reserva(n_puntos);

	leer(tiempo, presas, depredadores, n_puntos);
	//Condiciones iniciales para resolver las ecuaciones diferenciales
	float ini_presas, ini_depredadores;
	ini_presas=presas[0];
	ini_depredadores=depredadores[0];

	//Array con los resultados del ajuste
	float *mine_presas;
	mine_presas=reserva(n_puntos);
	float *mine_depredadores;
	mine_depredadores=reserva(n_puntos);

	//Arrays que guardan los datos de las variables
	float *a; //alpha
	float *b; //beta
	float *c; //gamma
	float *d; //delta
	float *l;
	a=reserva(n_steps);
	b=reserva(n_steps);
	c=reserva(n_steps);
	d=reserva(n_steps);
	l=reserva(n_steps);
	//Primer paso en la cadena
	a[0]=drand48();
	b[0]=drand48();
	c[0]=drand48();
	d[0]=drand48();
	float chi;
	my_model(mine_presas,mine_depredadores,tiempo,ini_presas,ini_depredadores,a[0],b[0],c[0],d[0],n_puntos);
	likelihood(&chi,presas,depredadores,mine_presas,mine_depredadores,n_puntos);
	l[0]=chi;

	mcmc(n_steps,a,b,c,d,l,tiempo,presas,depredadores,n_puntos,mine_presas,mine_depredadores,ini_presas,ini_depredadores);
	min(mine_presas,mine_depredadores,ini_presas,ini_depredadores,tiempo,a,b,c,d,l,n_steps,n_puntos);

	print_array(a,b,c,d,l,n_steps,n_burn);

	free(tiempo);
	free(presas);
	free(mine_presas);
	free(depredadores);
	free(mine_depredadores);
	free(a);
	free(b);
	free(c);
	free(d);

	return 0;
}

//OBTENCION DEL MEJOR AJUSTE
void min(float *mine_presas,float *mine_depredadores,float ini_presas,float ini_depredadores,float *tiempo,float *a,float *b,float *c,float *d,float *l,int n_steps,int n_puntos){
	int i;
	float temp, a2, b2, c2, d2;
	temp=l[0];
	a2=a[0];
	b2=b[0];
	c2=c[0];
	d2=d[0];
	for(i=1;i<n_steps;i++){
		if(temp > l[i] ){
			temp=l[i];
			a2=a[i];
			b2=b[i];
			c2=c[i];
			d2=d[i];
		}
	}
	my_model(mine_presas,mine_depredadores,tiempo,ini_presas,ini_depredadores,a2,b2,c2,d2,n_puntos);
}

//ITERACIONES
void mcmc(int n_steps,float *a,float *b,float *c,float *d,float *l,float *tiempo,float *presas,float *depredadores,int n_puntos,float *mine_presas,float *mine_depredadores, float ini_presas, float ini_depredadores){
	int i;
	float a2,b2,c2,d2,chi,chi2,alpha,beta;
	const gsl_rng_type * T;
	gsl_rng * r;
	gsl_rng_env_setup();
	T = gsl_rng_default;
	r = gsl_rng_alloc (T);

    for(i=0;i<(n_steps-1);i++){
		a2 = a[i] + gsl_ran_gaussian(r, 0.1);
		b2 = b[i] + gsl_ran_gaussian(r, 0.1);
		c2 = c[i] + gsl_ran_gaussian(r, 0.1);
		d2 = c[i] + gsl_ran_gaussian(r, 0.1);

		float *presas_init;
		float *presas_prime;
		presas_init=reserva(n_puntos);
		presas_prime=reserva(n_puntos);
		float *depredadores_init;
		float *depredadores_prime;
		depredadores_init=reserva(n_puntos);
		depredadores_prime=reserva(n_puntos);

		my_model(presas_init,depredadores_init,tiempo,ini_presas,ini_depredadores,a[i],b[i],c[i],d[i],n_puntos);
		likelihood(&chi,presas,depredadores,presas_init,depredadores_init,n_puntos);

		my_model(presas_prime,depredadores_prime,tiempo,ini_presas,ini_depredadores,a2,b2,c2,d2,n_puntos);
		likelihood(&chi2,presas,depredadores,presas_prime,depredadores_prime,n_puntos);

		alpha = -chi2 + chi;
		if(alpha >= 0.0){
			a[i+1]=a2;
			b[i+1]=b2;
			c[i+1]=c2;
			d[i+1]=d2;
			l[i+1]=chi2;
		}
		else{
			beta = drand48();
			if(alpha < -6000.0 ){
				a[i+1]=a[i];
				b[i+1]=b[i];
				c[i+1]=c[i];
				d[i+1]=d[i];
				l[i+1]=chi;
			}
			else if(beta <= exp(alpha)){
				a[i+1]=a2;
				b[i+1]=b2;
				c[i+1]=c2;
				d[i+1]=d2;
				l[i+1]=chi2;
			}
			else{
				a[i+1]=a[i];
				b[i+1]=b[i];
				c[i+1]=c[i];
				d[i+1]=d[i];
				l[i+1]=chi;
			}
		}

		free(presas_init);
		free(presas_prime);
		free(depredadores_init);
		free(depredadores_prime);
	}

  gsl_rng_free (r);
}

//IMPRESION DE LOS ARRAYS
void print_array(float *a, float *b, float *c, float *d , float *l, int n_steps, int n_burn){
  int i;
  for(i=n_burn;i<n_steps;i++){
    printf("%f %f %f %f %f\n", a[i], b[i], c[i] , d[i], l[i]);
  }
}

//DEFINICION DEL CALCULO DEL AJUSTE
void my_model(float *mine_presas,float *mine_depredadores,float *tiempo,float ini_presas, float ini_depredadores,float a, float b, float c, float d, int n_puntos){

	//Condiciones iniciales
	mine_presas[0] = ini_presas;
	mine_depredadores[0] = ini_depredadores;

	//Recorrido R-K
	int i;
	for(i=1;i<n_puntos;i++){
		float *r;
		float h;
		h = tiempo[i]-tiempo[i-1];
		r=reserva(2);
		rg4th(r, mine_presas[i-1], mine_depredadores[i-1], a, b, c, d, h);
		mine_presas[i]=r[0];
		mine_depredadores[i]=r[1];
		free(r);
	}
}

//RUNGE-KUTTA DE CUARTO ORDEN
void rg4th(float *resultado, float x, float y, float a, float b, float c, float d, float h){
    float k1_x,k1_y;
    x_prime(&k1_x,x,y,a,b);
    y_prime(&k1_y,x,y,c,d);

    //First step
    float x1,y1;
    x1 = x + (h/2.0) * k1_x;
    y1 = y + (h/2.0) * k1_y;

	float k2_x,k2_y;
    x_prime(&k2_x,x1,y1,a,b);
    y_prime(&k2_y,x1,y1,c,d);

    //Second step
    float x2,y2;
    x2 = x + (h/2.0) * k2_x;
    y2 = y + (h/2.0) * k2_y;

	float k3_x,k3_y;
	x_prime(&k3_x,x2,y2,a,b);
    y_prime(&k3_y,x2,y2,c,d);

    //Third step
    float x3,y3;
    x3 = x + h * k3_x;
    y3 = y + h * k3_y;

	float k4_x,k4_y;
	x_prime(&k4_x,x3,y3,a,b);
    y_prime(&k4_y,x3,y3,c,d);

    //Fourth step
    float average_x, average_y;
    average_x = (1.0/6.0)*(k1_x + 2.0*k2_x + 2.0*k3_x + k4_x);
    average_y = (1.0/6.0)*(k1_y + 2.0*k2_y + 2.0*k3_y + k4_y);

    resultado[0] = x + h * average_x;
    resultado[1] = y + h * average_y;
}

//ECUACION DIFERENCIAL DE PRESAS
void x_prime(float *valor, float x, float y, float alpha, float beta){
	*valor= x * (alpha - beta * y);
}

//ECUACION DIFERENCIAL DE DEPREDADORES
void y_prime(float *valor, float x, float y, float gamma, float delta){
	*valor = -1 * y * (gamma - delta * x);
}

//RESERVA DEL AJUSTE
float *reserva(int n_puntos){
  float *array;
  int i;
  if(!(array = malloc(n_puntos * sizeof(float)))){
    printf("Problema en reserva\n");
    exit(1);
  }
  for(i=0;i<n_puntos;i++){
    array[i] = 0.0;
  }
  return array;
}

//CALCULO DE LA VARIACION DEL AJUSTE
void likelihood(float *chi,float *presas,float *depredadores,float *mine_presas,float *mine_depredadores,int n_puntos){
	int i;
	float temp1=0;
	float temp2=0;
	for(i=0;i<n_puntos;i++){
		temp1=temp1+pow(presas[i]-mine_presas[i],2.0);
		temp2=temp2+pow(depredadores[i]-mine_depredadores[i],2.0);
	}
	*chi=(1.0/2.0)*(temp1+temp2);
}

//LECTURA DEL ARCHIVO
void leer(float *tiempo, float *presas, float *depredadores,int n_puntos){

	FILE *in;
	int i;
	in=fopen("archivo.dat","r");
	if(!in){
    printf("problems opening the file %s\n","archivo.dat");
    exit(1);
	}
	for(i=0;i<(n_puntos);i++){

		  fscanf(in, "%f %f %f\n", &tiempo[i], &presas[i], &depredadores[i]);

	}
	fclose(in);
}


#include <cublas.h>
#include <cblas.h>
#include <stdio.h>
#include <stdlib.h>

void checkStatus(char* message, cublasStatus status) {
    if (status != CUBLAS_STATUS_SUCCESS) {
    	fprintf (stderr, "!!!! %s fail %d\n", message, status);
    	exit(EXIT_FAILURE);
    }
}

double cblas_ddot (const int n, const double *x, const int incx, const double *y, const int incy) {
	double result;
	double *cuA, *cuB;
	cublasStatus status;

	status = cublasInit();
	checkStatus("init",  status);
	status = cublasAlloc(n, sizeof(double),(void**)&cuA);
	checkStatus("A", status);
	status = cublasAlloc(n, sizeof(double),(void**)&cuB);
	checkStatus("B", status);

	status = cublasSetVector(n, sizeof(double), x, incx, cuA, incx);
	checkStatus("setA", status);

	status = cublasSetVector(n, sizeof(double), y, incy, cuB, incy);
	checkStatus("setB", status);

	result = cublasDdot(n, cuA, incx, cuB, incy);

	status = cublasFree(cuA);
	checkStatus("freeA", status);
	status = cublasFree(cuB);
	checkStatus("freeB", status);

//	cublasShutdown();

	return result;
}

void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
                 const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
                 const int K, const double alpha, const double  *A,
                 const int lda, const double  *B, const int ldb,
                 const double beta, double  *C, const int ldc) {
	double *cuA, *cuB, *cuC;
	cublasStatus status;

	status = cublasInit();
	checkStatus("init",  status);
	status = cublasAlloc(M * N, sizeof(double),(void**)&cuA);
	checkStatus("A", status);
	status = cublasAlloc(N * K, sizeof(double),(void**)&cuB);
	checkStatus("B", status);
	status = cublasAlloc(M * K, sizeof(double),(void**)&cuC);
	checkStatus("C", status);

	status = cublasSetMatrix(M, N, sizeof(double), A, lda, cuA, lda);
	checkStatus("setA", status);

	status = cublasSetMatrix(M, N, sizeof(double), B, ldb, cuB, ldb);
	checkStatus("setB", status);

	// status = cublasSetMatrix(M, N, sizeof(double), C, ldc, cuC, ldc);
	// checkStatus("setC", status);

	// HACK: ignore trans
	cublasDgemm('N', 'N', M, N, K, alpha, cuA, lda, cuB, ldb, beta, cuC, ldc);
	//checkStatus("dgemm", status);

	status = cublasGetMatrix(M, N, sizeof(double), cuC, ldc, C, ldc);
	checkStatus("setB", status);

	status = cublasFree(cuA);
	checkStatus("freeA", status);
	status = cublasFree(cuB);
	checkStatus("freeB", status);
	status = cublasFree(cuC);
	checkStatus("freeC", status);

//	cublasShutdown();
}


#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_integration.h>

#include "allvars.h"
#include "proto.h"

static double logTimeBegin;
static double logTimeMax;

double drift_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * a * a * a);
}

double gravkick_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * a * a);
}

double hydrokick_integ(double a, void *param)
{
  double h;

  h = hubble_function(a);

  return 1 / (h * pow(a, 3 * GAMMA_MINUS1) * a);
}

double growthfactor_integ(double a, void *param)
{
  double s;

  s = hubble_function(a) / All.Hubble * sqrt(a * a * a);

  return pow(sqrt(a) / s, 3);
}

void init_drift_table(void)
{
#define WORKSIZE 100000
  int i;
  double result, abserr;

  /*---------- DEBUG ! ------------*/
#ifdef DARKENERGY_DEBUG
  FILE *FdDKfac;
  char mode[2], buf[200];

  strcpy(mode, "w");
  sprintf(buf, "%s%s", All.OutputDir, "driftkickfac.txt");
  FdDKfac = fopen(buf, mode);
  fprintf(FdDKfac, "i a drift GravKick HydroKick\n");
  /*---------- DEBUG ! ------------*/
#endif

  gsl_function F;
  gsl_integration_workspace *workspace;

  logTimeBegin = log(All.TimeBegin);
  logTimeMax = log(All.TimeMax);

  workspace = gsl_integration_workspace_alloc(WORKSIZE);

  for(i = 0; i < DRIFT_TABLE_LENGTH; i++)
    {
      F.function = &drift_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      DriftTable[i] = result;

      F.function = &gravkick_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      GravKickTable[i] = result;

      F.function = &hydrokick_integ;
      gsl_integration_qag(&F, exp(logTimeBegin), exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), All.Hubble,	/* note: absolute error just a dummy */
			  1.0e-8, WORKSIZE, GSL_INTEG_GAUSS41, workspace, &result, &abserr);
      HydroKickTable[i] = result;

#ifdef DARKENERGY_DEBUG
  /*---------- DEBUG ! ------------*/
      fprintf(FdDKfac, "%d %e %e %e %e \n", i,
	      exp(logTimeBegin + ((logTimeMax - logTimeBegin) / DRIFT_TABLE_LENGTH) * (i + 1)), DriftTable[i],
	      GravKickTable[i], HydroKickTable[i]);
  /*---------- DEBUG ! ------------*/
#endif
    }

  gsl_integration_workspace_free(workspace);

#ifdef DARKENERGY_DEBUG
  /*---------- DEBUG ! ------------*/
  fclose(FdDKfac);
  /*---------- DEBUG ! ------------*/
#endif
}

/*! This function integrates the cosmological prefactor for a drift
 *   step between time0 and time1. The value returned is
 *  \f[ \int_{a_0}^{a_1} \frac{{\rm d}a}{H(a)}
 *  \f]
 *
 *  A lookup-table is used for reasons of speed.
 */
double get_drift_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * DriftTable[0];
  else
    df1 = DriftTable[i1 - 1] + (DriftTable[i1] - DriftTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * DriftTable[0];
  else
    df2 = DriftTable[i2 - 1] + (DriftTable[i2] - DriftTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}

double get_gravkick_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * GravKickTable[0];
  else
    df1 = GravKickTable[i1 - 1] + (GravKickTable[i1] - GravKickTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * GravKickTable[0];
  else
    df2 = GravKickTable[i2 - 1] + (GravKickTable[i2] - GravKickTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}

double get_hydrokick_factor(int time0, int time1)
{
  double a1, a2, df1, df2, u1, u2;
  int i1, i2;
  static int last_time0 = -1, last_time1 = -1;
  static double last_value;

  if(time0 == last_time0 && time1 == last_time1)
    return last_value;

  /* note: will only be called for cosmological integration */

  a1 = logTimeBegin + time0 * All.Timebase_interval;
  a2 = logTimeBegin + time1 * All.Timebase_interval;

  if(logTimeMax > logTimeBegin)
    u1 = (a1 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u1 = 0;
  i1 = (int) u1;
  if(i1 >= DRIFT_TABLE_LENGTH)
    i1 = DRIFT_TABLE_LENGTH - 1;

  if(i1 <= 1)
    df1 = u1 * HydroKickTable[0];
  else
    df1 = HydroKickTable[i1 - 1] + (HydroKickTable[i1] - HydroKickTable[i1 - 1]) * (u1 - i1);

  if(logTimeMax > logTimeBegin)
    u2 = (a2 - logTimeBegin) / (logTimeMax - logTimeBegin) * DRIFT_TABLE_LENGTH;
  else
    u2 = 0;
  i2 = (int) u2;
  if(i2 >= DRIFT_TABLE_LENGTH)
    i2 = DRIFT_TABLE_LENGTH - 1;

  if(i2 <= 1)
    df2 = u2 * HydroKickTable[0];
  else
    df2 = HydroKickTable[i2 - 1] + (HydroKickTable[i2] - HydroKickTable[i2 - 1]) * (u2 - i2);

  last_time0 = time0;
  last_time1 = time1;

  return last_value = (df2 - df1);
}


/* randist/binomial.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_sf_gamma.h>

/* The binomial distribution has the form,

   prob(k) =  n!/(k!(n-k)!) *  p^k (1-p)^(n-k) for k = 0, 1, ..., n

   This is the algorithm from Knuth */

unsigned int
gsl_ran_binomial (const gsl_rng * r, double p, unsigned int n)
{
  unsigned int i, a, b, k = 0;

  while (n > 10)	/* This parameter is tunable */
    {
      double X;
      a = 1 + (n / 2);
      b = 1 + n - a;

      X = gsl_ran_beta (r, (double) a, (double) b);

      if (X >= p)
	{
	  n = a - 1;
	  p /= X;
	}
      else
	{
	  k += a;
	  n = b - 1;
	  p = (p - X) / (1 - X);
	}
    }

  for (i = 0; i < n; i++)
    {
      double u = gsl_rng_uniform (r);
      if (u < p)
	k++;
    }

  return k;
}

double
gsl_ran_binomial_pdf (const unsigned int k, const double p,
		      const unsigned int n)
{
  if (k > n)
    {
      return 0 ;
    }
  else
    {
      double a = k;
      double b = n - k;
      double P;
      double Cnk = gsl_sf_choose (n, k) ;

      P = Cnk * pow (p, a) * pow (1 - p, b);

      return P;
    }
}