#include "InterestRateTimeSliceProcess.h" 

InterestRateTimeSliceProcess::InterestRateTimeSliceProcess(
	std::unique_ptr<TimeSliceProcess> base_process, std::unique_ptr<OptionPricer> pricer,
	double accrual_factor, double discount_factor, double t1_discount_factor)
	:
	TimeSliceProcess(std::move(base_process), std::move(pricer)),
	m_t1_discount_factor(t1_discount_factor),
	m_discount_factor(discount_factor),
	m_accrual_factor(accrual_factor)
{}

InterestRateTimeSliceProcess::InterestRateTimeSliceProcess(
	std::unique_ptr<TimeSliceProcess> base_process, const std::string& ub_error,
	double beta, double mux_price, double muy_price, double Cxx_price, double Cyy_price, double Cxy_price, 
	double accrual_factor, double discount_factor, double t1_discount_factor)
	:
InterestRateTimeSliceProcess(
	std::move(base_process),
	std::move(std::unique_ptr<HullWhiteOptionPricer>(new HullWhiteOptionPricer(ub_error, -1.0 / accrual_factor, t1_discount_factor, beta,
		[&] (double k) -> double { return 1.0 + m_accrual_factor * k; },
		mux_price, muy_price, Cxx_price, Cyy_price, Cxy_price))),
	accrual_factor, discount_factor, t1_discount_factor)
{}

InterestRateTimeSliceProcess::InterestRateTimeSliceProcess(std::unique_ptr<TimeSliceProcess> base_process, const std::string& ub_error, double time, double this_caplet_volatility, double this_fwd_libor, double accrual_factor, double discount_factor, double t1_discount_factor)
	:
	InterestRateTimeSliceProcess(
		std::move(base_process),
		std::move(std::unique_ptr<CapletOptionPricer>(new CapletOptionPricer(ub_error, t1_discount_factor, time, discount_factor, this_caplet_volatility, accrual_factor, this_fwd_libor))),
		accrual_factor, discount_factor, t1_discount_factor)
{
}

std::unique_ptr<InterestRateTimeSliceProcess> InterestRateTimeSliceProcess::construct(std::unique_ptr<TimeSliceProcess> base_process, const std::string& ub_error, double time, double this_caplet_volatility, double this_fwd_libor, double accrual_factor, double discount_factor, double t1_discount_factor)
{
	return std::move(std::unique_ptr<InterestRateTimeSliceProcess>(new InterestRateTimeSliceProcess(std::move(base_process), ub_error, time, this_caplet_volatility, this_fwd_libor, accrual_factor, discount_factor, t1_discount_factor)));
}

std::unique_ptr<InterestRateTimeSliceProcess> InterestRateTimeSliceProcess::construct(std::unique_ptr<TimeSliceProcess> base_process, const std::string& ub_error, double beta, double mux_price, double muy_price, double Cxx_price, double Cyy_price, double Cxy_price, double accrual_factor, double discount_factor, double t1_discount_factor)
{
	return std::move(std::unique_ptr<InterestRateTimeSliceProcess>(new InterestRateTimeSliceProcess(std::move(base_process), ub_error, beta, mux_price, muy_price, Cxx_price, Cyy_price, Cxy_price, accrual_factor, discount_factor, t1_discount_factor)));
}

double InterestRateTimeSliceProcess::get_t1_discount_factor() const
{
	return m_t1_discount_factor;
}

double InterestRateTimeSliceProcess::get_discount_factor() const
{
	return m_discount_factor;
}

double InterestRateTimeSliceProcess::get_accrual_factor() const
{
	return m_accrual_factor;
}

std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> InterestRateTimeSliceProcess::construct(
	double grid_spread, size_t number_of_gridpoints, const MFMBase* model, double mean_reversion_param,
	gelib::d_vector const& times, gelib::d_vector const& process_volatilities_squared, gelib::d_vector const& process_variances,
	gelib::d_vector const& forward_libors, gelib::d_vector const& caplet_volatilities, gelib::d_vector const& accrual_factors, double t1_discount_factor)
{
	// assume currencies zero-centered at each timeslice
	gelib::d_vector grid_centers(times.size());
	std::fill(grid_centers.begin(), grid_centers.end(), 0.0);

	std::vector<std::unique_ptr<TimeSliceProcess>> processes = TimeSliceProcess::construct(grid_spread, number_of_gridpoints, model, mean_reversion_param, times, process_volatilities_squared, process_variances, grid_centers);

	std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> new_processes;

	new_processes.resize(processes.size());

	std::string err = "Digital caplet price cannot exceed discount factor at T1 (Currency Process)";
	double discount_factor;

	new_processes[0] = InterestRateTimeSliceProcess::construct(std::move(processes[0]), err, 0.0, 0.0, 0.0, 0.0, 1.0, t1_discount_factor);

	new_processes[1] = InterestRateTimeSliceProcess::construct(std::move(processes[1]), err, processes[1]->time(), caplet_volatilities[0], forward_libors[0], accrual_factors[0], t1_discount_factor, t1_discount_factor);

	discount_factor = t1_discount_factor / (1.0 + accrual_factors[0] * forward_libors[0]);

	for (size_t i = 2; i < processes.size(); ++i)
	{
		new_processes[i] = InterestRateTimeSliceProcess::construct(std::move(processes[i]), err, processes[i]->time(), caplet_volatilities[i - 1], forward_libors[i - 1], accrual_factors[i - 1], discount_factor, t1_discount_factor);
		discount_factor = discount_factor / (1.0 + accrual_factors[i - 1] * forward_libors[i - 1]);
	}

	return std::move(new_processes);
}

