/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test of basic functons (base/funcs*.hh).
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define TESTING 1
#include <vector>
#include "test.hh"
#include "base/funcs.hh"
#include "base/funcs2.hh"
#include "base/funcs_stl.hh"
#include "base/clamp.hh"
#include "base/misc.hh"

/*****************************************************************************
 * Test IfDuplicate().
 *****************************************************************************/
bool
TestIfDuplicate( void )
{
 {
    std::vector<int> v1;
    std::vector<int> v2;
    v1.push_back( 1 );
    v2.push_back( 2 );
    if ( IfDuplicate( v1, v2 ) ) return Fail();
 }

 {
    std::vector<int> v1;
    std::vector<int> v2;
    v1.push_back( 1 );
    v1.push_back( 2 );
    v2.push_back( 2 );
    v2.push_back( 3 );
    if ( not IfDuplicate( v1, v2 ) ) return Fail();
 }

    return true;
}

/*****************************************************************************
 * Test IfSameSign().
 *****************************************************************************/
bool
TestIfSameSign( void )
{
    if ( not IfSameSign( 1, 1 ) ) return Fail();
    if ( not IfSameSign( -1, -1 ) ) return Fail();
    if ( IfSameSign( 1, -1 ) ) return Fail();
    if ( IfSameSign( 0, -1 ) ) return Fail();
    if ( not IfSameSign( 0, 0 ) ) return Fail();
    return true;
}

/*****************************************************************************
 * Test NEG().
 *****************************************************************************/
bool
TestNEG( void )
{
    if ( NEG(1)  != -1 ) return Fail();
    if ( NEG(-1) != -1 ) return Fail();
    if ( NEG(0) != 0 ) return Fail();
    return true;
}

/*****************************************************************************
 * Test Min()/Max().
 *****************************************************************************/
bool
TestMinMax( void )
{
    if ( Min(10,20) != 10 ) return Fail();
    if ( Min(20,10) != 10 ) return Fail();
    if ( Min(20,-10) != -10 ) return Fail();

    if ( MIN(10,20) != 10 ) return Fail();
    if ( MIN(20,10) != 10 ) return Fail();
    if ( MIN(20,-10) != -10 ) return Fail();

    if ( Max(10,20) != 20 ) return Fail();
    if ( Max(20,10) != 20 ) return Fail();
    if ( Max(20,-10) != 20 ) return Fail();

    if ( MAX(10,20) != 20 ) return Fail();
    if ( MAX(20,10) != 20 ) return Fail();
    if ( MAX(20,-10) != 20 ) return Fail();

    return true;
}

/*****************************************************************************
 * Test IfPowerOf2().
 *****************************************************************************/
bool
TestIfPowerOf2( void )
{
    if ( IfPowerOf2(0) ) return Fail();      // zero isn't a power-of-2
    if ( not IfPowerOf2(1) ) return Fail();  // 2^0 == 1
    if ( not IfPowerOf2(2) ) return Fail();
    if ( IfPowerOf2(3) ) return Fail();
    if ( not IfPowerOf2(4) ) return Fail();
    if ( IfPowerOf2(5) ) return Fail();
    if ( not IfPowerOf2(8) ) return Fail();
    if ( not IfPowerOf2(256) ) return Fail();
    return true;
}

/*****************************************************************************
 * Test Clamp().
 *****************************************************************************/
