/**
 * @file TestTally.cpp
 * @date Aug 6, 2008
 * @author Ronald Kluth
 *
 * @brief Tests for ODEMx class Tally
 */

#include "TestStatistics.h"
#include "../TestBase/TestBase.h"
#include "../TestData/TestData.h"

#include <cmath>

/// @cond DOXYGEN_SKIP
SUITE( Statistics )
{
/// @endcond

	/**
	 * @struct TallyFixture
	 * @brief Helper struct providing set-up/tear-down of Tally tests
	 *
	 * @copydetails EventFixture
	 */
	struct TallyFixture
	{
		SuiteBase::SimulationTest sim;
		SuiteData::ReportTest reporter;
		Tally tally;

		TallyFixture()
		:	sim( "TallyTestSim" ),
			reporter(),
			tally( sim, "TallyTest" )
			{}
	};

	/**
	 * @test odemx::Tally construction
	 *
	 * Expected effects:
	 * @li label is set correctly
	 * @li statistics manager (Simulation) is set correctly
	 * @li tally values are 0
	 * @li last reset time is current SimTime
	 */
	TEST_FIXTURE( TallyFixture, ConstructionDestruction )
	{
		data::Label label = "TestTallyConstructionUserSim";
		std::auto_ptr< Tally > tally( new Tally( sim, label ) );
		CHECK_EQUAL( label, tally->getLabel() );
		CHECK_EQUAL( (unsigned int) 0, tally->getUpdateCount() );
		CHECK_EQUAL( 0.0, tally->getMin() );
		CHECK_EQUAL( 0.0, tally->getMax() );
		CHECK_EQUAL( 0.0, tally->getMean() );
		CHECK_EQUAL( 0.0, tally->getStandardDeviation() );
		CHECK_EQUAL( sim.getTime(), tally->getResetTime() );
	}

	/**
	 * @test odemx::Tally::update()
	 *
	 * Expected function call effects:
	 * @li usage counter is increased by the number of \c update() calls
	 * @li \c min and \c max are set correctly
	 */
	TEST_FIXTURE( TallyFixture, Update )
	{
		double min = 1.2;
		double max = 3.4;
		tally.update( min );
		tally.update( max );

		CHECK_EQUAL( (std::size_t) 2, tally.getUpdateCount() );
		CHECK_EQUAL( min, tally.getMin() );
		CHECK_EQUAL( max, tally.getMax() );
	}

	/**
	 * @test odemx::Tally::reset()
	 *
	 * Expected function call effects:
	 * @li the tally values are set to 0
	 * @li the last reset time is set to the sim's time
	 */
	TEST_FIXTURE( TallyFixture, Reset )
	{
		base::SimTime simTime = 25;

		tally.update( 16 );
		tally.update( 11 );

		tally.reset( simTime );
		CHECK_EQUAL( (std::size_t) 0, tally.getUpdateCount() );
		CHECK_EQUAL( 0.0, tally.getMin() );
		CHECK_EQUAL( 0.0, tally.getMax() );
		CHECK_EQUAL( 0.0, tally.getMean() );
		CHECK_EQUAL( 0.0, tally.getStandardDeviation() );
		CHECK_EQUAL( simTime, tally.getResetTime() );
	}

	/**
	 * @test odemx::Tally::report(Report*)
	 *
	 * Expected function call effects:
	 * @li the report table contains correct values
	 */
	TEST_FIXTURE( TallyFixture, Report )
	{
		base::SimTime simTime = 25;

		tally.reset( simTime ); // non-0 reset time
		double min = 1.2;
		double max = 3.4;
		tally.update( min );
		tally.update( max );

		double mean = tally.getMean();
		double deviation = std::sqrt(
				( ( min - mean ) * ( min - mean )
				+ ( max - mean ) * ( max - mean ) ) / 2 );

		CHECK_EQUAL( (std::size_t) 2, tally.getUpdateCount() );
		CHECK_EQUAL( min, tally.getMin() );
		CHECK_EQUAL( max, tally.getMax() );
		CHECK_EQUAL( ( min + max ) / 2, mean );
		CHECK_CLOSE( deviation, tally.getStandardDeviation(), 0.000000000000001 );

		reporter.addReportProducer( tally );
		reporter.generateReport();
		CHECK( reporter.processedTables );

		// check the table line
		CHECK_EQUAL( tally.getLabel(), reporter.allTableContent[0][0] );
		CHECK_EQUAL( toString( tally.getResetTime() ), reporter.allTableContent[0][1] );
		CHECK_EQUAL( toString( tally.getUpdateCount() ), reporter.allTableContent[0][2] );
		CHECK_EQUAL( toString( tally.getMin() ), reporter.allTableContent[0][3] );
		CHECK_EQUAL( toString( tally.getMax() ), reporter.allTableContent[0][4] );
		CHECK_EQUAL( toString( tally.getMean() ), reporter.allTableContent[0][5] );
		CHECK_EQUAL( toString( tally.getStandardDeviation() ), reporter.allTableContent[0][6] );
	}

	/**
	 * @test odemx::Tally::getMean()
	 *
	 * Expected function call effects:
	 * @li the mean is computed correctly
	 */
	TEST_FIXTURE( TallyFixture, GetMean )
	{
		double sum = 0;
		int values[] = { 1, 5, 2, 7, 10, 5 };
		size_t count = ( sizeof( values ) / sizeof( int ) );

		for( size_t i = 0; i < count; ++i )
		{
			sum += values[i];
			tally.update( values[i] );
		}

		double mean = sum / count;

		CHECK_EQUAL( mean, tally.getMean() );
	}

	/**
	 * @test odemx::Tally::getStandardDeviation()
	 *
	 * Expected function call effects:
	 * @li the standard deviation is computed correctly
	 */
	TEST_FIXTURE( TallyFixture, GetStandardDeviation )
	{
		double sum = 0;
		int values[] = { 1, 5, 2, 7, 10, 5 };
		size_t count = ( sizeof( values ) / sizeof( int ) );

		for( size_t i = 0; i < count; ++i )
		{
			sum += values[i];
			tally.update( values[i] );
		}

		double mean = sum / count;
		double variance = 0;
		for( size_t i = 0; i < count; ++i )
		{
			variance += ( values[i] - mean ) * ( values[i] - mean );
		}
		variance /= count;

		double standardDeviation = std::sqrt( variance );

		CHECK_EQUAL( standardDeviation, tally.getStandardDeviation() );
		CHECK_CLOSE( standardDeviation, tally.getStandardDeviation(), 0.001 );
	}

/// @cond DOXYGEN_SKIP
}
/// @endcond
