/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test of Array classes.
 *//*
 * LEGAL:   COPYRIGHT (C) 2005 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/module.hh"
#include "base/array.hh"
#include "base/array_small.hh"
#include "base/array_tiny.hh"
using namespace base;
#include "math/module.hh"
#include "math/funcs.hh"
using namespace math;

volatile uint idx10000 = 10000;  // circumvent gcc array checking

/*****************************************************************************
 * Test Array class.
 *****************************************************************************/
bool
TestArray( void )
{
    CDEBUG << "============================================\n";
    CDEBUG << "Testing Array class. \n";
    CDEBUG << "============================================\n";

    Array<int> arr;
    if ( not arr.empty() ) return Fail();
    if ( arr.size() != 0 ) return Fail();

    arr.push_back( 100 );
    if ( arr.empty() ) return Fail();
    if ( arr.size() != 1 ) return Fail();
    if ( arr[0] != 100 ) return Fail();

    arr.resize( 3 );
    if ( arr.empty() ) return Fail();
    if ( arr.size() != 3 ) return Fail();
    if ( arr[0] != 100 ) return Fail();

    Array<int> arr2( 50 );
    if ( arr2.empty() ) return Fail();
    if ( arr2.size() != 50 ) return Fail();
    arr2.push_back( 123456 );
    if ( arr2[50] != 123456 ) return Fail();

    CDEBUG << "ok \n";
    return true;
}

/*****************************************************************************
 * Test TinyArray class.
 *****************************************************************************/
