#include "HullWhiteModelTests.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//  CALCULATE SPREADS WITH HULL WHITE METHOD AND NO ARROW DEBREU PRICES
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HullWhiteModelTests::HullWhiteModelGetGridpoints(d_matrix* output, int* gridpoints_per_slice, double* sd_spread, int* poly_order, d_vector* times, double* t1_discount_domestic, double* t1_discount_foreign, d_vector* accrual_factors_domestic, d_vector* accrual_factors_foreign, d_vector* forward_libors_domestic, d_vector* forward_libors_foreign, d_vector* C_xstarxstar, d_vector* C_ystarystar, d_vector* C_zstarzstar, d_vector* C_xstarystar, d_vector* C_xstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_variance, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_discount_domestic, *t1_discount_foreign };
	std::array<d_vector, 2> accrual_factors = { *accrual_factors_domestic, *accrual_factors_foreign };
	std::array<d_vector, 2> forward_libors = { *forward_libors_domestic, *forward_libors_foreign };
	std::array<d_vector, 3> variances = { *C_xstarxstar, *C_ystarystar, *C_zstarzstar };
	std::array<d_vector, 3> covariances = { *C_xstarystar, *C_xstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridpoints_per_slice, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_variance, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.get_grids(*output, (size_t) *which_process);
}

bool HullWhiteModelTests::HullWhiteModelGetGridpointsCheck(XLOPER12* exception_output, int* gridpoints_per_slice, double* sd_spread, int* poly_order, d_vector* times, double* t1_discount_domestic, double* t1_discount_foreign, d_vector* accrual_factors_domestic, d_vector* accrual_factors_foreign, d_vector* forward_libors_domestic, d_vector* forward_libors_foreign, d_vector* C_xstarxstar, d_vector* C_ystarystar, d_vector* C_zstarzstar, d_vector* C_xstarystar, d_vector* C_xstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_variance, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_discount_domestic, *t1_discount_foreign };
	std::array<d_vector, 2> accrual_factors = { *accrual_factors_domestic, *accrual_factors_foreign };
	std::array<d_vector, 2> forward_libors = { *forward_libors_domestic, *forward_libors_foreign };
	std::array<d_vector, 3> variances = { *C_xstarxstar, *C_ystarystar, *C_zstarzstar };
	std::array<d_vector, 3> covariances = { *C_xstarystar, *C_xstarzstar, *C_ystarzstar };

	if (((size_t)*which_process < 0) || ((size_t)*which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridpoints_per_slice, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_variance, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelGetOptionValue(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process, int* slice_index, double* strike)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	*output =  mfm.get_option_in_arrears_price((size_t) *slice_index, (size_t) *which_process, *strike);
}

bool HullWhiteModelTests::HullWhiteModelGetOptionValueCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process, int* slice_index, double* strike)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*which_process < 0) || ((size_t) *which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");

	if (((size_t) *slice_index < 1) || ((size_t) *slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelGetOptionValueInverse(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process, int* slice_index, double* value)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	*output = mfm.get_option_in_arrears_price_inverse((size_t)*slice_index, (size_t) *which_process, *value);
}

bool HullWhiteModelTests::HullWhiteModelGetOptionValueInverseCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process, int* slice_index, double* value)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*which_process < 0) || ((size_t)*which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelGetJ0(d_matrix* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();

	mfm.get_J0_grids(*output, (size_t)*which_process);
}

bool HullWhiteModelTests::HullWhiteModelGetJ0Check(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*which_process < 0) || ((size_t)*which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

bool HullWhiteModelTests::HullWhiteModelDomFgnSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelGetSpot(d_matrix* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();

	mfm.get_spot_grids(*output, (size_t)*which_process);
}

bool HullWhiteModelTests::HullWhiteModelGetSpotCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	if (((size_t)*which_process < 0) || ((size_t)*which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// CALCULATE SPREADS WITH HULL WHITE METHOD AND ARROW DEBREU PRICES, J0 NOT GENERALISED
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void HullWhiteModelTests::HullWhiteModelDomFXSpread(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();
	*output = mfm.value_dom_fx_spread((size_t)*slice_index, *A, *B);

}

bool HullWhiteModelTests::HullWhiteModelDomFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelFgnFXSpread(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();
	*output = mfm.value_fgn_fx_spread((size_t)*slice_index, *A, *B);
}

bool HullWhiteModelTests::HullWhiteModelFgnFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelDomFgnSpread(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* C_ystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *C_ystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();
	*output = mfm.value_dom_fgn_spread((size_t)*slice_index, *A, *B);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// CALCULATE SPREADS WITH HULL WHITE METHOD AND ARROW DEBREU PRICES, J0 NOT GENERALISED
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HullWhiteModelTests::ADHullWhiteModelDomFXSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };
	
	HWMFMTwoCurrency<HullWhiteCalibratorADPrices> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADPrices::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_dom_fx_spread((size_t)(*slice_indices)[i], *A, *B);
	}
}

bool HullWhiteModelTests::ADHullWhiteModelDomFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t)(*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADHullWhiteModelFgnFXSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADPrices::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_fgn_fx_spread((size_t)(*slice_indices)[i], *A, *B);
	}
}

bool HullWhiteModelTests::ADHullWhiteModelFgnFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t)(*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADHullWhiteModelDomFgnSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADPrices::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_dom_fgn_spread((size_t)(*slice_indices)[i], *A, *B);
	}

}

