/** @file  tst_psBitSet_01.c
 *
 *  @brief Test driver for psBitSet functions
 *
 *  This test driver contains the following tests for psBitSet test point 1:
 *     A)  Create psBitSet
 *     B)  Set bits
 *     C)  Test bits
 *     D)  Attempt to test negative bit
 *     E)  Attempt to test bit to large
 *     F)  Attempt to test bit in null BitSet
 *     G)  Attempt to set negative bit
 *     H)  Attempt to set bit to large
 *     I)  Attempt to set bit in null BitSet
 *     J)  Free psBitSet
 *
 *  @author  Ross Harman, MHPCC
 *
 *  @version $Revision: 1.3 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2007-02-07 23:52:54 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include "pslib_strict.h"
#include "psTest.h"

static psS32 testBitSet01a(void);
static psS32 testBitSet01b(void);
static psS32 testBitSet01c(void);
static psS32 testBitSet02(void);
static psS32 testBitSet03(void);
static psS32 testBitSet04(void);
static psS32 testBitSet05(void);
static psS32 testBitSet06(void);

testDescription tests[] = {
                              {testBitSet01a, 1, "psBitSetAlloc", 0, false},
                              {testBitSet01b, 2, "psBitSetSet/psBitSetClear", 0, false},
                              {testBitSet01c, 3, "psBitSetTest", 0, false},
                              {testBitSet06, 4, "psBitSetOp", 0, false},
                              {testBitSet02, 5, "psBitSetOp AND Operator", 0, false},
                              {testBitSet03, 6, "psBitSetOp OR Operator", 0, false},
                              {testBitSet04, 7, "psBitSetOp XOR Operator", 0, false},
                              {testBitSet05, 8, "psBitSetNot", 0, false},

                              {NULL}
                          };


psS32 main( psS32 argc, char* argv[] )
{
    psLogSetLevel( PS_LOG_INFO );

    return ( ! runTestSuite( stderr, "psBitSet", tests, argc, argv ) );
}

psS32 testBitSet01a(void)
{
    psErr* err;

    // Test A - Create psBitSet
    fprintf(stderr,"Creating psBitSet with 24 bits...\n");
    psBitSet* bs = psBitSetAlloc(24);
    if (bs == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Failed to create a psBitSet");
        return 1;
    }
    if (bs->n != 3) {
        psError(PS_ERR_UNKNOWN,true,"Number of bytes for psBitSet incorrect (%ld vs 3)",
                bs->n);
        return 2;
    }
    if (bs->bits[0] != 0 || bs->bits[1] != 0 || bs->bits[2] != 0) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc didn't clean ou the bits by default (%x%x%x).",
                bs->bits[2],bs->bits[1],bs->bits[0]);
        return 3;
    }
    psFree(bs);

    // Test A - Create psBitSet
    fprintf(stderr,"Creating psBitSet with 25 bits...\n");
    bs = psBitSetAlloc(25);
    if (bs == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Failed to create a psBitSet with 25 bits");
        return 4;
    }
    if (bs->n != 4) {
        psError(PS_ERR_UNKNOWN,true,"Number of bytes for psBitSet incorrect (%ld vs 4)",bs->n);
        return 5;
    }
    psFree(bs);

    psErrorClear();
    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
    bs = psBitSetAlloc(-4);
    if (bs != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc returned something in case of a negative size");
        return 6;
    }
    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psError(PS_ERR_UNKNOWN,true,"psBitSetAlloc didn't generate expected error with size = -4");
        return 7;
    }
    psFree(err);

    return 0;
}

psS32 testBitSet01b(void)
{
    char *binOut = NULL;
    psBitSet *tempBs = NULL;
    psErr* err = NULL;

    psBitSet* bs = psBitSetAlloc(24);

    if (psBitSetTest(bs,0) ||
            psBitSetTest(bs,2) ||
            psBitSetTest(bs,23) ) {

        psAbort("psBitSetAlloc failed to clear all bits at allocation.");
    }

    // Test B - Set bits
    tempBs = bs;
    fprintf(stderr,"Setting first bit...\n");
    bs = psBitSetSet(bs, 0);
    fprintf(stderr,"Setting third bit...\n");
    bs = psBitSetSet(bs, 2);
    fprintf(stderr,"Setting last bit...\n");
    bs = psBitSetSet(bs, 23);
    if(bs != tempBs) {
        psAbort(                "Return pointer not equal to output argument pointer.");
    }
    if(bs->bits[0] != 0x05) {
        psAbort(                "Unexpected value for first byte (%d vs 5).",
                bs->bits[0]);
    }


    binOut = psBitSetToString(bs);
    fprintf(stderr,"%s\n\n", binOut);
    psFree(binOut);

    // Test C - Test bits
    if (! psBitSetTest(bs,0) ||
            ! psBitSetTest(bs,2) ||
            ! psBitSetTest(bs,23) ) {

        psAbort("Failed to set a bit.");
    }

    fprintf(stderr,"Clearing first bit...\n");
    bs = psBitSetClear(bs, 0);
    fprintf(stderr,"Clearing third bit...\n");
    bs = psBitSetClear(bs, 2);
    fprintf(stderr,"Clearing last bit...\n");
    bs = psBitSetClear(bs, 23);

    binOut = psBitSetToString(bs);
    fprintf(stderr,"%s\n\n", binOut);
    psFree(binOut);


    if (psBitSetTest(bs,0) ||
            psBitSetTest(bs,2) ||
            psBitSetTest(bs,23) ) {

        psAbort("Failed to clear a bit.");
    }

    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
    if(psBitSetClear(NULL,2) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetClear did not return NULL with NULL bitset");
        return 20;
    }

    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
    if(psBitSetClear(bs,-3) != bs) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetClear did not return original bitset");
        return 21;
    }

    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    psBitSetSet(bs, -4);

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetSet(bs, -4) didn't generate expected error.\n");
    }
    psFree(err);

    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    psBitSetSet(bs, 200);

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetSet(bs, 200) didn't generate expected error.\n");
    }
    psFree(err);

    psLogMsg("testBitSet01b",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    psBitSetSet(NULL, 0);

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetSet(NULL,0) didn't generate expected error.\n");
    }
    psFree(err);

    psFree(bs);

    return 0;
}

static psS32 testBitSet01c(void)
{
    psBitSet* bs = psBitSetAlloc(24);
    psErr* err = NULL;

    fprintf(stderr,"Setting first bit...\n");
    bs = psBitSetSet(bs, 0);
    fprintf(stderr,"Setting third bit...\n");
    bs = psBitSetSet(bs, 2);
    fprintf(stderr,"Setting last bit...\n");
    bs = psBitSetSet(bs, 23);

    if (! psBitSetTest(bs,0) ||
            ! psBitSetTest(bs,2) ||
            ! psBitSetTest(bs,23) ) {

        psAbort("Set bits returned false.");
    }

    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    if(psBitSetTest(bs, -4)) {
        psAbort("psBitSetTest returned true with negative bit position.\n");
    }

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetTest(bs, -4) didn't generate proper error.\n");
    }
    psFree(err);

    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    if(psBitSetTest(bs, 200)) {
        psAbort("psBitSetTest returned true with too-large bit position.\n");
    }

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetTest(bs, 200) didn't generate proper error.\n");
    }
    psFree(err);

    psLogMsg("testBitSet01c",PS_LOG_INFO,"Following should be an error");
    psErrorClear();
    if (psBitSetTest(NULL, 0)) {
        psAbort("psBitSetTest returned true with NULL psBitSet.\n");
    }

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_NULL) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetTest(NULL, 0) didn't generate proper error.\n");
    }
    psFree(err);

    psFree(bs);

    return 0;
}

psS32 testBitSet02()
{
    char *binOut1 = NULL;
    char *binOut2 = NULL;
    char *binOut3 = NULL;

    psBitSet* bs1 = psBitSetAlloc(24);
    psBitSet* bs2 = psBitSetAlloc(24);
    psBitSet* and = psBitSetAlloc(24);
    for(psS32 i=0; i<24; i++) {
        if ((i & 2) == 0) {
            bs1 = psBitSetSet(bs1, i);
        }
        if ((i & 1) == 0) {
            bs2 = psBitSetSet(bs2, i);
        }
        if (((i & 1) == 0) && ((i & 2) == 0) ) {
            and = psBitSetSet(and, i);
        }
    }
    binOut1 = psBitSetToString(bs1);
    binOut2 = psBitSetToString(bs2);
    binOut3 = psBitSetToString(and);
    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
            binOut1,binOut2,binOut3);
    psFree(binOut1);
    psFree(binOut2);
    psFree(binOut3);

    // Test B - Perform binary AND with psBitSets
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary AND with psBitSets");
    psBitSet* outbs = psBitSetAlloc(24);
    outbs = psBitSetOp(outbs, bs1, "AND", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp returned a NULL result for AND operation");
    }

    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(and,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with AND operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);

    // Test C - Perform binary AND and auto allocate output
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary AND and auto allocate output");
    outbs = psBitSetOp(NULL, bs1, "AND", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp failed to create a new psBitSet for the result");
    }
    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(and,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with AND operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);

    psFree(bs1);
    psFree(bs2);
    psFree(and);

    return 0;
}

static psS32 testBitSet03(void)
{
    char *binOut1 = NULL;
    char *binOut2 = NULL;
    char *binOut3 = NULL;

    psBitSet* bs1 = psBitSetAlloc(24);
    psBitSet* bs2 = psBitSetAlloc(24);
    psBitSet* or = psBitSetAlloc(24);
    for(psS32 i=0; i<24; i++) {
        if ((i/2) % 2) {
            bs1 = psBitSetSet(bs1, i);
        }
        if ((i) % 2) {
            bs2 = psBitSetSet(bs2, i);
        }
        if ( ((i/2) % 2) || ((i) % 2) ) {
            or = psBitSetSet(or, i);
        }
    }

    binOut1 = psBitSetToString(bs1);
    binOut2 = psBitSetToString(bs2);
    binOut3 = psBitSetToString(or);
    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
            binOut1,binOut2,binOut3);
    psFree(binOut1);
    psFree(binOut2);
    psFree(binOut3);

    // Test B - Perform binary AND with psBitSets
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary OR with psBitSets");
    psBitSet* outbs = psBitSetAlloc(24);
    outbs = psBitSetOp(outbs, bs1, "OR", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp returned a NULL result for OR operation");
    }

    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(or,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with OR operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);

    // Test C - Perform binary AND and auto allocate output
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary OR and auto allocate output");
    outbs = psBitSetOp(NULL, bs1, "OR", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp failed to create a new psBitSet for the result");
    }
    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(or,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with OR operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);
    outbs = NULL;

    psFree(bs1);
    psFree(bs2);
    psFree(or);

    return 0;
}

static psS32 testBitSet04(void)
{
    char *binOut1 = NULL;
    char *binOut2 = NULL;
    char *binOut3 = NULL;

    psBitSet* bs1 = psBitSetAlloc(24);
    psBitSet* bs2 = psBitSetAlloc(24);
    psBitSet* xor = psBitSetAlloc(24);
    for(psS32 i=0; i<24; i++) {
        if ((i/2) % 2) {
            bs1 = psBitSetSet(bs1, i);
        }
        if ((i) % 2) {
            bs2 = psBitSetSet(bs2, i);
        }
        if ( ((i/2) % 2) != ((i) % 2) ) {
            xor = psBitSetSet(xor, i);
        }
    }

    binOut1 = psBitSetToString(bs1);
    binOut2 = psBitSetToString(bs2);
    binOut3 = psBitSetToString(xor);
    fprintf(stderr,"psBitSetOp input is %s, %s.  Truth is %s.\n",
            binOut1,binOut2,binOut3);
    psFree(binOut1);
    psFree(binOut2);
    psFree(binOut3);

    // Test B - Perform binary AND with psBitSets
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary XOR with psBitSets");
    psBitSet* outbs = psBitSetAlloc(24);
    outbs = psBitSetOp(outbs, bs1, "XOR", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp returned a NULL result for XOR operation");
    }

    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(xor,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with XOR operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);

    // Test C - Perform binary AND and auto allocate output
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary XOR and auto allocate output");
    outbs = psBitSetOp(NULL, bs1, "XOR", bs2);
    if (outbs == NULL) {
        psAbort("psBitSetOp failed to create a new psBitSet for the result");
    }
    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(xor,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(bs2);
            binOut3 = psBitSetToString(outbs);
            psAbort("psBitSetOp with XOR operator failed.\nInput was %s, %s.  Output was %s",
                    binOut1,binOut2,binOut3);
        }
    }
    psFree(outbs);
    outbs = NULL;

    psFree(bs1);
    psFree(bs2);
    psFree(xor);

    return 0;
}

static psS32 testBitSet05(void)
{
    char *binOut1 = NULL;
    char *binOut2 = NULL;

    psBitSet* bs1 = psBitSetAlloc(24);
    psBitSet* not = psBitSetAlloc(24);
    for(psS32 i=0; i<24; i++) {
        if (i % 2) {
            bs1 = psBitSetSet(bs1, i);
        }
        if (i % 2 == 0) {
            not = psBitSetSet(not, i);
        }
    }

    binOut1 = psBitSetToString(bs1);
    binOut2 = psBitSetToString(not);
    fprintf(stderr,"psBitSetOp input is %s.  Truth is %s.\n",
            binOut1,binOut2);
    psFree(binOut1);
    psFree(binOut2);

    // Test B - Perform binary AND with psBitSets
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary NOT with psBitSets");
    psBitSet* outbs = psBitSetAlloc(24);
    outbs = psBitSetNot(outbs, bs1);
    if (outbs == NULL) {
        psAbort("psBitSetOp returned a NULL result for NOT operation");
    }

    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(not,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(outbs);
            psAbort("psBitSetOp with NOT operator failed.\nInput was %s.  Output was %s",
                    binOut1,binOut2);
        }
    }
    psFree(outbs);

    // Test C - Perform binary AND and auto allocate output
    psLogMsg(__func__,PS_LOG_INFO,"Perform binary NOT and auto allocate output");
    outbs = psBitSetNot(NULL, bs1);
    if (outbs == NULL) {
        psAbort("psBitSetOp failed to create a new psBitSet for the result");
    }
    for(psS32 i=0; i<24; i++) {
        psBool truth = psBitSetTest(not,i);
        psBool res = psBitSetTest(outbs,i);
        if ( res != truth) {
            binOut1 = psBitSetToString(bs1);
            binOut2 = psBitSetToString(outbs);
            psAbort("psBitSetOp with NOT operator failed.\nInput was %s.  Output was %s",
                    binOut1,binOut2);
        }
    }
    psFree(outbs);
    outbs = NULL;

    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
    if(psBitSetNot(NULL,NULL) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetNot did not return NULL with NULL input");
        return 30;
    }

    psFree(bs1);
    psFree(not);

    return 0;
}

static psS32 testBitSet06(void)
{
    psErr* err;

    psBitSet* bs1 = psBitSetAlloc(24);
    psBitSet* bs2 = psBitSetAlloc(40);

    psLogMsg(__func__,PS_LOG_INFO,"Following should be an error");
    psBitSet* outbs = psBitSetOp(NULL, bs1, "XOR", bs2);
    if (outbs != NULL) {
        psAbort("psBitSetOp did not return a NULL result when input sizes differ");
    }

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_SIZE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetOp didn't generate expected error with operands' sizes differed.");
    }
    psFree(err);
    psFree(bs1);
    psFree(bs2);

    bs1 = psBitSetAlloc(24);
    bs2 = psBitSetAlloc(24);

    outbs = psBitSetAlloc(40);
    psBitSet* outbs2 = psBitSetOp(outbs, bs1, "XOR", bs2);
    if (outbs2 == NULL) {
        psAbort("psBitSetOp failed when input size and output size differed (a recoverable error).");
    }
    if (outbs2 != outbs) {
        psAbort("psBitSetOp didn't reuse the given output struct.");
    }
    if (outbs2->n != bs1->n) {
        psAbort("psBitSetOp did properly adjust the output psBitSet size.");
    }

    psErrorClear();
    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
    outbs = psBitSetOp(outbs, bs1, "FOO", bs2);
    if (outbs != NULL) {
        psAbort("psBitSetOp returned something in case of a bogus operation.");
    }
    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetOp didn't generate expected error with bogus operator.");
    }
    psFree(err);


    // try again, though give a valid output bitset -- should free the out upon error to avoid leak.
    outbs = psBitSetAlloc(24);
    psErrorClear();
    psLogMsg(__func__,PS_LOG_INFO,"Following is an error.");
    outbs = psBitSetOp(outbs, bs1, "FOO", bs2);

    err = psErrorLast();
    if (err->code != PS_ERR_BAD_PARAMETER_VALUE) {
        psErrorStackPrint(stderr,"Error Stack:");
        psAbort("psBitSetOp didn't generate expected error with bogus operator.");
    }
    psFree(err);


    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
    if(psBitSetOp(outbs,NULL,"AND",bs2) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL input 1 bit set");
        return 40;
    }
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
    if(psBitSetOp(outbs,bs1,NULL,bs2) != NULL) {
        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL operator");
        return 41;
    }
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error");
    if(psBitSetOp(outbs,bs1,"AND",NULL) != NULL)  {
        psError(PS_ERR_UNKNOWN,true,"psBitSetOp did not return NULL with NULL input 2 bit set");
        return 42;
    }

    psFree(bs1);
    psFree(bs2);
    psFree(outbs);

    return 0;
}
