/*
 *  This file is a part of TiledArray.
 *  Copyright (C) 2013  Virginia Tech
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "TiledArray/tiled_range.h"
#include "tiledarray.h"
#include "unit_test_config.h"
#include "range_fixture.h"

using namespace TiledArray;

BOOST_FIXTURE_TEST_SUITE( tiled_range_suite, TiledRangeFixture )

BOOST_AUTO_TEST_CASE( accessor )
{
  BOOST_CHECK_EQUAL(tr.tiles_range(), tiles_range);
  BOOST_CHECK_EQUAL(tr.elements_range(), elements_range);
}

BOOST_AUTO_TEST_CASE( constructor )
{
  // check default constructor
  {
    BOOST_REQUIRE_NO_THROW(TiledRange r0);
    TiledRange r0;
    std::vector<std::size_t> s0(3,0);
    BOOST_CHECK(r0.tiles_range().extent_data() == nullptr);
    BOOST_CHECK(r0.elements_range().extent_data() == nullptr);
  }


  // check ranges constructor
  {
    BOOST_REQUIRE_NO_THROW(TiledRange r1(dims.begin(), dims.end()));
    TiledRange r1(dims.begin(), dims.end());
    BOOST_CHECK_EQUAL(r1.tiles_range(), tiles_range);
    BOOST_CHECK_EQUAL(r1.elements_range(), elements_range);
  }

  // check initializer list of initializer list constructor
  {
    TiledRange r1 { {0,2,5,10,17,28},
                    {0,2,5,10,17,28},
                    {0,2,5,10,17,28} };
    BOOST_CHECK_EQUAL(r1.tiles_range(), tiles_range);
    BOOST_CHECK_EQUAL(r1.elements_range(), elements_range);
  }

  // check copy constructor
  {
    BOOST_REQUIRE_NO_THROW(TiledRange r4(tr));
    TiledRange r4(tr);
    BOOST_CHECK_EQUAL(r4.tiles_range(), tr.tiles_range());
    BOOST_CHECK_EQUAL(r4.elements_range(), tr.elements_range());
  }
}

BOOST_AUTO_TEST_CASE( ostream )
{

  std::stringstream stm;
  stm << "( tiles = " << tr.tiles_range() <<
      ", elements = " << tr.elements_range() << " )";

  boost::test_tools::output_test_stream output;
  output << tr;
  BOOST_CHECK( !output.is_empty( false ) );
  BOOST_CHECK( output.check_length( stm.str().size(), false ) );
  BOOST_CHECK( output.is_equal( stm.str().c_str() ) );
}

BOOST_AUTO_TEST_CASE( comparison ) {
  TiledRange r1{{ 0, 2, 4, 6, 8, 10 },
                { 0, 2, 4, 6, 8, 10 }};
  TiledRange r2{{ 0, 2, 4, 6, 8, 10 },
                { 0, 2, 4, 6, 8, 10 }};
  TiledRange r3{{ 0, 3, 6, 9, 12, 15 },
                { 0, 3, 6, 9, 12, 15 }};
  BOOST_CHECK(r1 == r2);     // check equality operator
  BOOST_CHECK(! (r1 != r2)); // check not-equal operator
  BOOST_CHECK(! (r1 == r3)); // check for inequality with different number of tiles.
  BOOST_CHECK(r1 != r3);
}

BOOST_AUTO_TEST_CASE( assignment )
{
  TiledRange r1;

  // verify they are not equal before assignment.
  BOOST_CHECK_NE(r1, tr);

  // check that assignment returns itself.
  BOOST_CHECK_EQUAL((r1 = tr), tr);

  // check that assignment is valid.
  BOOST_CHECK_EQUAL(r1, tr);
}

BOOST_AUTO_TEST_CASE( permutation )
{
  Permutation p({2,0,1});
  TiledRange r1 = p * tr;
  BOOST_CHECK_EQUAL(r1.tiles_range(), p * tr.tiles_range()); // check that tile data was permuted properly.
  BOOST_CHECK_EQUAL(r1.elements_range(), p * tr.elements_range()); // check that element data was permuted properly.

  TiledRange r2(tr);
  BOOST_CHECK_EQUAL((r2 *= p), r1); // check that permutation returns itself.
  BOOST_CHECK_EQUAL(r2, r1);// check that the permutation was assigned correctly.
}

BOOST_AUTO_TEST_CASE( make_tiles_range )
{
  tile_index start(GlobalFixture::dim);
  tile_index finish(GlobalFixture::dim);

  // iterate over all the tile indexes in the tiled range.
  TiledRange::size_type i = 0;
  for(Range::const_iterator it = tr.tiles_range().begin(); it != tr.tiles_range().end(); ++it, ++i) {
    // get the start and finish indexes of the current range.
    for(unsigned int d = 0; d < GlobalFixture::dim; ++d) {
      start[d] = a[ (*it)[d] ];
      finish[d] = a[ (*it)[d] + 1 ];
    }

    // construct a range object that should match the range constructed by TiledRange.
    TiledRange::tiles_range_type range(start, finish);

    // Get the two ranges to be tested.
    TiledRange::tiles_range_type range_index = tr.make_tile_range(*it);
    TiledRange::tiles_range_type range_ordinal = tr.make_tile_range(i);

    BOOST_CHECK_EQUAL(range_index, range);
    BOOST_CHECK_EQUAL(range_ordinal, range);
  }
}

BOOST_AUTO_TEST_SUITE_END()

