/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder 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 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder 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 MoleCuilder.  If not, see .
 */
/*
 * LinkedCellUnitTest.cpp
 *
 *  Created on: Apr 9, 2010
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
using namespace std;
#include 
#include 
#include 
#include 
#include 
#include 
#include "Atom/atom.hpp"
#include "CodePatterns/Assert.hpp"
#include "Descriptors/MoleculeDescriptor.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "LinkedCell/linkedcell.hpp"
#include "LinkedCell/PointCloudAdaptor.hpp"
#include "molecule.hpp"
#include "World.hpp"
#include "linkedcellUnitTest.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( linkedcelltest );
void linkedcelltest::setUp()
{
  // failing asserts should be thrown
  ASSERT_DO(Assert::Throw);
  atom *Walker = NULL;
  // construct element
  hydrogen = World::getInstance().getPeriode()->FindElement(1);
  CPPUNIT_ASSERT(hydrogen != NULL && "could not find element hydrogen");
  // construct molecule (water molecule)
  TestMolecule = World::getInstance().createMolecule();
  CPPUNIT_ASSERT(TestMolecule != NULL && "could not create molecule");
  for (double x=0.5;x<3;x+=1.)
    for (double y=0.5;y<3;y+=1.)
      for (double z=0.5;z<3;z+=1.) {
        Walker = World::getInstance().createAtom();
        CPPUNIT_ASSERT(Walker != NULL && "could not create atom");
        Walker->setType(hydrogen);
        Walker->setPosition(Vector(x, y, z ));
        TestMolecule->AddAtom(Walker);
      }
  // construct linked cell
  PointCloudAdaptor cloud(TestMolecule, TestMolecule->name);
  LC = new LinkedCell_deprecated (cloud, 1.);
  CPPUNIT_ASSERT(LC != NULL && "could not create LinkedCell");
  // check that TestMolecule was correctly constructed
  CPPUNIT_ASSERT_EQUAL( TestMolecule->getAtomCount(), 3*3*3 );
};
void linkedcelltest::tearDown()
{
  delete(LC);
  World::purgeInstance();
};
/** UnitTest for LinkedCell_deprecated::CheckBounds().
 */
void linkedcelltest::CheckBoundsTest()
{
  // check for within bounds
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  CPPUNIT_ASSERT_EQUAL( true, LC->CheckBounds() );
  LC->n[0] = LC->n[1] = LC->n[2] = 1;
  CPPUNIT_ASSERT_EQUAL( true, LC->CheckBounds() );
  LC->n[0] = LC->n[1] = LC->n[2] = 2;
  CPPUNIT_ASSERT_EQUAL( true, LC->CheckBounds() );
  // check for out of bounds
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  LC->n[0] = 404040;
  CPPUNIT_ASSERT_EQUAL( false, LC->CheckBounds() );
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  LC->n[0] = 0;
  LC->n[1] = 5000;
  CPPUNIT_ASSERT_EQUAL( false, LC->CheckBounds() );
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  LC->n[1] = 0;
  LC->n[2] = -70;
  CPPUNIT_ASSERT_EQUAL( false, LC->CheckBounds() );
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  LC->n[0] = LC->n[1] = LC->n[2] = 3;
  CPPUNIT_ASSERT_EQUAL( false, LC->CheckBounds() );
};
/** UnitTest for LinkedCell_deprecated::GetCurrentCell().
 * Note that CheckBounds() is used and has to be tested already.
 */
void linkedcelltest::GetCurrentCellTest()
{
  // within bounds
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[0 * 3*3 + 0 * 3 + 0], LC->GetCurrentCell() );
  LC->n[0] = LC->n[1] = LC->n[2] = 1;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[1 * 3*3 + 1 * 3 + 1], LC->GetCurrentCell() );
  LC->n[0] = LC->n[1] = LC->n[2] = 2;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[2 * 3*3 + 2 * 3 + 2], LC->GetCurrentCell() );
  // out of bounds
  LC->n[0] = LC->n[1] = LC->n[2] = 3;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetCurrentCell() );
  LC->n[0] = LC->n[1] = LC->n[2] = -1;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetCurrentCell() );
};
/** UnitTest for LinkedCell_deprecated::GetRelativeToCurrentCell().
 */