bool
TestClamp( void )
{
    // Test template func Clamp( const SIGNED n, const SIGNED limit ).
    if ( Clamp<int>( -1, 10 ) != 0 ) return Fail();
    if ( Clamp<int>( 0, 10 ) != 0 ) return Fail();
    if ( Clamp<int>( 1, 10 ) != 1 ) return Fail();
    if ( Clamp<int>( 9, 10 ) != 9 ) return Fail();
    if ( Clamp<int>( 10, 10 ) != 10 ) return Fail();
    if ( Clamp<int>( 11, 10 ) != 10 ) return Fail();
    if ( Clamp<fp>( -1.0f, 10.0f ) != 0.0f ) return Fail();  // FEQ() unnecessary
    if ( Clamp<fp>( 0.0f, 10.0f ) != 0.0f ) return Fail();   // so fp arithmetic not done
    if ( Clamp<fp>( 5.0f, 10.0f ) != 5.0f ) return Fail();
    if ( Clamp<fp>( 9.9f, 10.0f ) != 9.9f ) return Fail();
    if ( Clamp<fp>( 10.0f, 10.0f ) != 10.0f ) return Fail();
    if ( Clamp<fp>( 10.05f, 10.0f ) != 10.0f ) return Fail();
    if ( Clamp<fp>( 10000.0f, 10.0f ) != 10.0f ) return Fail();

    // Test template func Clamp( const T n, const T low, const T high ).
    if ( Clamp<int>( 0, 50, 100 ) != 50 ) return Fail();
    if ( Clamp<int>( -999, 50, 100 ) != 50 ) return Fail();
    if ( Clamp<int>( 999, 50, 100 ) != 100 ) return Fail();
    if ( Clamp<int>( -900, -100, 100 ) != -100 ) return Fail();
    if ( Clamp<int>( 900, -100, 100 ) != 100 ) return Fail();
    if ( Clamp<uint>( 199, 200, 300 ) != 200 ) return Fail();
    if ( Clamp<uint>( 201, 200, 300 ) != 201 ) return Fail();
    if ( Clamp<uint>( 299, 200, 300 ) != 299 ) return Fail();
    if ( Clamp<uint>( 300, 200, 300 ) != 300 ) return Fail();
    if ( Clamp<uint>( 301, 200, 300 ) != 300 ) return Fail();

    // Test Clamp(int i, int limit)
    if ( Clamp(50,50) != 50 ) return Fail();
    if ( Clamp(51,50) != 50 ) return Fail();
    if ( Clamp(60,50) != 50 ) return Fail();
    if ( Clamp(-60,50) != 0 ) return Fail();
    if ( Clamp(40,50) != 40 ) return Fail();

    // Test uint8 ClampFF(uint i)
    if ( ClampFF( 1234u ) != 0xff ) return Fail();
    if ( ClampFF( 1u ) != 1 ) return Fail();
    if ( ClampFF( 0xff ) != 0xff ) return Fail();
    if ( ClampFF( 0x100 ) != 0xff ) return Fail();

    // Test uint8 ClampFF(int i).
    if ( ClampFF( -50 ) != 0x0 ) return Fail();
    if ( ClampFF( 255 ) != 0xff ) return Fail();
    if ( ClampFF( 256 ) != 0xff ) return Fail();

    // Test ClampFF(fp).
    // 0.0f : 0x00
    // 1.0f : 0xff
    if ( ClampFF( -0.05f ) != 0x0 ) return Fail();
    if ( ClampFF( 0.0f ) != 0x0 ) return Fail();
    if ( ClampFF( 0.005f ) != 0x1 ) return Fail();  // 1.275
    if ( ClampFF( 0.5f ) != 127 ) return Fail();  // 127.5
    if ( ClampFF( 0.9f ) != 229 ) return Fail();  // 229.5
    if ( ClampFF( 1.0f ) != 0xff ) return Fail();
    if ( ClampFF( 1.5f ) != 0xff ) return Fail();

    // Test Clamp1(fp).
    if ( Clamp1( -123.0f ) != 0.0f ) return Fail();
    if ( Clamp1( 1.5f ) != 1.0f ) return Fail();
    if ( Clamp1( 0.5f ) != 0.5f ) return Fail();

    return true;
}

/*****************************************************************************
 * Test SignedHexNumber2String().
 *****************************************************************************/
