/**
 *  @file   test.cpp
 *  @brief  xorshift乱数の速度テスト
 *  @author tenk*
 *  @date   2010-1
 */

#define XORSHIFTRAND_TEST
#include <stdio.h>
#include <stdlib.h>
#include "XorShiftRand.h"
#include "MtRand.h"

//static unsigned DSP(unsigned r) { printf("%#10x\n", r); return r; }
#define DSP(x)      x

// ===========================================================================

/// 適当な線形合同法の乱数. (http://www001.upp.so-net.ne.jp/isaku/rand.html の rand( )その1)
class Test_Rand1 {
    unsigned    s_;
public:
    Test_Rand1() : s_(1) {}

    unsigned operator()() {
        //return (s_ = s_ * 1103515245+12345) & 2147483647; // どうせなら>>1のほうがいい.
        return (s_ = s_ * 1103515245+12345);                // がこの例では最上位ビットをoffする必要なし.
    }
};


/// C標準ライブラリの rand() を呼び出す.
struct Test_Clib_Rand {
    unsigned operator()() { return rand(); }
};



// ===========================================================================
// 時間計測関係
#ifdef _WIN32
#include <windows.h>
typedef unsigned __int64        PERFCNT_T;
PERFCNT_T   PERFCNT_get()       { PERFCNT_T c; QueryPerformanceCounter((LARGE_INTEGER*)&c); return c; }
PERFCNT_T   PERFCNT_per_sec()   { static PERFCNT_T s = 0; if (!s) QueryPerformanceFrequency((LARGE_INTEGER*)&s); return s;  }
#elif defined LINUX
#include <sys/resource.h>
typedef  unsigned long long     PERFCNT_T;
PERFCNT_T   PERFCNT_get()       { struct rusage t; getrusage(RUSAGE_SELF, &t); return t.ru_utime.tv_sec * 1000000ULL + t.ru_utime.tv_usec; }
#define     PERFCNT_per_sec()   1000000ULL
#else
#include <time.h>
typedef clock_t                 PERFCNT_T;
#define     PERFCNT_get()       clock()
#define     PERFCNT_per_sec()   CLOCKS_PER_SEC
#endif



// ===========================================================================
// 計測&チェック

unsigned g_dummy_var;

template<class RND>
double benchmark(RND rnd, size_t count, unsigned flags, const char* ttl)
{
    PERFCNT_T       start_tick;
    PERFCNT_T       elapsed;
    unsigned        j;
    double          e;
    unsigned char   verboss     = flags & 1;
    if (verboss) {
        fprintf(stderr, "\t%s:start\n", ttl);
    }
    start_tick  = PERFCNT_get();                // 計測開始
    for (j = 0; j < count; ++j) {
        g_dummy_var = DSP( rnd() );
    }
    elapsed   = PERFCNT_get() - start_tick;     // 計測終了
    e         = elapsed * 1.0 / ((double)PERFCNT_per_sec() * (double)count);
    if (verboss) {
        fprintf(stderr, "\t%s:end (%f)\n", ttl, e * 1000000.0);
    }
    return e;   // 計測された時間.
}


static void test_all(unsigned count, unsigned flags)
{
    enum { N = 64 };
    const char* nm[N]       = {0};
    double      elaped[N]   = { 0 };

    printf( "\nxor-shift乱数の実行計測(%9d回実行の平均) 単位:ナノ秒\n", count );

    // いくつかのテスト呼び出し.
    {
        unsigned k     = 0;
        nm[k] = "XorShift  32"  ; elaped[k] = benchmark(XorShiftRand<32>() , count, flags, nm[k]); ++k;
        nm[k] = "XorShift  64"  ; elaped[k] = benchmark(XorShiftRand<64>() , count, flags, nm[k]); ++k;
        nm[k] = "XorShift  96"  ; elaped[k] = benchmark(XorShiftRand<96>() , count, flags, nm[k]); ++k;
        nm[k] = "XorShift 128"  ; elaped[k] = benchmark(XorShiftRand<128>(), count, flags, nm[k]); ++k;
        nm[k] = "XorShift 160"  ; elaped[k] = benchmark(XorShiftRand<160>(), count, flags, nm[k]); ++k;
        nm[k] = "XorShift 192"  ; elaped[k] = benchmark(XorShiftRand<192>(), count, flags, nm[k]); ++k;
        nm[k] = "XorShift 256"  ; elaped[k] = benchmark(XorShiftRand<256>(), count, flags, nm[k]); ++k;
        nm[k] = "LCG         "  ; elaped[k] = benchmark(Test_Rand1()       , count, flags, nm[k]); ++k;
        nm[k] = "clib rand   "  ; elaped[k] = benchmark(Test_Clib_Rand()   , count, flags, nm[k]); ++k;
        nm[k] = "MT Rand     "  ; elaped[k] = benchmark(MtRand()           , count, flags, nm[k]); ++k;
    }

    for (unsigned j = 0; j < N; ++j) {
        double  e;
        if (nm[j] == 0)
            continue;
        printf(",%-21s ,", nm[j]);
        e = elaped[j] * 1000000000.0;
        if (e > 0)
            printf("%12.3f,", e);
        else
            printf("%12s,", "---");
        printf("\n");
    }
}



// ===========================================================================

/** 使い方.
 */
static int usage()
{
    fprintf(stderr, "usage>test [-opts] file(s)\n"
           "  xor shift 乱数の 速度チェック.\n"
           "  -cN   試行回数の指定\n"
           "  -v    経過メッセージを増やす\n"
          );
    return 1;
}



/** メイン.
 */
int main(int argc, const char* argv[])
{
    int      i;
    unsigned count = 1;
    unsigned flags = 0;

    if (argc < 2)
        return usage();

    // オプション取得.
    for (i = 1; i < argc; ++i) {
        const char* a = argv[i];
        if (a[0] == '-') {
            if (a[1] == 'v') {
                flags = 1;      // verboss;
            } else if (a[1] == 'c') {
                count   = strtol(a+2,0,0);
                if (count < 1)
                    count = 1;
            } else {
                return usage();
            }
        }
    }

    test_all(count, flags);

    return 0;
}