std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> InterestRateTimeSliceProcess::construct(
	double grid_spread, size_t number_of_gridpoints, const MFMBase* model, double mean_reversion_param,
	gelib::d_vector const& times, gelib::d_vector const& forward_libors, gelib::d_vector const& process_volatilities_squared, gelib::d_vector const& process_variances,
	gelib::d_vector const& accrual_factors, double t1_discount_factor, 
	gelib::d_vector const& mux_price, gelib::d_vector const& muy_price, gelib::d_vector const& Cxx_price, gelib::d_vector const& Cyy_price, gelib::d_vector const& Cxy_price)
{
	// assume currencies zero-centered at each timeslice
	gelib::d_vector grid_centers(times.size());
	std::fill(grid_centers.begin(), grid_centers.end(), 0.0);

	std::vector<std::unique_ptr<TimeSliceProcess>> processes = TimeSliceProcess::construct(grid_spread, number_of_gridpoints, model, mean_reversion_param, times, process_volatilities_squared, process_variances, grid_centers);

	std::vector<std::unique_ptr<InterestRateTimeSliceProcess>> new_processes;

	new_processes.resize(processes.size());

	std::string err = "Hull White digital caplet price cannot exceed discount factor at T1 (Currency Process)";
	double beta = -1.0;
	double discount_factor;

	new_processes[0] = InterestRateTimeSliceProcess::construct(std::move(processes[0]), err, beta, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, t1_discount_factor);

	new_processes[1] = InterestRateTimeSliceProcess::construct(std::move(processes[1]), err, beta, mux_price[0], muy_price[0], Cxx_price[0], Cyy_price[0], Cxy_price[0], accrual_factors[0], t1_discount_factor, t1_discount_factor);

	discount_factor = t1_discount_factor / (1.0 + accrual_factors[0] * forward_libors[0]);

	for (size_t i = 2; i < processes.size(); ++i)
	{
		new_processes[i] = InterestRateTimeSliceProcess::construct(std::move(processes[i]), err, beta, mux_price[i - 1], muy_price[i - 1], Cxx_price[i - 1], Cyy_price[i - 1], Cxy_price[i - 1], accrual_factors[i - 1], discount_factor, t1_discount_factor);
		discount_factor = discount_factor / (1.0 + accrual_factors[i - 1] * forward_libors[i - 1]);
	}

	return std::move(new_processes);
}

void InterestRateTimeSliceProcess::constructor_check(
	double grid_spread, size_t number_of_gridpoints, double mean_reversion_param,
	gelib::d_vector const& process_volatilities, double discount_factor_t1, gelib::d_vector const& forward_libors,
	gelib::d_vector const& caplet_volatilities, gelib::d_vector const& accrual_factors)
{
	std::string err;

	const size_t max_poly_order = gelib_impl::Polynomial1d::MAXIMUM_DEGREE;

	std::array<size_t, 4> sizes = { forward_libors.size(), caplet_volatilities.size(), process_volatilities.size(), accrual_factors.size() };

	for (size_t i = 0; i < accrual_factors.size(); i++)
	{
		if (accrual_factors[i] < 0.0)
			err = "Accrual factor " + std::to_string(i) + " is negative.";
	}
	if (grid_spread <= 0)
		err = "Spread should be positive.";
	if (!((0.0 <= discount_factor_t1) && (discount_factor_t1 <= 1.0)))
		err = "T1 Discount factor should be between 0 and 1";
	if (!std::equal(sizes.begin() + 1, sizes.end(), sizes.begin()))
		err = "The vectors for accrual factors, times, forward libors, caplet volatilities and x volatilities should all be equal lengths.";
	if (number_of_gridpoints > MFMBase::MAXIMUM_GRID_POINTS)
		err = "Maximum grid points exceeded, was a negative parameter supplied?";

	gelib_impl::IO::throw_on_input_error(err);
}