bool
TestSignedHexNumber2String( void )
{
#undef TEST_CASE
#define TEST_CASE( STRING, HEX_VAL )                                                \
{{                                                                                  \
    const string hexStr = SignedHexNumber2String( HEX_VAL, minWidth, format );      \
    COUT << "Should print '" STRING "' : '" << hexStr << "'" << std::endl;          \
    if ( hexStr != string(STRING) )                                                 \
        return Fail();                                                              \
}}

    {
    const eNumberFormat format = eNumberFormat_DEFAULT;
    const uint minWidth = 0;
    TEST_CASE( "0x0", 0 )
    TEST_CASE( "0xa", 10 )
    TEST_CASE( "0xf", 15 )
    TEST_CASE( "-0xf", -15 )
    TEST_CASE( "0x1234abcd", 0x1234abcd )
    }

    {
    const eNumberFormat format = eNumberFormat( int(eNumberFormat_PAD_ZEROS) | int(eNumberFormat_NO_SHOW_BASE) );
    const uint minWidth = 6;
    TEST_CASE( "0000a0", 0xa0 )
    }

    {
    const eNumberFormat format = eNumberFormat( int(eNumberFormat_PAD_ZEROS) | int(eNumberFormat_NO_SHOW_BASE) );
    const uint minWidth = 6;
    TEST_CASE( "0000a0", 0xa0 )
    }

    {
    const eNumberFormat format = eNumberFormat_PAD_ZEROS;
    const uint minWidth = 6;
    TEST_CASE( "0x00ff", 0xff )
    }

    {
    const eNumberFormat format = eNumberFormat_PAD_ZEROS;
    const uint minWidth = 4;
    TEST_CASE( "0xff", 0xff )
    }

    {
    const eNumberFormat format = eNumberFormat_NO_SHOW_BASE;
    const uint minWidth = 5;
    TEST_CASE( "    f", 0xf )
    TEST_CASE( "   ff", 0xff )
    TEST_CASE( "  fff", 0xfff )
    TEST_CASE( " ffff", 0xffff )
    TEST_CASE( "-   f", -0xf )
    TEST_CASE( "-  ff", -0xff )
    TEST_CASE( "- fff", -0xfff )
    TEST_CASE( "-ffff", -0xffff )

    TEST_CASE( "1234abcd", 0x1234abcd )  // past width
    TEST_CASE( "-1234abcd", -0x1234abcd )  // past width
    }

    {
    const eNumberFormat format = eNumberFormat_SHOW_PLUS;
    const uint minWidth = 0;
    TEST_CASE( "0x0", 0 )
    TEST_CASE( "+0x123", 0x123 )
    TEST_CASE( "-0xf", -15 )
    }

    {
    const eNumberFormat format = eNumberFormat( int(eNumberFormat_SHOW_PLUS) | int(eNumberFormat_NO_SHOW_BASE) );
    const uint minWidth = 0;
    TEST_CASE( "+123", 0x123 )
    TEST_CASE( "-f", -15 )
    }

    {
    const eNumberFormat format = eNumberFormat( int(eNumberFormat_SHOW_PLUS) | int(eNumberFormat_NO_SHOW_BASE) );
    const uint minWidth = 4;
    TEST_CASE( "   0", 0 )  // zero has no sign
    TEST_CASE( "+123", 0x123 )
    }

    {
    const eNumberFormat format = eNumberFormat( int(eNumberFormat_SHOW_PLUS) | int(eNumberFormat_NO_SHOW_BASE) );
    const uint minWidth = 2;
    TEST_CASE( "+a099", 0xa099 )  // past minWidth
    TEST_CASE( "-aaabbb", -0xaaabbb )  // past minWidth
    }

    return true;
}

/*****************************************************************************
 * main.
 *****************************************************************************/
int main( int argc, char** argv )
{
    RUN_TEST( "funcs", TEST_ERROR_MODE_ABORT,
              TestIfDuplicate()
          and TestIfSameSign()
          and TestNEG()
          and TestMinMax()
          and TestIfPowerOf2()
          and TestClamp()
          and TestSignedHexNumber2String() )
}