void linkedcelltest::GetRelativeToCurrentCellTest()
{
  int offset[3];
  // offset to (0,0,0) always
  offset[0] = offset[1] = offset[2] = 0;
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[0], LC->GetRelativeToCurrentCell(offset) );
  offset[0] = offset[1] = offset[2] = -1;
  LC->n[0] = LC->n[1] = LC->n[2] = 1;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[0], LC->GetRelativeToCurrentCell(offset) );
  offset[0] = offset[1] = offset[2] = -2;
  LC->n[0] = LC->n[1] = LC->n[2] = 2;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[0], LC->GetRelativeToCurrentCell(offset) );
  // offset to (0,0,0) - 1.*(x/y/z) out of bounds
  offset[0] = offset[1] = offset[2] = 0;
  offset[0] = -1;
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
  offset[0] = offset[1] = offset[2] = 0;
  offset[1] = -1;
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
  offset[0] = offset[1] = offset[2] = 0;
  offset[2] = -1;
  LC->n[0] = LC->n[1] = LC->n[2] = 0;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
  // out of bounds
  offset[0] = offset[1] = offset[2] = -5054932;
  LC->n[0] = LC->n[1] = LC->n[2] = 1;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
  offset[0] = offset[1] = offset[2] = 192345;
  LC->n[0] = LC->n[1] = LC->n[2] = 1;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
  // index is out of bounds, offset points within
  offset[0] = offset[1] = offset[2] = -2;
  LC->n[0] = LC->n[1] = LC->n[2] = 4;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)&LC->LC[2 * 3*3 + 2 * 3 + 2], LC->GetRelativeToCurrentCell(offset) );
  // index is within bounds, offset points out
  offset[0] = offset[1] = offset[2] = 2;
  LC->n[0] = LC->n[1] = LC->n[2] = 2;
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( (const TesselPointSTLList*)NULL, LC->GetRelativeToCurrentCell(offset) );
};
/** UnitTest for LinkedCell_deprecated::SetIndexToNode().
 */
void linkedcelltest::SetIndexToNodeTest()
{
  // check all atoms
  for(molecule::iterator iter = TestMolecule->begin(); iter != TestMolecule->end();++iter){
    CPPUNIT_ASSERT_EQUAL( true, LC->SetIndexToNode(*iter) );
  }
  // check internal vectors, returns false, because this atom is not in LC-list!
  atom *newAtom = World::getInstance().createAtom();
  newAtom->setName("test");
  newAtom->setPosition(Vector(1,1,1));
  CPPUNIT_ASSERT_EQUAL( false, LC->SetIndexToNode(newAtom) );
  World::getInstance().destroyAtom(newAtom);
  // check out of bounds vectors
  newAtom = World::getInstance().createAtom();
  newAtom->setName("test");
  newAtom->setPosition(Vector(0,-1,0));
  CPPUNIT_ASSERT_EQUAL( false, LC->SetIndexToNode(newAtom) );
  World::getInstance().destroyAtom(newAtom);
};
/** UnitTest for LinkedCell_deprecated::SetIndexToVector().
 */
void linkedcelltest::SetIndexToVectorTest()
{
  Vector tester;
  // check center of each cell
  for (double x=0.5;x<3;x+=1.)
    for (double y=0.5;y<3;y+=1.)
      for (double z=0.5;z<3;z+=1.) {
        tester = Vector(x,y,z);
        CPPUNIT_ASSERT_EQUAL( true, LC->SetIndexToVector(tester) );
      }
  // check corners of each cell
  for (double x=1.;x<4;x+=1.)
    for (double y=1.;y<4;y+=1.)
      for (double z=1.;z<4;z+=1.) {
        tester= Vector(x,y,z);
        cout << "Tester is at " << tester << "." << endl;
        CPPUNIT_ASSERT_EQUAL( true, LC->SetIndexToVector(tester) );
      }
  // check out of bounds
  for (double x=0.5-1e-10;x<5;x+=3.1)
    for (double y=0.5-1e-10;y<5;y+=3.1)
      for (double z=0.5-1e-10;z<5;z+=3.1) {
        tester = Vector(x,y,z);
        cout << "The following test is supposed to fail and produce an ERROR." << endl;
        CPPUNIT_ASSERT_EQUAL( false, LC->SetIndexToVector(tester) );
      }
  // check nonsense vectors
  tester= Vector(-423598,3245978,29349);
  cout << "The following test is supposed to fail and produce an ERROR." << endl;
  CPPUNIT_ASSERT_EQUAL( false, LC->SetIndexToVector(tester) );
};
/** UnitTest for LinkedCell_deprecated::GetNeighbourBounds().
 */
