
#include "fxttypes.h"
#include "bits/bitsperlong.h"
#include "bits/bitasm.h"
#include "bits-all.h"
#include "bits/printbin.h"
#include "jjassert.h"
#include "fxtio.h"
#include "fxtiomanip.h"

#include <cstdlib> // rand()

char str[BYTES_PER_LONG];

#define PP(x) \
 cout << setw(25) << #x << " = "; \
 print_bin("", (x),0); \
 cout << "  " << x; cout << endl;

//static inline ulong hbi(ulong x)
//// this is a _NON_WORKING_ highest_one_idx() implementation
//{
//    ulong r = 0;
//#if  BITS_PER_LONG >= 64
//    if ( x & (~0UL<<32) )  r += 32;
//#endif
//    if ( x & 0xffff0000UL )  r += 16;
//    if ( x & 0xff00ff00UL )  r +=  8;
//    if ( x & 0xf0f0f0f0UL )  r +=  4;
//    if ( x & 0xccccccccUL )  r +=  2;
//    if ( x & 0xaaaaaaaaUL )  r +=  1;
//    return r;
//}
//// -------------------------
//
//static inline ulong bc15(uint x)
//// Return number of set bits, must have at most 15 set bits,
//{
//    x -=  (x>>1) & 0x55555555UL;
//    x  = ((x>>2) & 0x33333333UL) + (x & 0x33333333UL);
//    x *= 0x11111111UL;
//    return  x>>28;
//}
//// -------------------------

//static inline ulong par(ulong x)
//{
//    x *= 0xffffffffffffffffUL;
//    return x >> 63;
//}
//// -------------------------



#define BH (BITS_PER_LONG/2)
#define CLH(v) ((v<<BH)>>BH)  // clear left half
//#define VERBOSE_TEST

