/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 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 .
 */
/*
 * ParserMpqcUnitTest.cpp
 *
 *  Created on: Mar 3, 2010
 *      Author: metzler
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "ParserMpqcUnitTest.hpp"
#include 
#include 
#include 
#include 
#include "Atom/atom.hpp"
#include "Atom/AtomObserver.hpp"
#include "CodePatterns/Assert.hpp"
#include "Descriptors/AtomTypeDescriptor.hpp"
#include "Element/element.hpp"
#include "Element/periodentafel.hpp"
#include "Parser/ChangeTracker.hpp"
#include "Parser/MpqcParser.hpp"
#include "World.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
using namespace std;
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( ParserMpqcUnitTest );
static string waterMpqc_CLHF ="% Created by MoleCuilder\n\
mpqc: (\n\
\tcheckpoint = no\n\
\tsavestate = no\n\
\tdo_gradient = yes\n\
\tmole: (\n\
\t\tmolecule = $:molecule\n\
\t\tbasis = $:basis\n\
\t\tmaxiter = 1000\n\
\t\tmemory = 16000000\n\
\t)\n\
)\n\
molecule: (\n\
\tunit = angstrom\n\
\t{ atoms geometry } = {\n\
\t\tO [ 0\t0\t0 ]\n\
\t\tH [ 0.758602\t0\t0.504284 ]\n\
\t\tH [ 0.758602\t0\t-0.504284 ]\n\
\t}\n\
)\n\
basis: (\n\
\tname = \"3-21G\"\n\
\tmolecule = $:molecule\n\
)\n"; // tested with mpqc 3.0.0-alpha
static string waterMpqc_CLKS ="% Created by MoleCuilder\n\
mpqc: (\n\
\tcheckpoint = no\n\
\tsavestate = no\n\
\tdo_gradient = yes\n\
\tmole: (\n\
\t\tfunctional:(name=B3LYP)\n\
\t\tmolecule = $:molecule\n\
\t\tbasis = $:basis\n\
\t\tmaxiter = 1000\n\
\t\tmemory = 16000000\n\
\t)\n\
)\n\
molecule: (\n\
\tunit = angstrom\n\
\t{ atoms geometry } = {\n\
\t\tO [ 0\t0\t0 ]\n\
\t\tH [ 0.758602\t0\t0.504284 ]\n\
\t\tH [ 0.758602\t0\t-0.504284 ]\n\
\t}\n\
)\n\
basis: (\n\
\tname = \"3-21G\"\n\
\tmolecule = $:molecule\n\
)\n"; // tested with mpqc 3.0.0-alpha
static string waterMpqc_MBPT2 ="% Created by MoleCuilder\n\
mpqc: (\n\
\tcheckpoint = no\n\
\tsavestate = no\n\
\tdo_gradient = yes\n\
\tmole: (\n\
\t\tbasis = $:basis\n\
\t\tmolecule = $:molecule\n\
\t\tmemory = 16000000\n\
\t\treference: (\n\
\t\t\tmaxiter = 1000\n\
\t\t\tbasis = $:basis\n\
\t\t\tmolecule = $:molecule\n\
\t\t\tmemory = 16000000\n\
\t\t)\n\
\t)\n\
)\n\
molecule: (\n\
\tunit = angstrom\n\
\t{ atoms geometry } = {\n\
\t\tO [ 0\t0\t0 ]\n\
\t\tH [ 0.758602\t0\t0.504284 ]\n\
\t\tH [ 0.758602\t0\t-0.504284 ]\n\
\t}\n\
)\n\
basis: (\n\
\tname = \"3-21G\"\n\
\tmolecule = $:molecule\n\
)\n"; // tested with mpqc 3.0.0-alpha
static string waterMpqc_MBPT2_R12 ="% Created by MoleCuilder\n\
mpqc: (\n\
\tcheckpoint = no\n\
\tsavestate = no\n\
\tdo_gradient = yes\n\
\tmole: (\n\
\t\tmolecule = $:molecule\n\
\t\tbasis = $:basis\n\
\t\taux_basis = $:abasis\n\
\t\tstdapprox = \"A'\"\n\
\t\tnfzc = 1\n\
\t\tmemory = 16000000\n\
\t\tintegrals:()\n\
\t\treference: (\n\
\t\t\tmolecule = $:molecule\n\
\t\t\tbasis = $:basis\n\
\t\t\tmaxiter = 1000\n\
\t\t\tmemory = 16000000\n\
\t\t\tintegrals:()\n\
\t\t)\n\
\t)\n\
)\n\
molecule: (\n\
\tunit = angstrom\n\
\t{ atoms geometry } = {\n\
\t\tO [ 0\t0\t0 ]\n\
\t\tH [ 0.758602\t0\t0.504284 ]\n\
\t\tH [ 0.758602\t0\t-0.504284 ]\n\
\t}\n\
)\n\
basis: (\n\
\tname = \"3-21G\"\n\
\tmolecule = $:molecule\n\
)\n\
% auxiliary basis set specification\n\
\tabasis: (\n\
\tname = \"aug-cc-pVDZ\"\n\
\tmolecule = $:molecule\n\
)\n"; // basically tested with mpqc 3.0.0-alpha (no parse errors but did not calculate due to missing code)
void ParserMpqcUnitTest::setUp() 
{
  // failing asserts should be thrown
  ASSERT_DO(Assert::Throw);
  parser = new FormatParser();
  World::getInstance();
  setVerbosity(2);
  // we need hydrogens and oxygens in the following tests
  CPPUNIT_ASSERT(World::getInstance().getPeriode()->FindElement(1) != NULL);
  CPPUNIT_ASSERT(World::getInstance().getPeriode()->FindElement(8) != NULL);
}
void ParserMpqcUnitTest::tearDown() 
{
  delete parser;
  ChangeTracker::purgeInstance();
  World::purgeInstance();
  AtomObserver::purgeInstance();
}
/************************************ tests ***********************************/
void ParserMpqcUnitTest::ParameterDefaultTest() {
  // check default values
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::hessianParam) == std::string("no"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::savestateParam) == std::string("no"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::do_gradientParam) == std::string("yes"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::maxiterParam) == std::string("1000"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::memoryParam) == std::string("16000000"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::stdapproxParam) == std::string("A'"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::nfzcParam) == std::string("1"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::basisParam) == std::string("3-21G"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::aux_basisParam) == std::string("aug-cc-pVDZ"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::integrationParam) == std::string("IntegralCints"));
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::theoryParam) == std::string("MBPT2"));
}
void ParserMpqcUnitTest::ParameterCloneTest() {
  FormatParser_Parameters *clone = parser->getParams().clone();
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::theoryParam) == std::string("MBPT2"));
  std::stringstream setvalue("theory = CLHF");
  setvalue >> parser->getParams();
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::theoryParam) == std::string("CLHF"));
  parser->getParams().makeClone(*clone);
  CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::theoryParam) == std::string("MBPT2"));
}
void ParserMpqcUnitTest::ParameterSetterTest() {
  // test a string
  {
    std::stringstream setvalue("theory = CLHF");
    setvalue >> parser->getParams();
//    std::cout << "integration method is "
//        << parser->getParams().getString(MpqcParser_Parameters::theoryParam) << std::endl;
    CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::theoryParam) == std::string("CLHF"));
  }
  // test a bool
  {
    std::stringstream setvalue("Hessian = yes");
    setvalue >> parser->getParams();
//    std::cout << "Hessian is "
//        << parser->getParams().getString(MpqcParser_Parameters::hessianParam) << std::endl;
    CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::hessianParam) == std::string("yes"));
  }
  // test int
  {
    std::stringstream setvalue("maxiter = 500");
    setvalue >> parser->getParams();
//    std::cout << "maxiter is "
//        << parser->getParams().getString(MpqcParser_Parameters::maxiterParam) << std::endl;
    CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::maxiterParam) == std::string("500"));
  }
  // test whether unknown key fails
  std::cout << "The following Assertion warning is desired and does not indicate a failure of the test." << std::endl;
  {
    std::stringstream setvalue("hessian = no");
#ifndef NDEBUG
    ASSERT_DO(Assert::Throw);
    CPPUNIT_ASSERT_THROW(setvalue >> parser->getParams(), Assert::AssertionFailure);
#else
    setvalue >> parser->getParams();
#endif
//    std::cout << "Hessian is still "
//        << parser->getParams().getString(MpqcParser_Parameters::hessianParam) << std::endl;
    CPPUNIT_ASSERT(parser->getParams().getParameter(MpqcParser_Parameters::hessianParam) == std::string("yes"));
  }
}
void ParserMpqcUnitTest::readMpqcTest() {
  stringstream input(waterMpqc_CLHF);
  parser->getParams().setParameter(
      MpqcParser_Parameters::theoryParam,
      parser->getParams().getTheoryName(MpqcParser_Parameters::CLHF)
          );
  parser->load(&input);
  CPPUNIT_ASSERT_EQUAL(3, World::getInstance().numAtoms());
}
void ParserMpqcUnitTest::writeMpqcTest() {
  // build up water molecule
  string first;
  string second;
  atom *Walker = NULL;
  Walker = World::getInstance().createAtom();
  Walker->setType(8);
  Walker->setPosition(Vector(0,0,0));
  Walker = World::getInstance().createAtom();
  Walker->setType(1);
  Walker->setPosition(Vector(0.758602,0,0.504284));
  Walker = World::getInstance().createAtom();
  Walker->setType(1);
  Walker->setPosition(Vector(0.758602,0,-0.504284));
  CPPUNIT_ASSERT_EQUAL(3, World::getInstance().numAtoms());
  // create two stringstreams, one stored, one created
  std::vector atoms = const_cast(World::getInstance()).
      getAllAtoms();
  {
    // compare both configs for CLHF
    stringstream output;
    parser->getParams().setParameter(
        MpqcParser_Parameters::theoryParam,
        parser->getParams().getTheoryName(MpqcParser_Parameters::CLHF)
            );
    parser->save(&output, atoms);
    stringstream input(waterMpqc_CLHF);
    // check for non-empty streams
    input.peek();
    output.peek();
    CPPUNIT_ASSERT(input.good() && output.good());
    // check equality of streams per line (for debugging)
    for (; std::getline(input, first) && std::getline(output, second); ) {
      //std::cout << "Comparing '" << first << "' to '" << second << "'" << std::endl;
      CPPUNIT_ASSERT(first == second);
    }
  }
  {
    // compare both configs for CLKS
    stringstream output;
    parser->getParams().setParameter(
        MpqcParser_Parameters::theoryParam,
        parser->getParams().getTheoryName(MpqcParser_Parameters::CLKS)
            );
    parser->save(&output, atoms);
    stringstream input(waterMpqc_CLKS);
    // check for non-empty streams
    input.peek();
    output.peek();
    CPPUNIT_ASSERT(input.good() && output.good());
    // check equality of streams per line (for debugging)
    for (; std::getline(input, first) && std::getline(output, second); ) {
      //std::cout << "Comparing '" << first << "' to '" << second << "'" << std::endl;
      CPPUNIT_ASSERT(first == second);
    }
  }
  {
    // compare both configs for MBPT2
    stringstream output;
    parser->getParams().setParameter(
        MpqcParser_Parameters::theoryParam,
        parser->getParams().getTheoryName(MpqcParser_Parameters::MBPT2)
            );
    parser->save(&output, atoms);
    stringstream input(waterMpqc_MBPT2);
    // check for non-empty streams
    input.peek();
    output.peek();
    CPPUNIT_ASSERT(input.good() && output.good());
    // check equality of streams per line (for debugging)
    for (; std::getline(input, first) && std::getline(output, second); ) {
      //std::cout << "Comparing '" << first << "' to '" << second << "'" << std::endl;
      CPPUNIT_ASSERT(first == second);
    }
  }
  {
    // compare both configs for MBPT2_R12
    stringstream output;
    parser->getParams().setParameter(
        MpqcParser_Parameters::theoryParam,
        parser->getParams().getTheoryName(MpqcParser_Parameters::MBPT2_R12)
            );
    parser->save(&output, atoms);
    stringstream input(waterMpqc_MBPT2_R12);
    // check for non-empty streams
    input.peek();
    output.peek();
    CPPUNIT_ASSERT(input.good() && output.good());
    // check equality of streams per line (for debugging)
    for (; std::getline(input, first) && std::getline(output, second); ) {
      //std::cout << "Comparing '" << first << "' to '" << second << "'" << std::endl;
      CPPUNIT_ASSERT(first == second);
    }
  }
}