bool HullWhiteModelTests::ADHullWhiteModelDomFgnSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t) (*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// CALCULATE SPREADS WITH HULL WHITE METHOD AND ARROW DEBREU PRICES, With general J0
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HullWhiteModelTests::ADGeneralHullWhiteModelDomFXSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADGeneral::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_dom_fx_spread((size_t)(*slice_indices)[i], *A, *B);
	}
}

bool HullWhiteModelTests::ADGeneralHullWhiteModelDomFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t)(*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADGeneralHullWhiteModelFgnFXSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADGeneral::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_fgn_fx_spread((size_t)(*slice_indices)[i], *A, *B);
	}
}

bool HullWhiteModelTests::ADGeneralHullWhiteModelFgnFXSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t)(*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADGeneralHullWhiteModelDomFgnSpread(d_vector* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADGeneral::construct);

	mfm.calibrate();
	output->resize(slice_indices->size());

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		(*output)[i] = mfm.value_dom_fgn_spread((size_t)(*slice_indices)[i], *A, *B);
	}
}

bool HullWhiteModelTests::ADGeneralHullWhiteModelDomFgnSpreadCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, d_vector* slice_indices, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	for (size_t i = 0; i < slice_indices->size(); ++i)
	{
		if (((size_t)(*slice_indices)[i] < 1) || ((size_t)(*slice_indices)[i] > times->size()))
			throw std::runtime_error("Slice index " + std::to_string((*slice_indices)[i]) + "is not valid.");
	}

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADGeneralHullWhiteModelGetJ0(d_matrix* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADGeneral::construct);

	mfm.calibrate();

	mfm.get_J0_grids(*output, (size_t)*which_process);
}

bool HullWhiteModelTests::ADGeneralHullWhiteModelGetJ0Check(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* which_process)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };


	if (((size_t)*which_process < 0) || ((size_t)*which_process > 2))
		throw std::runtime_error("process number must be 0, 1 or 2");


	HWMFMTwoCurrency<HullWhiteCalibratorADGeneral>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// Debugging functions
//
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void HullWhiteModelTests::HullWhiteModelDomFXxyslice(d_matrix* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, int* value_index, double* A, double* B)
{

	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();
	mfm.get_domfx_xy_slice(*output, *A, *B, *slice_index, *value_index);
}

bool HullWhiteModelTests::HullWhiteModelDomFXxysliceCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, int* value_index, double* A, double* B)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	if (*value_index > * slice_index)
		throw std::runtime_error("Tried to retrieve rollback value from index higher than the slice value was originally rolling back from.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::ADHullWhiteModelGetArrowDebreu(d_matrix* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibratorADPrices::construct);

	mfm.calibrate();

	mfm.get_arrow_debreu(*output, *slice_index);

}

bool HullWhiteModelTests::ADHullWhiteModelGetArrowDebreuCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };


	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	HWMFMTwoCurrency<HullWhiteCalibratorADPrices>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}

void HullWhiteModelTests::HullWhiteModelValueOneArrowDebreu(double* output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, int* x, int* y, int* z)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	HWMFMTwoCurrency<HullWhiteCalibrator> mfm(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data, &HullWhiteCalibrator::construct);

	mfm.calibrate();
	*output =  mfm.value_single_arrow_debreu((size_t)*slice_index, (size_t) *x, (size_t)*y, (size_t) *z);
}

bool HullWhiteModelTests::HullWhiteModelValueOneArrowDebreuCheck(XLOPER12* exception_output, int* gridsize, double* sd_spread, int* poly_order, d_vector* times, double* t1_df_dom, double* t1_df_fgn, d_vector* accrual_dom, d_vector* accrual_fgn, d_vector* fwd_libors_dom, d_vector* fwd_libors_fgn, d_vector* Cxstarxstar, d_vector* Cystarystar, d_vector* Czstarzstar, d_vector* Cxstarystar, d_vector* Cxstarzstar, d_vector* Cystarzstar, d_vector* y_larrow_mean, d_vector* y_larrow_var, double* spot_fx, d_matrix* mu_data, d_matrix* C_data, int* slice_index, int* x , int* y, int* z)
{
	std::array<double, 2> t1_discounts = { *t1_df_dom, *t1_df_fgn };
	std::array<d_vector, 2> accrual_factors = { *accrual_dom, *accrual_fgn };
	std::array<d_vector, 2> forward_libors = { *fwd_libors_dom, *fwd_libors_fgn };
	std::array<d_vector, 3> variances = { *Cxstarxstar, *Cystarystar, *Czstarzstar };
	std::array<d_vector, 3> covariances = { *Cxstarystar, *Cxstarzstar, *Cystarzstar };

	if (((size_t)*slice_index < 1) || ((size_t)*slice_index > times->size()))
		throw std::runtime_error("Slice index " + std::to_string(*slice_index) + "is not valid.");

	if ((*x < 0) || (*x > (*gridsize - 1)))
		throw std::runtime_error("The x point is not valid for this grid size.");

	if ((*y < 0) || (*y > (*gridsize - 1)))
		throw std::runtime_error("The y point is not valid for this grid size.");

	if ((*z < 0) || (*z > (*gridsize - 1)))
		throw std::runtime_error("The z point is not valid for this grid size.");

	HWMFMTwoCurrency<HullWhiteCalibrator>::constructor_check(
		(size_t)*gridsize, *sd_spread, (size_t)*poly_order, *times,
		t1_discounts, accrual_factors, forward_libors, variances, covariances,
		*y_larrow_mean, *y_larrow_var, *spot_fx,
		*mu_data, *C_data);

	return true;
}
