<!--

////////////////////////////////////////////////////////
// Copyright 2003, Timothy James Forsythe, all rights reserved.
// Permission granted to use, copy, modify, and distribute so long as
// the above copyright and this permission statement are retained in all
// copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
//
// based on the algorithms of Scott E. Lee
////////////////////////////////////////////////////////
// $selId: jewish.c,v 2.0 1995/10/24 01:13:06 lees Exp $
// Copyright 1993-1995, Scott E. Lee, all rights reserved.
// Permission granted to use, copy, modify, distribute and sell so long as
// the above copyright and this permission statement are retained in all
// copies.  THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
////////////////////////////////////////////////////////

var HEBREW_CORRELATION = 347997;

var HALAKIM_PER_DAY           = 25920;
var HALAKIM_PER_LUNAR_CYCLE   = (29 * HALAKIM_PER_DAY) + 13753;			// 765433
var HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * ((12 * 19) + 7);	// 179876755

var HEBREW_MO_LEN = new Array(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);

function MoladDate(moladDay, moladHalakim)
{
  this.moladDay     = moladDay;
  this.moladHalakim = moladHalakim;
}

function MetonicDate(metonicCycle, metonicYear, moladDay, moladHalakim)
{
  this.metonicCycle = metonicCycle;
  this.metonicYear  = metonicYear;
  this.moladDay     = moladDay;
  this.moladHalakim = moladHalakim;
}

function TishriDate(metonicCycle, metonicYear, moladDay, moladHalakim, tishri)
{
  this.metonicCycle = metonicCycle;
  this.metonicYear  = metonicYear;
  this.moladDay     = moladDay;
  this.moladHalakim = moladHalakim;
  this.tishri       = tishri;
}

function HDate(d,m,mstr,y)
{
  this.d    = d;
  this.m    = m;
  this.mstr = mstr;
  this.y    = y;
}

function trunc(val)
{
  if (val >= 0)
  {
    return Math.floor(val);
  }
  else
  {
    return Math.ceil(val);
  }
}


///////////////////////////////////////////////////////////////////////////   
// Given the year within the 19 year metonic cycle and the time of a molad
// (new moon) which starts that year, this routine will calculate what day
// will be the actual start of the year (Tishri 1 or Rosh Ha-Shanah).  This
// first day of the year will be the day of the molad unless one of 4 rules
// (called dehiyyot) delays it.  These 4 rules can delay the start of the
// year by as much as 2 days.
// 
function Tishri1(metonicYear, moladDay, moladHalakim)
{
    var SUNDAY    = 0;
    var MONDAY    = 1;
    var TUESDAY   = 2;
    var WEDNESDAY = 3;
    var THURSDAY  = 4;
    var FRIDAY    = 5;
    var SATURDAY  = 6;

    var HALAKIM_PER_HOUR = 1080;
    var NOON             = 18 * HALAKIM_PER_HOUR;
    var AM3_11_20        = (9 * HALAKIM_PER_HOUR) + 204;
    var AM9_32_43        = (15 * HALAKIM_PER_HOUR) + 589;

    var tishri1;
    var dow;
    var leapYear = false;
    var lastWasLeapYear = false;
  
    if (   metonicYear == 2  || metonicYear == 5  || metonicYear == 7
	|| metonicYear == 10 || metonicYear == 13 || metonicYear == 16
	|| metonicYear == 18)
    {
      leapYear = true;
    }
	        
    if (   metonicYear == 3  || metonicYear == 6  || metonicYear == 8 
        || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 
        || metonicYear == 0)
    {
      lastWasLeapYear = true;
    }

    tishri1 = moladDay;
    dow = tishri1 % 7;

    // Apply rules 2, 3 and 4. 
    if (   (moladHalakim >= NOON) 
        || (!leapYear       && (dow == TUESDAY) && (moladHalakim >= AM3_11_20)) 
        || (lastWasLeapYear && (dow == MONDAY)  && (moladHalakim >= AM9_32_43))
       )
    {
	tishri1++;
	dow++;
	if (dow == 7) 
	{
	  dow = 0;
	}
    }

    // Apply rule 1 after the others because it can cause an additional delay of one day.
    if ((dow == WEDNESDAY) || (dow == FRIDAY) || (dow == SUNDAY)) 
    {
	tishri1++;
    }

    return tishri1;
}