void linkedcelltest::GetNeighbourBoundsTest()
{
  Vector tester;
  int lower[NDIM], upper[NDIM];
  tester= Vector(0.5,0.5,0.5);
  LC->SetIndexToVector(tester);
  LC->GetNeighbourBounds(lower, upper);
  for (int i=0;iSetIndexToVector(tester) );
  ListOfPoints = LC->GetallNeighbours();
  size = ListOfPoints->size();
  CPPUNIT_ASSERT_EQUAL( (size_t)27, size );
  for(molecule::iterator iter = TestMolecule->begin(); iter != TestMolecule->end(); ++iter){
    ListOfPoints->remove((*iter));
    size--;
    CPPUNIT_ASSERT_EQUAL( size, ListOfPoints->size() );
  }
  CPPUNIT_ASSERT_EQUAL( (size_t)0, size );
  CPPUNIT_ASSERT_EQUAL( (size_t)0, ListOfPoints->size() );
  CPPUNIT_ASSERT_EQUAL( true, ListOfPoints->empty() );
  delete(ListOfPoints);
  // get all atoms in one corner
  tester= Vector(0.5, 0.5, 0.5);
  CPPUNIT_ASSERT_EQUAL ( true, LC->SetIndexToVector(tester) );
  ListOfPoints = LC->GetallNeighbours();
  size=ListOfPoints->size();
  CPPUNIT_ASSERT_EQUAL( (size_t)8, size );
  for(molecule::iterator iter = TestMolecule->begin(); iter != TestMolecule->end(); ++iter){
    if (((*iter)->at(0) <2) && ((*iter)->at(1) <2) && ((*iter)->at(2) <2)) {
      ListOfPoints->remove(*iter);
      size--;
      CPPUNIT_ASSERT_EQUAL( size, ListOfPoints->size() );
    }
  }
  CPPUNIT_ASSERT_EQUAL( (size_t)0, size );
  CPPUNIT_ASSERT_EQUAL( (size_t)0, ListOfPoints->size() );
  CPPUNIT_ASSERT_EQUAL( true, ListOfPoints->empty() );
  delete(ListOfPoints);
  // get all atoms from one corner
  tester = Vector(0.5, 0.5, 0.5);
  CPPUNIT_ASSERT_EQUAL ( true, LC->SetIndexToVector(tester) );
  ListOfPoints = LC->GetallNeighbours(3);
  size=ListOfPoints->size();
  CPPUNIT_ASSERT_EQUAL( (size_t)27, size );
  for(molecule::iterator iter = TestMolecule->begin(); iter!=TestMolecule->end();++iter){
    ListOfPoints->remove(*iter);
    size--;
    CPPUNIT_ASSERT_EQUAL( size, ListOfPoints->size() );
  }
  CPPUNIT_ASSERT_EQUAL( (size_t)0, size );
  CPPUNIT_ASSERT_EQUAL( (size_t)0, ListOfPoints->size() );
  CPPUNIT_ASSERT_EQUAL( true, ListOfPoints->empty() );
  delete(ListOfPoints);
};
/** UnitTest for LinkedCell_deprecated::GetPointsInsideSphere().
 */
void linkedcelltest::GetPointsInsideSphereTest()
{
  Vector tester;
  TesselPointSTLList *ListOfPoints = NULL;
  size_t size = 0;
  // get all points around central arom with radius 1.
  tester= Vector(1.5,1.5,1.5);
  CPPUNIT_ASSERT_EQUAL ( true, LC->SetIndexToVector(tester) );
  ListOfPoints = LC->GetPointsInsideSphere(1., &tester);
  size = ListOfPoints->size();
  CPPUNIT_ASSERT_EQUAL( (size_t)7, size );
  for(molecule::iterator iter = TestMolecule->begin(); iter!=TestMolecule->end();++iter){
    if (((*iter)->DistanceSquared(tester) - 1.) < MYEPSILON ) {
      ListOfPoints->remove(*iter);
      size--;
      CPPUNIT_ASSERT_EQUAL( size, ListOfPoints->size() );
    }
  }
  CPPUNIT_ASSERT_EQUAL( (size_t)0, size );
  CPPUNIT_ASSERT_EQUAL( (size_t)0, ListOfPoints->size() );
  CPPUNIT_ASSERT_EQUAL( true, ListOfPoints->empty() );
  delete(ListOfPoints);
};