/*
**	Thirty-360 calculations.
**
**	For a "proper" description of this insane algorithm, please refer 
**	to the SIA manual for fixed income securities.
**
**	This work is derived from GNU software, so you should read
**	the GNU license agreement if you are plannning to use
**	them in commercial software.
*/
#include <stdio.h>
#include <common/portability.h>

#include "timelib.h"
#include "julian.h"

JULIAN_DAY 
thirty_360_start_date (end_date, length)
	JULIAN_DAY end_date; 
	int 	length;
{
	JULIAN_DAY start_date;

	int start_day, end_day;
	int start_month, end_month;
	int start_year, end_year;
	int num_days;

	/*
	**	Break down our dates.
	*/
	end_day = julian_to_gregorian_day (end_date);
	end_month = julian_to_gregorian_month (end_date);
	end_year = julian_to_gregorian_year (end_date);

	/*
	**	Number of thirty-360 days since the beginning of year 1
	**	less the length.
	*/
	num_days = (30 * ((12 * end_year) + end_month)) + end_day - length;

	/*
	**	Crude estimate.
	*/
	start_year = num_days / 360;
	start_month = (num_days - (360 * start_year)) / 30;
	start_day = num_days - (30 * start_month) - (360 * start_year);

	/*
	**	Adjustments.
	*/
	if ((start_day == 0 /* not valid */)
	    || ((start_day == 1) && (end_day >= 28)))
	{
		/*
		**	Roll back the month.
		*/
		if (start_month == 1)
		{
			--start_year;
			start_month = 12;
		}
		else /* (start_month != 1) */
		{
			--start_month;
		}

		/*
		**	Adjust the day.
		*/
		start_day = month_length (start_month, start_year);
	}

	start_date = gregorian_to_julian (start_day, start_month, 
					  start_year);

	return start_date;
}

JULIAN_DAY
thirty_360_end_date (start_date, length)
	JULIAN_DAY start_date; 
	int 	length;
{
	JULIAN_DAY end_date;

	int start_day, end_day;
	int start_month, end_month;
	int start_year, end_year;
	int num_days;
	
	/*
	**	Break down our dates.
	*/
	start_day = julian_to_gregorian_day (start_date);
	start_month = julian_to_gregorian_month (start_date);
	start_year = julian_to_gregorian_year (start_date);

	/*
	**	Adjust day of start date.
	**
	**	If it's the last day of the month, the start_date's 
	**	"day" must be 30.
	*/
	if (start_day == month_length (start_month, start_year))
	{
		start_day = 30;
	}
	
	/*
	**	Number of thirty-360 days since the beginning of year 1
	**	plus the length.
	*/
	num_days = (30 * ((12 * start_year) + start_month))
		+ start_day + length;

	/*
	**	Crude estimate.
	*/
	end_year = num_days / 360;
	end_month = (num_days - (360 * end_year)) / 30;
	end_day = num_days - (30 * end_month) - (360 * end_year);

	/*
	**	Adjustments.
	*/
	if ((end_day == 31) && (end_month == 2 /* February */))
	{
		if (is_leap_year (end_year))
		{
			end_day = 29;
		}
		else
		{
			end_day = 28;
		}
	}

	if ((end_day == 0 /* not valid */)
	    || ((end_day == 1) && (start_day >= 28)))
	{
		/*
		**	Roll forward the month.
		*/
		if (end_month == 12)
		{
			++end_year;
			end_month = 1;
		}
		else /* (end_month != 1) */
		{
			++end_month;
		}

		/*
		**	Adjust the day.
		*/
		end_day = month_length (end_month, end_year);
	}

	end_date = gregorian_to_julian (end_day, end_month, end_year);

	return end_date;
}

int 
thirty_360_length (start_date, end_date)
	JULIAN_DAY start_date, end_date;
{
	int length;
	int start_day, end_day;
	int start_month, end_month;
	int start_year, end_year;
	int num_months, num_days;
	
	/*
	**	Break down our dates.
	*/
	start_day = julian_to_gregorian_day (start_date);
	end_day = julian_to_gregorian_day (end_date);
	start_month = julian_to_gregorian_month (start_date);
	end_month = julian_to_gregorian_month (end_date);
	start_year = julian_to_gregorian_year (start_date);
	end_year = julian_to_gregorian_year (end_date);

	num_months = (12 * (end_year - start_year)) + 
		(end_month - start_month);

 	/*
	**	If it's the last day of the month, the start_date's 
	**	"day" must be 30.
	*/
	if (start_day == month_length (start_month, start_year))
	{
		start_day = 30;
	}

	/*
	**	If the day of the end_date is 31 and our adjusted start day
	**	is 30, change the end_day to 30!
	*/
	if ((end_day == 31) && (start_day == 30))
	{
		end_day = 30;
	}

	num_days = end_day - start_day;

	length = (num_months * 30) + num_days;

	return length;
}





