#include <amsculib3/amsculib3.hpp>

namespace amscuda
{
namespace random
{

    __host__ void amscurand_tests1_randstat1()
    {
        int I;
        float q0,qlast,qmindelta,qmaxdelta,qmx,qmn,qmean,qstd,qsum,qsumsq;
        int N = 100000;
        uint32_t seed= 2;
        rand_seed(seed);
        printf("from seed %u...\n",seed);
        q0 = randnf();
        qmx = q0;
        qmn = q0;
        qsum = 0.0f;
        qsumsq = 0.0f;
        qlast = q0;
        q0 = randnf();
        qmindelta = ::fabsf(q0-qlast);
        qmaxdelta = ::fabsf(q0-qlast);
        for(I=0;I<N;I++)
        {
            qlast = q0;
            q0 = randnf();

            qsum += q0;
            qsumsq += q0*q0;
            if(q0>qmx) qmx = q0;
            if(q0<qmn) qmn = q0;
            if(::fabsf(q0-qlast)<qmindelta) qmindelta = ::fabsf(q0-qlast);
            if(::fabsf(q0-qlast)>qmaxdelta) qmaxdelta = ::fabsf(q0-qlast);
        }

        qmean = qsum/((float)N);
        qstd = ::sqrtf((qsumsq-qsum*qsum/((float)N))/((float)N));

        printf("Statistics for randnf() N=%d\n",N);
        printf("\tmin: %1.3g\n",qmn);
        printf("\tmax: %1.3g\n",qmx);
        printf("\tmean: %1.3g\n",qmean);
        printf("\tstdev: %1.3g\n",qstd);
        printf("\tmindelta: %1.3g\n",qmindelta);
        printf("\tmaxdelta: %1.3g\n",qmaxdelta);
    }

    __host__ void amscurand_tests1_randstat2()
    {
        int I;
        double q0,qlast,qmindelta,qmaxdelta,qmx,qmn,qmean,qstd,qsum,qsumsq;
        int N = 100000;
        uint32_t seed= 2;
        rand_seed(seed);
        printf("from seed %u...\n",seed);
        q0 = randn();
        qmx = q0;
        qmn = q0;
        qsum = 0.0f;
        qsumsq = 0.0f;
        qlast = q0;
        q0 = randn();
        qmindelta = ::fabs(q0-qlast);
        qmaxdelta = ::fabs(q0-qlast);
        for(I=0;I<N;I++)
        {
            qlast = q0;
            q0 = randn();

            qsum += q0;
            qsumsq += q0*q0;
            if(q0>qmx) qmx = q0;
            if(q0<qmn) qmn = q0;
            if(::fabs(q0-qlast)<qmindelta) qmindelta = ::fabs(q0-qlast);
            if(::fabs(q0-qlast)>qmaxdelta) qmaxdelta = ::fabs(q0-qlast);
        }

        qmean = qsum/((double)N);
        qstd = ::sqrt((qsumsq-qsum*qsum/((double)N))/((double)N));

        printf("Statistics for randn() N=%d\n",N);
        printf("\tmin: %1.6g\n",qmn);
        printf("\tmax: %1.6g\n",qmx);
        printf("\tmean: %1.6g\n",qmean);
        printf("\tstdev: %1.6g\n",qstd);
        printf("\tmindelta: %1.6g\n",qmindelta);
        printf("\tmaxdelta: %1.6g\n",qmaxdelta);
    }


    __host__ void amscurand_tests1()
    {
        printf("Random number generator basic function test:\n");
        int I;
        // uint32_t q1;
        // float q2;
        // double q3;
        // int q4;
        
        rand_seed(1);
        printf("from seed 1...\n");
        for(I=0;I<10;I++)
        {
            printf("randui32[%d] = %u\n",I,randui32());
        }

        rand_seed(1);
        printf("from seed 1...\n");
        for(I=0;I<10;I++)
        {
            printf("randf[%d] = %1.3f\n",I,randf());
        }

        rand_seed(0);
        printf("from seed 0...\n");
        for(I=0;I<10;I++)
        {
            printf("randf[%d] = %1.3f\n",I,rand());
        }


        amscurand_tests1_randstat1();
        amscurand_tests1_randstat2();
        



        return;
    }