bool
TestTinyArray( void )
{
    CDEBUG << "============================================\n";
    CDEBUG << "Testing TinyArray class. \n";
    CDEBUG << "============================================\n";

 {
    CDEBUG << "----------- basic test TinyArray<3> --------- \n";
    TinyArray<3> a;
    a[0] = 1;
    a[1] = 2;
    a[2] = 3;
    if (    a[0] != 1
         || a[1] != 2
         || a[2] != 3 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<3,float> --------- \n";
    TinyArray<2,float> a;
    a[0] = 12.34;
    a[1] = 56.78;

    if ( not FP_EQ<double>( a[0], 12.34 )
      || not FP_EQ<double>( a[1], 56.78 ) ) return Fail();

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<>(val) constructor> --------- \n";
    TinyArray<2> a(123);        // initialize every element with same value
    if (   a[0] != 123
        || a[1] != 123 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<>() = { } constructor> --------- \n";
    int arr[3] = { 64, 32, 16 };
    TinyArray<3> a = arr;
    if (   a[0] != 64
        || a[1] != 32
        || a[2] != 16 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<> copy constructor> --------- \n";
    TinyArray<2> a1;
    a1[0] = 101;
    a1[1] = 202;
    TinyArray<2> a2 = a1;
    if (    a2[0] != 101
         || a2[1] != 202 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<>.Fill(),Count()  --------- \n";
    TinyArray<50> a(555);
    a.Fill(777);

    if ( a.GetCount() != 50 ) return Fail();
    for ( uint i = 0; i < a.GetCount(); ++i )
        if ( a[i] != 777 ) return Fail();

    CDEBUG << "sizeof( TinyArray<50> ) = " << sizeof(a) << std::endl;

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<> operator=() --------- \n";
    TinyArray<2> a1(0);
    TinyArray<2> a2(1);
    a1[0] = 112;
    a1[1] = 221;
    a2 = a1;
    if (    a2[0] != 112
         || a2[1] != 221 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<> operator==/!=()  --------- \n";
    TinyArray<2> a1(3);
    TinyArray<2> a2(4);

    a1 = a2;            // they're equal
    if ( ! (a1 == a2) ) return Fail();
    if ( a1 != a2 )  return Fail();

    a1[0] = 99; a2[0] = 100;    // now they're different
    if ( a1 == a2 ) return Fail();
    if ( ! (a1 != a2) ) return Fail();

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray<>.operator<()  --------- \n";
    TinyArray<4> a1(555), a2(555);
    ++a2[3];    // slightly larger
    if ( ! (a1 < a2) ) return Fail();
    if (   (a2 < a1) ) return Fail();

    a2[3] = a1[3];
    ++a1[0];    // slightly larger
    if (   (a1 < a2 ) ) return Fail();
    if ( ! (a2 < a1 ) ) return Fail();

    a1 = a2;    // equal
    if (   (a1 < a2 ) ) return Fail();
    if (   (a2 < a1 ) ) return Fail();

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test TinyArray invalid index  --------- \n";
    TinyArray<10> ar;

    ar[0] = 1;  // this assignment fools gcc 4.4 uninitialized warning
    int idx = 0;
    idx += idx10000;

    try
    {
      //if ( ar[10000] )  // gcc will catch this
        if ( ar[idx] )
            return Fail();  // fail in either case
        else
            return Fail();

        if ( ar[uint(-1)] )
            return Fail();  // fail in either case
        else
            return Fail();
    }
    catch (...)
    {
        CDEBUG << "caught exception (expected)" << std::endl;
    }
 }

    return true;
}

/*****************************************************************************
 * Test SmallArray class.
 *****************************************************************************/
bool
TestSmallArray( void )
{
    CDEBUG << "============================================\n";
    CDEBUG << "Testing SmallArray class. \n";
    CDEBUG << "============================================\n";

 {
    CDEBUG << "----------- test SmallArray<> --------- \n";

    // Remember that SmallArray is constructed with a fixed-size (template arg),
    // but it has a variable-size which is initially zero.

    SmallArray<10,int> a1;                // 10 is its FIXED-SIZE
    if ( not a1.empty() ) return Fail();  // yes, it's VARIABLE-SIZE should be empty
    if ( a1.size() != 0 ) return Fail();

    a1.resize( 2 );
    if ( a1.empty() ) return Fail();
    if ( a1.size() != 2 ) return Fail();

    a1[0] = 123;
    a1[1] = 456;
    a1.resize( 4 );  // resize() shouldn't destroy/corrupt existing elements
    if ( a1[0] != 123 ) return Fail();
    if ( a1[1] != 456 ) return Fail();

    a1.clear();
    if ( not a1.empty() ) return Fail();
    if ( a1.size() != 0 ) return Fail();

    CDEBUG << "ok" << std::endl;
 }

 {
    CDEBUG << "----------- test SmallArray<> error cases --------- \n";
    SmallArray<256,long> a1;
    try
    {
        a1[0] = 123;  // invalid index: array's variable-size is zero!
    }
    catch (...)
    {
        CDEBUG << "caught exception (expected)" << std::endl;
    }

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test SmallArray<> operator=() --------- \n";
    SmallArray<2,long> a1( 2 );  // idiom: fixed-size = variable-size
    SmallArray<2,long> a2( 2 );
    a1[0] = 112;
    a1[1] = 221;
    a2 = a1;
    if ( a2[0] != 112
      || a2[1] != 221 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test SmallArray<> operator=() (different var-size) --------- \n";
    SmallArray<3,long> a1;
    SmallArray<3,long> a2;
    a1.push_back( 1000 );  // a1 has 2 elems, a2 is empty
    a1.push_back( 1001 );
    a2 = a1;               // copy
    if ( a1.size() != 2 ) return Fail();
    if ( a2.size() != 2 ) return Fail();
    if ( not (a1 == a2 ) ) return Fail();
    if ( a2[0] != 1000
      || a2[1] != 1001 ) return Fail();
    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test SmallArray<> operator==/!=()  --------- \n";
    SmallArray<2,short> a1( 2 );
    SmallArray<2,short> a2( 2 );

    a1[0] = a2[0] = 123;
    a1[1] = a2[1] = 456;

    a1 = a2;            // they're equal
    if ( ! (a1 == a2) ) return Fail();
    if ( a1 != a2 )  return Fail();

    ++a2[0];            // they're different
    if ( a1 == a2 ) return Fail();
    if ( ! (a1 != a2) ) return Fail();

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test SmallArray<>.operator<()  --------- \n";

    SmallArray<2,short> a1( 2 );
    SmallArray<2,short> a2( 2 );

    a1[0] = 100;
    a1[1] = 100;
    a2[0] = 100;
    a2[1] = 100;  // they're equal
    if ( a1 < a2 ) return Fail();
    if ( a2 < a1 ) return Fail();

    a1[0] =  99;
    a1[1] = 100;
    a2[0] = 100;
    a2[1] = 100;  // a1 is less
    if ( not (a1 < a2) ) return Fail();
    if ( a2 < a1 ) return Fail();

    a1[0] = 100;
    a1[1] =  99;
    a2[0] = 100;
    a2[1] = 100;  // a1 is less
    if ( not (a1 < a2) ) return Fail();
    if ( a2 < a1 ) return Fail();

    CDEBUG << "ok \n";
 }

 {
    CDEBUG << "----------- test SmallArray STL methods  --------- \n";
    SmallArray<100,int> ar;
    ar.push_back( -1 );
    ar.push_back( -2 );
    if ( ar.size() != 2 ) return Fail();
    if ( ar[0] != -1 ) return Fail();
    if ( ar[1] != -2 ) return Fail();
    CDEBUG << "ok \n";
 }

    return true;
}

/*****************************************************************************
 * main.
 *****************************************************************************/
int main( int argc, char** argv )
{
    RUN_TEST( "arrays", TEST_ERROR_MODE_CATCH,  // this test actually expects an exception
              TestArray()
          and TestTinyArray()
          and TestSmallArray() )
}
