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

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

#include <fstream>

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

	/**
	 * @struct ErlangFixture
	 * @brief Helper struct providing set-up/tear-down of Erlang tests
	 *
	 * @copydetails EventFixture
	 */
	struct ErlangFixture
	{
		SuiteBase::SimulationTest sim;
		TestLogConsumer::Ptr log;
		double rate;
		int shape;
		int cells;
		Erlang erlang;
		statistics::Histogram histogram;
		data::TypeInfo type;

		ErlangFixture()
		:	sim( "ErlangTestSim" ),
			log( TestLogConsumer::create() ),
			rate( 2.0 ),
			shape( 3 ),
			cells( 100 ),
			erlang( sim, "ErlangTest", rate, shape ),
			histogram( sim, "ErlangTestHistogram", 0, 20, cells ),
			type( typeid(Erlang) )
			{
				sim.addConsumer( log );
			}
	};

	/**
	 * @test odemx::Erlang 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( ErlangFixture, ConstructionDestruction )
	{
		data::Label l = "ErlangTestUserSimConstruction";
		{
			Erlang erlang2( sim, l, -rate, -shape );
			CHECK( log->getTraceRecord( "create", type ) );
			CHECK( log->getWarningRecord( "Erlang(): rate <= 0.0; -rate or 0.01 used", type ) );
			CHECK( log->getWarningRecord( "Erlang(): shape <= 0; -shape or 1 used", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "seed", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "rate", type ) );
			CHECK( log->getStatisticsRecord( "parameter", "shape", type ) );
			CHECK_EQUAL( l, erlang2.getLabel() );
			CHECK_EQUAL( rate, erlang2.getRate() );
			CHECK_EQUAL( shape, erlang2.getShape() );
		}
		CHECK( log->getTraceRecord( "destroy", type ) );
	}

	/**
	 * @test odemx::Erlang::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( ErlangFixture, Sample )
	{
		erlang.sample();
		CHECK( log->getTraceRecord( "sample", type ) );
		CHECK( log->getStatisticsRecord( "count", "uses", type ) );

		for( int i = 0; i < 1000000; ++i )
		{
			double value = erlang.sample();
			histogram.update( value );
		}

		CHECK_CLOSE( erlang.getShape() / erlang.getRate(), histogram.getMean(), 0.002  );
		CHECK_CLOSE( erlang.getShape() / std::pow( erlang.getRate(), 2 ), std::pow( histogram.getStandardDeviation(), 2 ), 0.0002 );
	}
}
/// @endcond