///////////////////////////////////////////////////////////////////////////   
// Given a metonic cycle number, calculate the date and time of the molad
// (new moon) that starts that cycle.  Since the length of a metonic cycle
// is a constant, this is a simple calculation, except that it requires an
// intermediate value which is bigger that 32 bits.  Because this
// intermediate value only needs 36 to 37 bits and the other numbers are
// constants, the process has been reduced to just a few steps.
//
function MoladOfMetonicCycle(metonicCycle)
{
    var NEW_MOON_OF_CREATION = 31524;

    var moladDate = new MoladDate(0,0);
    
    var r1;

    // Start with the time of the first molad after creation.
    r1 = NEW_MOON_OF_CREATION + (metonicCycle * HALAKIM_PER_METONIC_CYCLE);

    moladDate.moladDay     = trunc(r1 / HALAKIM_PER_DAY);
    moladDate.moladHalakim = r1 % HALAKIM_PER_DAY;
    
    return moladDate;
}


///////////////////////////////////////////////////////////////////////////   
// Given a day number, find the molad of Tishri (the new moon at the start
// of a year) which is closest to that day number.  It's not really the
// *closest* molad that we want here.  If the input day is in the first two
// months, we want the molad at the start of the year.  If the input day is
// in the fourth to last months, we want the molad at the end of the year.
// If the input day is in the third month, it doesn't matter which molad is
// returned, because both will be required.  This type of "rounding" allows
// us to avoid calculating the length of the year in most cases.
//
function FindTishriMolad(inputDay)
{
    var metonicDate = new MetonicDate(0,0,0,0);
    var moladDate;

    // Estimate the metonic cycle number.  Note that this may be an under
    // estimate because there are 6939.6896 days in a metonic cycle not
    // 6940, but it will never be an over estimate.  The loop below will
    // correct for any error in this estimate. 
    metonicDate.metonicCycle = trunc((inputDay + 310) / 6940);

    // Calculate the time of the starting molad for this metonic cycle. 
    moladDate = MoladOfMetonicCycle(metonicDate.metonicCycle);
    
    metonicDate.moladDay     = moladDate.moladDay;
    metonicDate.moladHalakim = moladDate.moladHalakim;

    // If the above was an under estimate, increment the cycle number until
    // the correct one is found.  For modern dates this loop is about 98.6%
    // likely to not execute, even once, because the above estimate is
    // really quite close. 
    while (metonicDate.moladDay < (inputDay - 6940 + 310)) 
    {
	metonicDate.metonicCycle++;
	metonicDate.moladHalakim = metonicDate.moladHalakim + HALAKIM_PER_METONIC_CYCLE;
	metonicDate.moladDay     = metonicDate.moladDay     + trunc(metonicDate.moladHalakim / HALAKIM_PER_DAY);
	metonicDate.moladHalakim = metonicDate.moladHalakim % HALAKIM_PER_DAY;
    }

    // Find the molad of Tishri closest to this date. 
    for (metonicDate.metonicYear = 0; metonicDate.metonicYear < 18; metonicDate.metonicYear++) 
    {
	if (metonicDate.moladDay > (inputDay - 74)) 
	{
	    break;
	}
	metonicDate.moladHalakim = metonicDate.moladHalakim + (HALAKIM_PER_LUNAR_CYCLE * HEBREW_MO_LEN[metonicDate.metonicYear]);
	metonicDate.moladDay     = metonicDate.moladDay     + trunc(metonicDate.moladHalakim / HALAKIM_PER_DAY);
	metonicDate.moladHalakim = metonicDate.moladHalakim % HALAKIM_PER_DAY;
    }

    return metonicDate;
}

///////////////////////////////////////////////////////////////////////////   
// Given a year, find the number of the first day of that year and the date
// and time of the starting molad.
//
function FindStartOfYear(year)
{
    var yearOffset = new Array(0, 12, 24, 37, 49, 61, 74, 86, 99, 111, 123, 136, 148, 160, 173, 185, 197, 210, 222);

    var tdate = new TishriDate(0,0,0,0,0);
    
    tdate.metonicCycle = trunc((year - 1) / 19);
    tdate.metonicYear  = (year - 1) % 19;
    
    mdate = MoladOfMetonicCycle(tdate.metonicCycle);
    tdate.moladDay     = mdate.moladDay;
    tdate.moladHalakim = mdate.moladHalakim;

    tdate.moladHalakim = tdate.moladHalakim + (HALAKIM_PER_LUNAR_CYCLE * yearOffset[tdate.metonicYear]);
    tdate.moladDay     = tdate.moladDay     + trunc(tdate.moladHalakim / HALAKIM_PER_DAY);
    tdate.moladHalakim = tdate.moladHalakim % HALAKIM_PER_DAY;

    tdate.tishri = Tishri1(tdate.metonicYear, tdate.moladDay, tdate.moladHalakim);
    
    return tdate;
}