    void amscurand_tests2_1()
    {
        const char *fnout = "../test_scripts/randf_array.bin";
        FILE *fp = NULL;
        int Nx = 4096;
        int Ny = 4096;
        cuarray<float> data;
        cuarray<int> dims;
        fp = fopen(fnout,"w+b");

        data.resize(Nx*Ny);
        dims.resize(2);
        dims[0] = Nx;
        dims[1] = Ny;

        double t0,t1;
        t0 = util::time_msec();
        hbuff_randf(data.data,Nx*Ny);
        t1 = util::time_msec();

        printf("hbuff_randf execution time: %1.3f msec\n",t1-t0);

        fwrite_ndarray(fp,&dims,&data);
        if(fp==NULL)
        {
            printf("amscurand_tests2_1 error: could not write %s",fnout);
        }

        fclose(fp);
        return;
    }

    void amscurand_tests2_2()
    {
        const char *fnout = "../test_scripts/randf_array2.bin";
        FILE *fp = NULL;
        int Nx = 4096;
        int Ny = 4096;
        cuarray<float> data;
        float *ddata = NULL;
        cuarray<int> dims;
        fp = fopen(fnout,"w+b");

        data.resize(Nx*Ny);
        cudaMalloc(&ddata,sizeof(float)*Nx*Ny);
        dims.resize(2);
        dims[0] = Nx;
        dims[1] = Ny;

        double t0,t1;
        t0 = amscuda::util::time_msec();
        dbuff_randf(ddata,Nx*Ny);
        t1 = amscuda::util::time_msec();
        
        printf("dbuff_randf execution time: %1.3f msec\n",t1-t0);

        cudaMemcpy(data.data,ddata,sizeof(float)*Nx*Ny,cudaMemcpyDeviceToHost);


        fwrite_ndarray(fp,&dims,&data);
        if(fp==NULL)
        {
            printf("amscurand_tests2_1 error: could not write %s",fnout);
        }

        cudaFree(ddata); ddata=NULL;

        fclose(fp);
        return;
    }

    void amscurand_tests2_3()
    {
        const char *fnout = "../test_scripts/randint_array.bin";
        FILE *fp = NULL;
        int Nx = 4096;
        int Ny = 4096;
        cuarray<int> data;
        cuarray<int> dims;
        fp = fopen(fnout,"w+b");

        data.resize(Nx*Ny);
        dims.resize(2);
        dims[0] = Nx;
        dims[1] = Ny;

        double t0,t1;
        t0 = util::time_msec();
        hbuff_randint(data.data,Nx*Ny,0,10);
        t1 = util::time_msec();

        printf("hbuff_randint execution time: %1.3f msec\n",t1-t0);

        fwrite_ndarray(fp,&dims,&data);
        if(fp==NULL)
        {
            printf("amscurand_tests2_1 error: could not write %s",fnout);
        }

        fclose(fp);
        return;
    }

    void amscurand_tests2_4()
    {
        const char *fnout = "../test_scripts/randint_array2.bin";
        FILE *fp = NULL;
        int Nx = 4096;
        int Ny = 4096;
        cuarray<int> data;
        int *ddata = NULL;
        cuarray<int> dims;
        fp = fopen(fnout,"w+b");

        data.resize(Nx*Ny);
        cudaMalloc(&ddata,sizeof(int)*Nx*Ny);
        dims.resize(2);
        dims[0] = Nx;
        dims[1] = Ny;

        double t0,t1;
        t0 = amscuda::util::time_msec();
        dbuff_randint(ddata,Nx*Ny,0,10);
        t1 = amscuda::util::time_msec();
        
        printf("dbuff_randint execution time: %1.3f msec\n",t1-t0);

        cudaMemcpy(data.data,ddata,sizeof(int)*Nx*Ny,cudaMemcpyDeviceToHost);


        fwrite_ndarray(fp,&dims,&data);
        if(fp==NULL)
        {
            printf("amscurand_tests2_1 error: could not write %s",fnout);
        }

        cudaFree(ddata); ddata=NULL;

        fclose(fp);
        return;
    }

    __host__ void amscurand_tests2()
    {
        amscurand_tests2_1();
        amscurand_tests2_2();
        amscurand_tests2_3();
        amscurand_tests2_4();

        return;
    }

}; //end namespaces
};