/*
 * 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 .
 */
/*
 * MatrixContentSymmetricUnitTest.cpp
 *
 *  Created on: Jan 8, 2010
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
using namespace std;
#include 
#include 
#include 
#include "MatrixContentSymmetricUnitTest.hpp"
#include "MatrixContent.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( MatrixContentSymmetricTest );
void MatrixContentSymmetricTest::setUp()
{
  m = new MatrixContent(3,3);
};
void MatrixContentSymmetricTest::tearDown()
{
  delete(m);
};
/** Unit Test for accessing matrix elements.
 *
 */
void MatrixContentSymmetricTest::AccessTest()
{
  // check whether all elements are initially zero
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( 0., m->at(i,j) );
  // set
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      m->set(i,j, i*3+j );
  // and check
  double *ptr = NULL;
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++) {
      CPPUNIT_ASSERT_EQUAL( (double)(i*3+j), m->at(i,j) );
      ptr = m->Pointer(i,j);
      CPPUNIT_ASSERT_EQUAL( (double)(i*3+j), *ptr );
    }
  // assignment
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      m->set(i,j, i*3+j );
  MatrixContent *dest = new MatrixContent(3,3);
  *dest = *m;
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( dest->at(i,j), m->at(i,j) );
  delete(dest);
  // out of bounds
  //CPPUNIT_ASSERT_EQUAL(0., v->at(4,2) );
  //CPPUNIT_ASSERT_EQUAL(0., v->at(2,17) );
  //CPPUNIT_ASSERT_EQUAL(0., v->at(1024,140040) );
  //CPPUNIT_ASSERT_EQUAL(0., v->at(-1,0) );
  //CPPUNIT_ASSERT_EQUAL(0., v->at(0,-1) );
  //CPPUNIT_ASSERT_EQUAL(0., v->at(-1,-1) );
};
/** Unit Test for initializating matrices.
 *
 */
void MatrixContentSymmetricTest::InitializationTest()
{
  // set zero
  m->setZero();
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( 0., m->at(i,j) );
  // set all
  m->setValue(1.5);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( 1.5, m->at(i,j) );
  // set basis
  m->setIdentity();
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( i == j ? 1. : 0. , m->at(i,j) );
  // set from array
  double array[] = { 1., 0., 0.,
                     0., 1., 0.,
                     0., 0., 1. };
  m->setFromDoubleArray(array);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( i == j ? 1. : 0. , m->at(i,j) );
};
/** Unit Test for copying matrices.
 *
 */
void MatrixContentSymmetricTest::CopyTest()
{
  // set basis
  MatrixContent *dest = NULL;
  for (int i=0;i<3;i++) {
    m->setValue(i);
    dest = new MatrixContent(m);
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( m->at(i,j) , dest->at(i,j) );
    delete(dest);
  }
};
/** Unit Test for exchanging rows and columns.
 *
 */
void MatrixContentSymmetricTest::ExchangeTest()
{
  // set to 1,1,1,2, ...
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      m->set(i,j, i+1 );
  // swap such that nothing happens
  m->SwapColumns(1,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( (double)i+1., m->at(i,j) );
  // swap two rows
  m->SwapRows(1,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      switch (j) {
        case 0:
          CPPUNIT_ASSERT_EQUAL( 1., m->at(j,i) );
          break;
        case 1:
          CPPUNIT_ASSERT_EQUAL( 3., m->at(j,i) );
          break;
        case 2:
          CPPUNIT_ASSERT_EQUAL( 2., m->at(j,i) );
          break;
        default:
          CPPUNIT_ASSERT_EQUAL( -1., m->at(i,j) );
      }
  // check that op is reversable
  m->SwapRows(1,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( (double)i+1., m->at(i,j) );
  // set to 1,2,3,1, ...
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      m->set(i,j, j+1. );
  // swap such that nothing happens
  m->SwapRows(0,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( (double)j+1., m->at(i,j) );
  // swap two columns
  m->SwapColumns(0,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      switch (j) {
        case 0:
          CPPUNIT_ASSERT_EQUAL( 3., m->at(i,j) );
          break;
        case 1:
          CPPUNIT_ASSERT_EQUAL( 2., m->at(i,j) );
          break;
        case 2:
          CPPUNIT_ASSERT_EQUAL( 1., m->at(i,j) );
          break;
        default:
          CPPUNIT_ASSERT_EQUAL( -1., m->at(i,j) );
      }
  // check that op is reversable
  m->SwapColumns(0,2);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++)
      CPPUNIT_ASSERT_EQUAL( (double)j+1., m->at(i,j) );
  // set to 1,2,3, ...
  MatrixContent *n =  new MatrixContent(3,3);
  for (int i=0;i<3;i++)
    for (int j=0;j<3;j++) {
      m->set(i,j, 3*i+j+1 );
      n->set(i,j, 3*j+i+1 );
    }
  // transpose
  MatrixContent res = (*m).transposed();
  CPPUNIT_ASSERT( *n == res );
  // second transpose
  res.transpose();
  CPPUNIT_ASSERT( *m == res );
  delete n;
};
/** Unit Test for matrix properties.
 *
 */
void MatrixContentSymmetricTest::PropertiesTest()
{
  // is zero
  m->setZero();
  CPPUNIT_ASSERT_EQUAL( true, m->IsNull() );
  CPPUNIT_ASSERT_EQUAL( false, m->IsPositive() );
  CPPUNIT_ASSERT_EQUAL( false, m->IsNegative() );
  CPPUNIT_ASSERT_EQUAL( true, m->IsNonNegative() );
  // is positive
  m->setValue(0.5);
  CPPUNIT_ASSERT_EQUAL( false, m->IsNull() );
  CPPUNIT_ASSERT_EQUAL( true, m->IsPositive() );
  CPPUNIT_ASSERT_EQUAL( false, m->IsNegative() );
  CPPUNIT_ASSERT_EQUAL( true, m->IsNonNegative() );
  // is negative
  m->setValue(-0.1);
  CPPUNIT_ASSERT_EQUAL( false, m->IsNull() );
  CPPUNIT_ASSERT_EQUAL( false, m->IsPositive() );
  CPPUNIT_ASSERT_EQUAL( true, m->IsNegative() );
  CPPUNIT_ASSERT_EQUAL( false, m->IsNonNegative() );
  // is positive definite
  double array[] = { 1., 0., 0.,
                     0., 1., 1.,
                     0., 0., 1. };
  m->setFromDoubleArray(array);
  CPPUNIT_ASSERT_EQUAL( true, m->IsPositiveDefinite() );
  //determinant
  m->setIdentity();
  CPPUNIT_ASSERT_EQUAL( 1., m->Determinant() );
  m->setZero();
  CPPUNIT_ASSERT_EQUAL( 0., m->Determinant() );
  m->set( 0, 0, 1.);
  m->set( 1, 1, 1.);
  m->set( 2, 1, 1.);
  CPPUNIT_ASSERT_EQUAL( 0., m->Determinant() );
  
  double array2[] = { 2., 0., 1.,
                     -3., 1., 1.,
                     1., 5.5, 1. };
  m->setFromDoubleArray(array2);
  CPPUNIT_ASSERT_EQUAL( -26.5, m->Determinant() );
};