///////////////////////////////////////////////////////////////////////////   
// Given a serial day number (SDN), find the corresponding year, month and
// day in the Jewish calendar.  The three output values will always be
// modified.  If the input SDN is before the first day of year 1, they will
// all be set to zero, otherwise hdate.y will be > 0; hdate.m will be in the
// range 1 to 13 inclusive; hdate.d will be in the range 1 to 30 inclusive.
//
function JDNToHebrew(jdn)
{
  var MONTHS = new Array("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "Adar I", "Adar II", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul");


  var hdate = new HDate(0,0,0,0);

  var inputDay;
  var tishri1;
  var tishri1After;
  var yearLength;
  var metonicDate;

  if (jdn > HEBREW_CORRELATION) 
  {
    inputDay = jdn - HEBREW_CORRELATION;

    metonicDate = FindTishriMolad(inputDay);
      
    tishri1 = Tishri1(metonicDate.metonicYear, metonicDate.moladDay, metonicDate.moladHalakim);
    
    if (inputDay >= tishri1) 
    {
      // It found Tishri 1 at the start of the year. 
      hdate.y = (metonicDate.metonicCycle * 19) + metonicDate.metonicYear + 1;
      if (inputDay < (tishri1 + 59)) 
      {
        if (inputDay < (tishri1 + 30)) 
        {
          hdate.m = 1;
          hdate.d = inputDay - tishri1 + 1;
        } 
        else 
        {
          hdate.m = 2;
          hdate.d = inputDay - tishri1 - 29;
        }
        return hdate;
      }

      // We need the length of the year to figure this out, so find
      // Tishri 1 of the next year. 
      metonicDate.moladHalakim = metonicDate.moladHalakim + (HALAKIM_PER_LUNAR_CYCLE * HEBREW_MO_LEN[metonicDate.metonicYear]);
      metonicDate.moladDay     = metonicDate.moladDay     + trunc(metonicDate.moladHalakim / HALAKIM_PER_DAY);
      metonicDate.moladHalakim = metonicDate.moladHalakim % HALAKIM_PER_DAY;
	
      tishri1After = Tishri1((metonicDate.metonicYear + 1) % 19, metonicDate.moladDay, metonicDate.moladHalakim);
    }  
    else
    {
      // It found Tishri 1 at the end of the year. 
      hdate.y = (metonicDate.metonicCycle * 19) + metonicDate.metonicYear;
	
      if (inputDay >= (tishri1 - 177)) 
      {
        // It is one of the last 6 months of the year. 
        if (inputDay > (tishri1 - 30)) 
        {
          hdate.m = 13;
          hdate.d = inputDay - tishri1 + 30;
        } 
        else 
        {
          if (inputDay > (tishri1 - 60)) 
          {
            hdate.m = 12;
            hdate.d = inputDay - tishri1 + 60;
          } 
          else 
          {
            if (inputDay > (tishri1 - 89)) 
            {
              hdate.m = 11;
              hdate.d = inputDay - tishri1 + 89;
            } 
            else 
            {
              if (inputDay > (tishri1 - 119)) 
              {
                hdate.m = 10;
                hdate.d = inputDay - tishri1 + 119;
              } 
              else 
              {
                if (inputDay > (tishri1 - 148)) 
                {
                  hdate.m = 9;
                  hdate.d = inputDay - tishri1 + 148;
                } 
                else 
                {
                  hdate.m = 8;
                  hdate.d = inputDay - tishri1 + 178;
                }
              }
            }
          }
        }
        
	return hdate;
      } 
      else 
      {
        if (HEBREW_MO_LEN[(hdate.y - 1) % 19] == 13) 
        {
          hdate.m = 7;
          hdate.d = inputDay - tishri1 + 207;
          if (hdate.d > 0) 
          {
            return hdate;
          }
	
          hdate.m = hdate.m - 1;
          hdate.d = hdate.d + 30;
	    
          if (hdate.d > 0) 
          {
            return hdate;
          }
		
          hdate.m = hdate.m - 1;
          hdate.d = hdate.d + 30;
        } 
        else 
        {
          hdate.m = 6;
          hdate.d = inputDay - tishri1 + 207;
	    
          if (hdate.d > 0) 
          {
            return hdate;
          }
		
          hdate.m = hdate.m - 1;
          hdate.d = hdate.d + 30;
        }
	
	if (hdate.d > 0) 
	{
	  return hdate;
	}
	
	hdate.m = hdate.m - 1;
	hdate.d = hdate.d + 29;
	
	if (hdate.d > 0) 
	{
	  return hdate;
        }

        // We need the length of the year to figure this out, so find
	// Tishri 1 of this year. 
        tishri1After = tishri1;

        metonicDate = FindTishriMolad(metonicDate.moladDay - 365)
        
        tishri1 = Tishri1(metonicDate.metonicYear, metonicDate.moladDay, metonicDate.moladHalakim);
      }
    }
  
    yearLength = tishri1After - tishri1;
    metonicDate.moladDay = inputDay - tishri1 - 29;

    if ((yearLength == 355) || (yearLength == 385))
    {
      // Heshvan has 30 days 
      if (metonicDate.moladDay <= 30) 
      { 
        hdate.m = 2;
        hdate.d = metonicDate.moladDay;
        return hdate;
      }
      
      metonicDate.moladDay = metonicDate.moladDay - 30;
    } 
    else 
    {
      // Heshvan has 29 days 
      if (metonicDate.moladDay <= 29) 
      {
        hdate.m = 2;
        hdate.d = metonicDate.moladDay;
        return hdate;
      }

      metonicDate.moladDay = metonicDate.moladDay - 29;
    }

    // It has to be Kislev. 
    hdate.m = 3;
    hdate.d = metonicDate.moladDay;

  }
  else
  {
    hdate.y = "";
  }
  
  if ((hdate.m >= 0) && (hdate.m <= 12))
  {
    hdate.mstr = MONTHS[hdate.m-1];
  }

  return hdate;
}

