/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2013 Frederik Heber. 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 .
 */
/*
 * FourBodyPotential_TorsionUnitTest.cpp
 *
 *  Created on: Jul 08, 2013
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
using namespace std;
#include 
#include 
#include 
#include "FourBodyPotential_TorsionUnitTest.hpp"
#include 
#include "CodePatterns/Assert.hpp"
#include "FunctionApproximation/FunctionArgument.hpp"
#include "Potentials/Specifics/FourBodyPotential_Torsion.hpp"
#include "Potentials/helpers.hpp"
using namespace boost::assign;
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( FourBodyPotential_TorsionTest );
const double spring_constant = .5;
void FourBodyPotential_TorsionTest::setUp()
{
  // failing asserts should be thrown
  ASSERT_DO(Assert::Throw);
  // create three distances to have the desired angle
  typedef std::vector angles_t;
  angles_t angles;
  angles += -0.5, -.4, -.3, -.2, -.1, 0., .1, .2, .3, .4, .5;
  FunctionModel::arguments_t tempvector(6);
  tempvector[0] = argument_t( std::make_pair(0,1), std::make_pair(0,1), 1.);
  tempvector[1] = argument_t( std::make_pair(0,2), std::make_pair(0,1), 1.);
  tempvector[2] = argument_t( std::make_pair(0,3), std::make_pair(0,0), 0.);
  tempvector[3] = argument_t( std::make_pair(1,2), std::make_pair(1,1), 1.);
  tempvector[4] = argument_t( std::make_pair(1,3), std::make_pair(0,1), 1.);
  tempvector[5] = argument_t( std::make_pair(2,3), std::make_pair(0,1), 1.);
  // we have two equilateral triangles (length 1)
  // height is then \sqrt(2*(1+1)-1)*.5=sqrt(3)*.5
  // then angle in triangle of both heights and connecting line is
  // (h_1^2+h_2^2 - x^2) / (2*h_1*h_2) = cos(theta). Solving this equation
  // for the unknown distance x is what we need
  // 3/4+3/4-x/(2*sqrt(3)*.5*sqrt(3)*.5)
  // x = sqrt(h_1^2+h_2^2 - cos(theta) * (2*h_1*h_2))
  for (angles_t::const_iterator iter = angles.begin(); iter != angles.end(); ++iter) {
    tempvector[2].distance = sqrt(.75+.75 - (*iter)*(1.5));
    input += tempvector;
  }
  output +=
      spring_constant*0.25,
      spring_constant*0.16,
      spring_constant*0.09,
      spring_constant*0.04,
      spring_constant*0.01,
      spring_constant*0.,
      spring_constant*0.01,
      spring_constant*0.04,
      spring_constant*0.09,
      spring_constant*0.16,
      spring_constant*0.25;
  CPPUNIT_ASSERT_EQUAL( input.size(), output.size() );
}
void FourBodyPotential_TorsionTest::tearDown()
{
}
/** UnitTest for operator()
 */
void FourBodyPotential_TorsionTest::operatorTest()
{
  FourBodyPotential_Torsion::ParticleTypes_t types =
      boost::assign::list_of
        (0)(1)(1)(0)
      ;
  FourBodyPotential_Torsion angle(types, spring_constant,0.);
  for (size_t index = 0; index < input.size(); ++index) {
    const FourBodyPotential_Torsion::results_t result =
        angle( FunctionModel::list_of_arguments_t(1, input[index]) );
    CPPUNIT_ASSERT(
        Helpers::isEqual(
            output[index],
            result[0]
        )
    );
  }
}
/** UnitTest for derivative()
 */
void FourBodyPotential_TorsionTest::derivativeTest()
{
  FourBodyPotential_Torsion::ParticleTypes_t types =
      boost::assign::list_of
        (0)(1)(1)(0)
      ;
  FourBodyPotential_Torsion angle(types, spring_constant,0.);
  CPPUNIT_ASSERT(
      Helpers::isEqual(
          0.,
          angle.derivative(
              FunctionModel::list_of_arguments_t(1, input[5])
          )[0],
          10.
      )
  );
}
/** UnitTest for parameter_derivative()
 */
void FourBodyPotential_TorsionTest::parameter_derivativeTest()
{
  FourBodyPotential_Torsion::ParticleTypes_t types =
      boost::assign::list_of
        (0)(1)(1)(0)
      ;
  FourBodyPotential_Torsion angle(types, spring_constant,0.);
  CPPUNIT_ASSERT(
      Helpers::isEqual(
          0.,
          angle.parameter_derivative(
              FunctionModel::list_of_arguments_t(1, input[5]),
              0
          )[0],
          10.
      )
  );
  CPPUNIT_ASSERT(
      Helpers::isEqual(
          0.,
          angle.parameter_derivative(
              FunctionModel::list_of_arguments_t(1, input[5]),
              1
          )[0],
          10.
      )
  );
}