void
do_some_tests(ulong v)
{
//#ifdef VERBOSE_TEST  // print to debug  ;-)
//    cout << "\n v = " << v  << hex << " = 0x" << v << dec << " = ";
//    cout << endl;
//    PP((v));
//    PP((0xffffffffffffffffUL));
//    PP((v*0xffffffffffffffffUL));
//    ulong h1 = parity(v);
//    ulong h2 = par(v);
//    PP((h1));
//    PP((h2));
//    jjassert( h1 == h2 );
//    return;
//#endif // VERBOSE_TEST

    ulong t;

#if BITS_PER_LONG == 64
    jjassert( v == butterfly_16(butterfly_16(v)) );
#endif
    jjassert( v == butterfly_8(butterfly_8(v)) );
    jjassert( v == butterfly_4(butterfly_4(v)) );
    jjassert( v == butterfly_2(butterfly_2(v)) );
    jjassert( v == butterfly_1(butterfly_1(v)) );
    jjassert( v == bit_unzip(bit_zip(v)) );

    jjassert( v == bit_inverse_gray_permute(bit_gray_permute(v)) );

    t = CLH(v);  jjassert( bit_zip(t)==bit_zip0(t) );
    t = CLH(v);  jjassert( t==bit_unzip0(bit_zip0(t)) );

    { ulong lo, hi;
        bit_zip2(v, lo, hi); t=bit_unzip2(lo, hi); jjassert( v==t );

        t = CLH(v);  t=bit_zip0(t);
        bit_zip2(v, lo, hi);
        jjassert( t==lo );
    }

    jjassert( (low_ones(v) & low_zeros(v)) == 0 );
    jjassert( (low_ones(v) | low_zeros(v)) != 0 );

    jjassert( high_ones(v) == high_zeros(~v) );
    jjassert( (high_ones(v) & high_zeros(v)) == 0 );
    jjassert( (high_ones(v) | high_zeros(v)) != 0 );

    jjassert( (highest_one_01edge(v) & highest_one_10edge(v)) == highest_one(v) ); // CHECK

    t = highest_one_01edge(v);  jjassert( bit_count(t) == bit_count_01(t) ); // CHECK

    jjassert( (lowest_one_01edge(v) & lowest_one_10edge(v)) == lowest_one(v) ); // CHECK

    jjassert( (v==0) || (lowest_one_idx(v) == (BITS_PER_LONG-1 - highest_one_idx(revbin(v)))) ); // CHECK

    jjassert( (v!=0) || (lowest_one_idx(v) == highest_one_idx(revbin(v))) ); // CHECK

    jjassert( lowest_one(v) == lowest_zero(~v) ); // CHECK
    jjassert( highest_one(v) == highest_zero(~v) ); // CHECK
    jjassert( revbin( highest_one(v) ) == lowest_one( revbin(v) ) ); // CHECK
    jjassert( revbin(v) == revbin(v, BITS_PER_LONG) ); // CHECK

    jjassert( single_values(v) == single_ones(v) | single_zeros(v) ); // CHECK
    jjassert( single_ones(v)  == single_zeros(~v) ); // CHECK
    jjassert( single_ones(~v) == single_zeros(v) ); // CHECK

    jjassert( border_ones(v) == high_border_ones(v) | low_border_ones(v) ); // CHECK
//    jjassert( border_ones(v) == border_ones(~v) );  // not true

    jjassert( block_border_ones(v) == low_block_border_ones(v) ^ high_block_border_ones(v) ); // CHECK
    jjassert( block_ones(v) == block_border_ones(v) | interior_ones(v) ); // CHECK

    jjassert( v == block_ones(v) | single_ones(v) ); // CHECK

    t = bit_count(v);
    jjassert( t == bit_count_sparse(v) ); // CHECK
    if ( t<16 )  jjassert( t == bit_count_15(v) ); // CHECK
    if ( t<4 )  jjassert( t == bit_count_3(v) ); // CHECK

//    t = CLH(v);  t = bit_count(t);
//    if ( t<16 )  jjassert( t == bc15(v) ); // CHECK

    jjassert( interior_ones(v) == (interior_ones(v) & interior_values(v)) ); // CHECK

//    jjassert( (interior_ones(v) | interior_ones(~v)) == interior_values(v) ); //  not true

    jjassert( parity(v) == (inverse_gray_code(v) & 1) ); // CHECK

    const ulong irg = inverse_rev_gray_code(v);
    const ulong ig = inverse_gray_code(v);
    const ulong igs = inverse_gray_code(v) >> 1;
    jjassert( inverse_gray_code( (v)>>1 ) == inverse_gray_code(v) >> 1 ); // CHECK
    if ( parity(v) )
    {
        jjassert( ~v == irg ^ ig ); // CHECK
        jjassert( ~irg == igs ); // CHECK
    }
    else
    {
        jjassert( v == irg ^ ig ); // CHECK
        jjassert( irg == igs ); // CHECK
    }

//    jjassert( yellow_code(v) == revbin( blue_code( revbin( v ) ) ) );
//    jjassert( red_code(v) == revbin( blue_code( v ) ) );
//    jjassert( green_code(v) == blue_code( revbin( v ) ) );

//    jjassert( (~inverse_rev_gray_code(v)) == (inverse_gray_code(v)>>1) ); // not true

    if ( v ) { jjassert( (1UL<<ld(v)) <= v ); }  else { jjassert( ld(v) == 0 ); } // CHECK

    for (ulong r=0; r<(BITS_PER_LONG); ++r)
    {
        jjassert( v==bit_rotate_right(bit_rotate_left(v,r), r) ); // CHECK
        jjassert( v==bit_rotate_right(bit_rotate_right(v,r), BITS_PER_LONG-r) ); // CHECK
        jjassert( v==bit_rotate_left(bit_rotate_left(v,r), BITS_PER_LONG-r) ); // CHECK
    }

    if ( contains_zero_byte(v) )
    {
        ulong x = v;
        for (ulong k=0; k<BYTES_PER_LONG; ++k)
        {
            str[k] = (char)(x & 0xff);
            x >>= 8;
        }
        ulong k;
        for (k=0; k<BYTES_PER_LONG; ++k)  if ( 0==str[k] )  break;
        if ( k==BYTES_PER_LONG )
        {
            cout << hex << " contains_zero_byte( 0x" << v << " )";
            cout        << "   gave  0x" << contains_zero_byte(v) << endl;
            jjassert2(0, " contains_zero_byte() failed ");
        }
    }
}
// -------------------------


int
main()
{
    cout << "Running tests for low level (bit-) functions "
#ifdef  NO_ONES_USE_ASM
        "WITHOUT asm-functions"
#else
        "with ASM-versions"
#endif
         "..." << endl;
//    cout << "RAND_MAX=" << (RAND_MAX) << endl;

    // some special values we'd like to test:

    for (ulong x=0; x<100; x++)  do_some_tests( x );

    for (ulong x=1; x; x<<=1)
    {
        do_some_tests( x );    // one bit
        do_some_tests( x-1 );  // low burst
        do_some_tests( ~(x-1) );  // high burst
    }

    do_some_tests( 0 );
    do_some_tests( ~0UL );

    ulong x = 1UL<<(BITS_PER_LONG-1);  // highest bit set
    do_some_tests( x );
    do_some_tests( x + 1 );
    do_some_tests( x - 1 );


    ulong n = 300*1000;  // this many tests with random values
    for (ulong i=0; i<n; ++i)
    {
        ulong v = (ulong)rand();
        v ^= ((ulong)rand())<<(BH); // also want high bit !=0

        do_some_tests(v);
        if ( 0==(i%1024) )  { cout << "."; cout.flush(); }
    }

    cout << "\nOK passed " << n << " tests." << endl;

    return 0;
}
// -------------------------
