#include "mpdf_harness.h"
#include "base/bdmbase.h"
#include "base/user_info.h"
#include "stat/exp_family.h"
#include "mat_checks.h"
#include "test_util.h"
#include "UnitTest++.h"

namespace bdm {

template<>
const ParticularUI<mpdf_harness> &ParticularUI<mpdf_harness>::factory (
    ParticularUI<mpdf_harness> ( "mpdf_harness" ) );

void mpdf_harness::test_config ( const char *config_file_name ) {
	RV::clear_all();

	UIFile in ( config_file_name );
	Array<mpdf_harness *> input;
	UI::get ( input, in, "data", UI::compulsory );
	int sz = input.size();
	CHECK ( sz > 0 );
	for ( int i = 0; i < sz; ++i ) {
		input ( i )->test ( config_file_name, i );
	}
}

void mpdf_harness::from_setting ( const Setting &set ) {
	hmpdf = UI::build<mpdf> ( set, "mpdf", UI::compulsory );
	UI::get ( mean, set, "mean", UI::compulsory );
	UI::get ( variance, set, "variance", UI::compulsory );

	if ( !UI::get ( cond, set, "cond", UI::optional ) ) {
		cond = zeros ( hmpdf->dimensionc() );
	}

	UI::get ( nsamples, set, "nsamples", UI::optional );
	UI::get ( R, set, "R", UI::optional );
	UI::get ( tolerance, set, "tolerance", UI::optional );
}

void mpdf_harness::test ( const char *config_name, int idx ) {
	CurrentContext cc ( config_name, idx );
	check_mean();
	if ( R.rows() > 0 ) {
		check_covariance();
	}
}

void mpdf_harness::check_mean() {
	vec delta = make_close_tolerance ( variance, nsamples );

	int tc = 0;
	Array<vec> actual(CurrentContext::max_trial_count);
	do {
		mat smp = hmpdf->samplecond_m ( cond, nsamples );
		vec emu = smp * ones ( nsamples ) / nsamples;
		actual( tc ) = emu;
		++tc;
	} while ( ( tc < CurrentContext::max_trial_count ) &&
		  !UnitTest::AreClose ( mean, actual( tc - 1 ), delta ) );
	if ( ( tc == CurrentContext::max_trial_count ) &&
	     ( !UnitTest::AreClose ( mean, actual( CurrentContext::max_trial_count - 1 ), delta ) ) ) {
		UnitTest::MemoryOutStream stream;
		stream << CurrentContext::format_context(__LINE__) << "expected " << mean << " +/- " << delta << " but was " << actual;

		UnitTest::TestDetails details(*UnitTest::CurrentTest::Details(), 0, false);

		UnitTest::CurrentTest::Results()->OnTestFailure ( details, stream.GetText() );
       }
}

void mpdf_harness::check_covariance() {
	int tc = 0;
	Array<mat> actual(CurrentContext::max_trial_count);
	do {
		mat smp = hmpdf->samplecond_m ( cond, nsamples );
		vec emu = smp * ones ( nsamples ) / nsamples;
		mat er = ( smp * smp.T() ) / nsamples - outer_product ( emu, emu );
		actual( tc ) = er;
		++tc;
	} while ( ( tc < CurrentContext::max_trial_count ) &&
		  !UnitTest::AreClose ( R, actual( tc - 1 ), tolerance ) );
	if ( ( tc == CurrentContext::max_trial_count ) &&
	     ( !UnitTest::AreClose ( R, actual( CurrentContext::max_trial_count - 1 ), tolerance ) ) ) {
		UnitTest::MemoryOutStream stream;
		stream << CurrentContext::format_context(__LINE__) << "expected " << R << " +/- " << tolerance << " but was " << actual;

		UnitTest::TestDetails details(*UnitTest::CurrentTest::Details(), 0, false);

		UnitTest::CurrentTest::Results()->OnTestFailure ( details, stream.GetText() );
       }
}

}