///////////////////////////////////////////////////////////////////////////   
// Given a year, month and day in the Jewish calendar, find the
// corresponding serial day number (SDN).  Zero is returned when the input
// date is detected as invalid.  The return value will be > 0 for all valid
// dates, but there are some invalid dates that will return a positive
// value.  To verify that a date is valid, convert it to SDN and then back
// and compare with the original.
//
function HebrewToJDN (day, month, year)
{
    var jdn = 0;

    var yearLength;
    var lengthOfAdarIAndII;
    var tdate;

    if ((year <= 0) || (day <= 0) || (day > 30)) 
    {
      return jdn;
    }

    switch (month) 
    {

      case 1:
      case 2:
	// It is Tishri or Heshvan - don't need the year length. 
	tdate = FindStartOfYear(year);
	
	if (month == 1) 
	{
	    jdn = tdate.tishri + day - 1;
	} 
	else 
	{
	    jdn = tdate.tishri + day + 29;
	}
	break;
    
      case 3:
	// It is Kislev - must find the year length. 
	// Find the start of the year. 
	tdate = FindStartOfYear(year);

	// Find the end of the year. 
	tdate.moladHalakim = tdate.moladHalakim + (HALAKIM_PER_LUNAR_CYCLE * HEBREW_MO_LEN[tdate.metonicYear]);
	tdate.moladDay     = tdate.moladDay     + trunc(tdate.moladHalakim / HALAKIM_PER_DAY);
	tdate.moladHalakim = tdate.moladHalakim % HALAKIM_PER_DAY;
	
	tishri1After = Tishri1((tdate.metonicYear + 1) % 19, tdate.moladDay, tdate.moladHalakim);

	yearLength = tishri1After - tdate.tishri;

	if ((yearLength == 355) || (yearLength == 385)) 
	{
	    jdn = tdate.tishri + day + 59;
	} 
	else 
	{
	    jdn = tdate.tishri + day + 58;
	}
	break;

      case 4:
      case 5:
      case 6:
	// It is Tevet, Shevat or Adar I - don't need the year length.

	tdate = FindStartOfYear(year+1);

	if (HEBREW_MO_LEN[(year - 1) % 19] == 12) 
	{
	    lengthOfAdarIAndII = 29;
	} 
	else 
	{
	    lengthOfAdarIAndII = 59;
	}

	if (month == 4) 
	{
	    jdn = tdate.tishri + day - lengthOfAdarIAndII - 237;
	} 
	else 
	{
	  if (month == 5) 
	  {
	    jdn = tdate.tishri + day - lengthOfAdarIAndII - 208;
	  } 
	  else 
	  {
	    jdn = tdate.tishri + day - lengthOfAdarIAndII - 178;
	  }
	}
	break;
    
      default:
	// It is Adar II or later - don't need the year length. 
	tdate = FindStartOfYear(year+1);

	switch (month) 
	{
	  case  7:
	    jdn = tdate.tishri + day - 207;
	    break;
	  case  8:
	    jdn = tdate.tishri + day - 178;
	    break;
	  case  9:
	    jdn = tdate.tishri + day - 148;
	    break;
	  case 10:
	    jdn = tdate.tishri + day - 119;
	    break;
	  case 11:
	    jdn = tdate.tishri + day - 89;
	    break;
	  case 12:
	    jdn = tdate.tishri + day - 60;
	    break;
	  case 13:
	    jdn = tdate.tishri + day - 30;
	    break;
	  default:
	    return jdn;
	}
    }

    jdn = jdn + HEBREW_CORRELATION; 	

    return jdn;
}

//-->
