#include "CalibratorSingleCurrency.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//===================================================================================================================
//
// Constructor 
//
//===================================================================================================================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SingleCurrencyCalibrator::initialise(const std::vector<size_t>& grid_sizes, const MFMBase* model)
{
	TimeSliceCalibrator::initialise(grid_sizes, model);
	m_arrow_debreu.resize(grid_sizes[0]);
	std::fill(m_arrow_debreu.begin(), m_arrow_debreu.end(), 1.0);
	m_calibration_factors.resize(grid_sizes[0]);
}

std::unique_ptr<SingleCurrencyCalibrator> SingleCurrencyCalibrator::construct()
{
	std::unique_ptr<SingleCurrencyCalibrator> ad = std::unique_ptr<SingleCurrencyCalibrator>(
		new SingleCurrencyCalibrator());
	return std::move(ad);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//===================================================================================================================
//
// All necessary work at the current time step
//
//===================================================================================================================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void SingleCurrencyCalibrator::calibrate_at_T0(
	std::vector<std::unique_ptr<TimeSliceProcess>>& processes_at_T0)
{
	calibrate_T0(static_cast<const InterestRateTimeSliceProcess*>(processes_at_T0[0].get()));
}

void SingleCurrencyCalibrator::calibrate_T0(const InterestRateTimeSliceProcess* currency)
{
	assert(m_calibration_factors.size() == 1);
	m_calibration_factors[0] = currency->get_t1_discount_factor();
}

void SingleCurrencyCalibrator::calibrate(std::vector<std::unique_ptr<TimeSliceProcess>>& processes)
{
	calibrate(static_cast<const InterestRateTimeSliceProcess*>(processes[0].get()));
}

void SingleCurrencyCalibrator::calibrate(const InterestRateTimeSliceProcess* currency)
{
	std::transform(currency->get_spot()->begin(), currency->get_spot()->end(), m_calibration_factors.begin(),
		[&](double L) -> double {return (1.0 / (1.0 + currency->get_accrual_factor() * L)); });

	// multiply each calibration factor by corresponding arrow debreu
	std::transform(m_calibration_factors.begin(), m_calibration_factors.end(), m_arrow_debreu.begin(), m_calibration_factors.begin(),
		std::multiplies<double>());
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//===================================================================================================================
//
//  All necessary work at the next time step
//
//===================================================================================================================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void SingleCurrencyCalibrator::calibrate_next(TimeSliceCalibrator& next_calibrator, std::vector<std::unique_ptr<TimeSliceProcess>>& current_processes, std::vector<std::unique_ptr<TimeSliceProcess>>& next_processes)
{

	gelib::d_vector* J0x = next_processes[0]->get_J0_grid();
	const gelib::d_vector* next_xgrid = next_processes[0]->get_grid();
	const gelib::d_vector* means_grid = current_processes[0]->get_conditional_means();

	// Arrow debreu for the next grid
	gelib::d_matrix b_in(next_processes[0]->total_grid_points(), current_processes[0]->total_grid_points());
	gelib::d_vector cf;

	// every row is identical
	for (size_t j = 0; j < b_in.rows(); ++j)
	{
		cf = m_calibration_factors;
		b_in.insert_vector(cf, (int) j, -1);
		static_cast<SingleCurrencyCalibrator&>(next_calibrator).m_arrow_debreu[j] = 0.0;
	}

	gelib::basis_expectation_sum_1d(static_cast<SingleCurrencyCalibrator&>(next_calibrator).m_arrow_debreu, b_in, *next_xgrid, *means_grid,
		current_processes[0]->get_conditional_standard_deviation(), MFMBase::minus_inf, MFMBase::plus_inf, m_model->get_order(), "constant", MFMBase::DEBUG_ON);
	
	// J0 for the next grid
	double recip_sigma = 1.0 / current_processes[0]->get_conditional_standard_deviation();


	for (size_t j = 0; j < next_processes[0]->total_grid_points(); ++j)
	{
		(*J0x)[j] = 0.0;
		for (size_t i = 0; i < m_calibration_factors.size(); ++i)
			(*J0x)[j] += m_calibration_factors[i] * Algorithm::normal_CDF(((*means_grid)[i] - (*next_xgrid)[j]) * recip_sigma);
	}

}

double SingleCurrencyCalibrator::weight_vector(gelib::d_vector& in)
{
	return std::inner_product(m_calibration_factors.begin(), m_calibration_factors.end(), in.begin(), 0.0);
}

double SingleCurrencyCalibrator::roll_back(std::vector<double> const& spread) const
{
	return std::inner_product(m_calibration_factors.begin(), m_calibration_factors.end(), spread.begin(), 0.0);
}

std::vector<double>& SingleCurrencyCalibrator::get()
{
	return m_arrow_debreu;
}