/**
 * @file TestUniform.cpp
 * @date Aug 9, 2008
 * @author Ronald Kluth
 *
 * @brief Tests for ODEMx class Uniform
 */

#include "TestRandom.h"
#include "../TestBase/TestBase.h"
#include "../TestData/TestData.h"
#include <cstdlib> // atof()
#include <cmath> // floor()

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

	/**
	 * @struct UniformFixture
	 * @brief Helper struct providing set-up/tear-down of Uniform tests
	 *
	 * @copydetails EventFixture
	 */
	struct UniformFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		double lower;
		double upper;
		int cells;
		Uniform uniform;
		SuiteData::ReportTest reporter;
		statistics::Histogram histogram;
		data::TypeInfo type;

		UniformFixture()
		:	sim( "UniformTestSim" ),
			log( TestLogConsumer::create() ),
			lower( 0.0 ),
			upper( 4.0 ),
			cells( (int)floor(upper - lower) * 5 ),
			uniform( sim, "UniformTest", lower, upper ),
			histogram( sim, "UniformTestHistogram", lower, upper, cells ),
			type( typeid(Uniform) )
			{
				sim.addConsumer( log );
			}
	};

	/**
	 * @test odemx::Uniform construction
	 *
	 * Expected function call effects:
	 * @li lower bound greater upper bound produces a warning and switches
	 * the two values
	 * @li label is set
	 * @li dist context (simulation) is set
	 * @li lower and upper bounds are set
	 */
	TEST_FIXTURE( UniformFixture, ConstructionDestruction )
	{
		data::Label l = "UniformTestUserSimConstruction";
		{
			Uniform uniform2( sim, l, upper, lower );
			CHECK( log->getTraceRecord( "create", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "seed", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "lower bound", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "upper bound", type ) );
			CHECK( log->getWarningRecord( "Uniform(): lower bound greater than upper bound; bounds swapped", type ) );
			CHECK_EQUAL( l, uniform2.getLabel() );
			CHECK_EQUAL( lower, uniform2.getLowerBound() );
			CHECK_EQUAL( upper, uniform2.getUpperBound() );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );
	}

	/**
	 * @test odemx::Uniform::sample()
	 *
	 * Expected function call effects:
	 * @li samples are always within the interval [lower, upper]
	 * @li all values have roughly the same hit percentage
	 */
	TEST_FIXTURE( UniformFixture, Sample )
	{
		uniform.sample();
		CHECK( log->getTraceRecord( "sample", type ) );
		CHECK( log->getStatisticsRecord( "count", "uses", type ) );

		for( int i = 0; i < 5000000; ++i )
		{
			double value = uniform.sample();
			CHECK( uniform.getLowerBound() <= value );
			CHECK( uniform.getUpperBound() >= value );
			histogram.update( value );
		}

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

		vector< string >& table = reporter.allTableContent[1];
		// the first and last rows should not contain anything, i.e. 0
		CHECK_EQUAL( 0, atof( table[ 0 * 5 + 3 ].c_str() ) );
		CHECK_EQUAL( 0, atof( table[ (cells + 1) * 5 + 3 ].c_str() ) );
		for( int i = 1; i < cells + 1; ++i )
		{
			CHECK_CLOSE( 100.0 / cells, atof( table[ i * 5 + 3 ].c_str() ), 0.02  );
		}

		// print histogram
//		for( size_t i = 0; i < table.size(); ++i )
//		{
//			cout << table[i] << " ";
//			if( ( i % 5 ) == 4 ) cout << endl;
//		}
//		cout << endl;
	}

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