      /***************
       * DEFINITIONS *
       ***************/

      /* display names */

      var Centuries = [
        "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th",
        "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th",
        "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th", "30th",
        "31st", "32nd", "33rd", "34th", "35th", "36th", "37th", "38th", "39th", "40th",
        "41st", "42nd", "43rd", "44th", "45th", "46th", "47th", "48th", "49th", "50th",
        "51st", "52nd", "53rd", "54th", "55th", "56th", "57th", "58th", "59th", "60th",
        "61st", "62nd", "63rd", "64th", "65th", "66th", "67th", "68th", "69th", "70th",
        "71st", "72nd", "73rd", "74th", "75th", "76th", "77th", "78th", "79th", "80th",
        "81st", "82nd", "83rd", "84th", "85th", "86th", "87th", "88th", "89th", "90th",
        "91st", "92nd", "93rd", "94th", "95th", "96th", "97th", "98th", "99th", "100th"];

      var MuslimMonths = [
        "Muharram", "Safar", "Rabi' I", "Rabi' II", "Jumada I", "Jumada II",
//        "Muharram", "Safar", "Rabi' al-awwal", "Rabi' al-thani", "Jumada al-awwal", "Jumada al-thani",
        "Rajab", "Sha'aban", "Ramadan", "Shawwal", "Dhu al-Qi'day", "Dhu al-Hijjah"];

      var EnglishMonths = [
        "January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December"];

      var EnglishDaysOfWeek =
        ["Friday", "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"];
        // note Friday must come first because origin day (July 19, 622) was on a Friday

      var MuslimDaysOfWeek =
        ["al-jumu`a", "as-sabt", "al-ahad", "al-ithnayn", "ath-thulaathaa", "al-arbia`aa'", "al-khamis"];
        // note al-juma;a (Friday) must come first because origin day (July 19, 622) was on a Friday

      /* internal names (should really be enumeration types) */

      englishCentury = 0;
      muslimCentury = 1;

      muharram = 0;
      safar = 1;
      rabialawwal = 2;
      rabialthani = 3;
      jumadaalawwal = 4;
      jumadaalthani = 4;
      rajab = 6
      shaaban = 7;
      ramadan = 8;
      shawwal = 9;
      dhualqiday = 10;
      dhualhijjah = 11;

      january = 0;
      february = 1;
      march = 2;
      april = 3;
      may = 4;
      june = 5;
      july = 6;
      august = 7;
      september = 8;
      october = 9;
      november = 10;
      december = 11;

      saturday = 0;
      sunday = 1;
      monday = 2;
      tuesday = 3;
      wednesday = 4;
      thursday = 5;
      friday = 6;

      julian = 0;
      gregorian = 1;

      /*******************
       * STATE VARIABLES *
       *******************/

      var CenturyIndex;
      var CenturyTypeIndex;

      var MuslimMonthIndex;
      var MuslimDayIndex;
      var MuslimYearIndex;
      var MuslimYearKind;

      var EnglishMonthIndex;
      var EnglishDayIndex;
      var EnglishYearIndex;

      var EnglishDayOfWeekIndex;
      var MuslimYearKindForDisplay;

      var CalendarType = gregorian;

      /********
       * Misc *
       ********/

      function Mod(X, Y) {
        return X - Math.floor(X / Y) * Y;
      }

      var DaysIn400JulianYears = 365*400 + 100;
      var DaysIn400GregorianYears = DaysIn400JulianYears - 3;
      var DaysIn30Years = 10631; // 30*354+11 = number of days in 30 year cycle

      /****************************
       * Muslim UTILITY FUNCTIONS *
       ****************************/

      function MuslimStartYear() {
        var year = 100*(CenturyIndex);
        if (CenturyTypeIndex == englishCentury) {

          var absolute = 0;
          var DaysIn400Years = (CalendarType == gregorian) ? DaysIn400GregorianYears : DaysIn400JulianYears;

          // get total number of days up to start of specified muslim century

          while (year > 622 + 400) {
            absolute += DaysIn400Years;
            year -= 400;            
          }
          while (year < 622) {
            absolute -= DaysIn400Years;
            year += 400;
          }

          while (year > 622) {
            absolute += DaysInEnglishYear(year);
            year--;
          }

          // find Muslim year corresponding to that number of days

          if (CenturyIndex == 6) {
            year = -21;
          } else if (CenturyIndex > 6) { // absolute will be at least equal to days in 100 years
            year = -21;
            while (absolute > DaysIn30Years) {
              absolute -= DaysIn30Years;
              year += 30;
            }
            while (absolute > DaysInMuslimYear(year)) {
              absolute -= DaysInMuslimYear(year);
              year++;
            }
          } else { // CenturyIndex < 6, absolute will be at least equal to days in -100 years
            year = -21;
            while (absolute < -DaysIn30Years) {
              absolute += DaysIn30Years;
              year -= 30;
            }
            while (absolute < DaysInMuslimYear(year-1)) {
              absolute += DaysInMuslimYear(year-1);
              year--;
            }
          }
        }
        return year;
      }

      function MuslimYear() {
        var x = MuslimStartYear() + MuslimYearIndex;
        return x;
      }

      function DaysInMuslimMonth(year, month) {
        if (month%2 == 0) {
          return 30;
        } else if (month == dhualhijjah && IsMuslimLeapYear(year)) {
          return 30;
        } else {
          return 29;
        }
      }

      function IsMuslimLeapYear(year) {

        var yr = Mod(year, 30);
        return (yr == 2 || yr == 5 || yr == 7 || yr == 10 || yr == 13 || yr == 16 ||
                 yr == 18 || yr == 21 || yr == 24 || yr == 27 || yr == 29);
      }

      function DaysInMuslimYear(year) {
        return IsMuslimLeapYear(year) ? 355 : 354;
      }

      /*****************************
       * ENGLISH UTILITY FUNCTIONS *
       *****************************/

      function EnglishStartYear() {
        var year = 100*(CenturyIndex);
        if (CenturyTypeIndex == muslimCentury) {

          var absolute = 0;

          // get total number of days up to start of specified muslim century

          while (year > 30) {
            absolute += DaysIn30Years;
            year -= 30;
          }
          while (year < 0) {
            absolute -= DaysIn30Years;
            year += 30;
          }
          for (var i=1; i<year; i++) {
            absolute += 354;
            if (IsMuslimLeapYear(i)) {
              absolute++;
            }
          }

          // find common year corresponding to that number of days

          if (CenturyIndex == 0) {
            year = 622;
          } else if (CenturyIndex > 0) { // absolute will be at least equal to days in 100 years
            absolute -= (CalendarType == gregorian) ? 13 : 16; // get from July 19/16 to August 1, 622
            absolute -= (31+30+31+30+31); // get to Jan 1, 623
            absolute -= 77*365 + 19; // get to Jan 1, 700
            year = 700;

            DaysIn400Years = (CalendarType == gregorian) ? DaysIn400GregorianYears : DaysIn400JulianYears;
            while (absolute > DaysIn400Years) { // remove multiples of 400 years
              absolute -= DaysIn400Years;
              year += 400;
            }
            while (absolute > DaysInEnglishYear(year)) {
              absolute -= DaysInEnglishYear(year);
              year++;
            }            
          } else { // CenturyIndex < 0, absolute will be at least equal to days in -100 years
            absolute += (CalendarType == gregorian) ? 18 : 15; // get from July 19/16 to July 1, 622
            absolute += (31+28+31+30+31+30); // get to Jan 1, 622
            absolute += 22*365 + 6 - ((CalendarType == gregorian) ? 1 : 0); // get to Jan 1, 600
            year = 700;

            DaysIn400Years = (CalendarType == gregorian) ? DaysIn400GregorianYears : DaysIn400JulianYears;
            while (absolute < -DaysIn400Years) { // remove multiples of 400 years
              absolute += DaysIn400Years;
              year -= 400;
            }
            while (absolute < -DaysInEnglishYear(year-1)) {
              absolute += DaysInEnglishYear(year-1);
              year--;
            }            
          }
        }
        return year;
      }

      function EnglishYear() {
        return EnglishStartYear() + EnglishYearIndex;
      }

      function DaysInEnglishMonth(year, month) {
        if (month == april || month == june || month == september || month == november) {
          return 30;
        } else if (month != february) {
          return 31;
        } else if (IsEnglishLeapYear(year)) {
          return 29;
        } else {
          return 28;
        }
      }

      function IsEnglishLeapYear(year) {
        yr4 = Mod(year, 4);
        yr100 = Mod(year, 100);
        yr400 = Mod(year, 400);
        if (CalendarType == gregorian) {
          return ((yr4 == 0 && yr100 != 0) || yr400 == 0) ? true : false;
        } else {
          return (yr4 == 0) ? true : false;
        }
      }

      function DaysInEnglishYear(year) {
        return IsEnglishLeapYear(year) ? 366 : 365;
      }

      /********************************
       * MAIN COMPUTATIONAL FUNCTIONS *
       ********************************/

      var absolute = 0;

      function MuslimToAbsolute() {
        if (debugging) alert("entering MuslimToAbsolute");

        // start at beginning of Muslim year 1
        
        absolute = 0;

        // get to start of current year

        var my = MuslimYear();
        while (my>30) {
          absolute += DaysIn30Years;
          my -= 30;
        }
        while (my<=0) {
          absolute -= DaysIn30Years;
          my += 30;
        }
        for (var i=1; i<my; i++) {
          absolute += DaysInMuslimYear(i);
        }

        // add in days for each Muslim month up to current month

        absolute += MuslimDayIndex;
        for (k=muharram; k<MuslimMonthIndex; k++) {
          absolute += DaysInMuslimMonth(MuslimYear(), k);
        }
        if (debugging) alert("leaving MuslimToAbsolute");
      }

      function EnglishToAbsolute() {
        if (debugging) alert("entering EnglishToAbsolute");

        absolute = 0;

        // factor out 400-year cycles for efficiency

        ey = EnglishYear();
        while (ey-622 >= 400) {
          ey -= 400;
          absolute += (CalendarType == gregorian) ? DaysIn400GregorianYears : DaysIn400JulianYears;
        }
        // get to start of current year

        for (var i=622; i<ey; i++) {
          absolute += DaysInEnglishYear(i);
        }

        // add in days for each English month up to current month

        absolute += EnglishDayIndex;
        for (k=january; k<EnglishMonthIndex; k++) {
          absolute += DaysInEnglishMonth(ey, k);
        }

        // subtract out days from Jan 1 to July 16 (julian) or 19 (gregorian) in 622

        for (k=january; k<july; k++) {
          absolute -= DaysInEnglishMonth(622, k);
        }
        absolute -= (CalendarType == gregorian) ? 18 : 15;
        if (debugging) alert("leaving EnglishToAbsolute");
      }

      function EnglishDayOfWeek() {
        return EnglishDaysOfWeek[Mod(absolute,7)];
      }

      function MuslimDayOfWeek() {
        return "yaum " + MuslimDaysOfWeek[Mod((absolute+6),7)];
      }

      function AbsoluteToMuslim() {
        // start with muharram 1, 1

        var year = 1;
        var month = muharram;
        var day = 0;
        var oldAbsolute = absolute;

        // process full years

        while (absolute > DaysIn30Years) {
          absolute -= DaysIn30Years;
          year += 30;
        }
        while (absolute <= 0) {
          absolute += DaysIn30Years;
          year -= 30;
        }

        while (absolute >= DaysInMuslimYear(year)) {
          absolute -= DaysInMuslimYear(year);
          year++;
        }

        // process last partial year

        while (absolute >= DaysInMuslimMonth(year, month)) {
          absolute -= DaysInMuslimMonth(year, month);
          month++;
        }
        day += absolute;


        // update display

        MuslimYearIndex = year - MuslimStartYear();
        MuslimMonthIndex = month;
        MuslimDayIndex = day;

        /* make sure century start is ok
         *   note: if the century was specified as being a muslim century, an extra
         *   year was put into the english year list so this conversion can never give an
         *   out-of-bound year.  Problem arises only when an english century was specified.
         */
        if (CenturyTypeIndex == englishCentury) {
          while(MuslimYearIndex>100) {
            MuslimYearIndex -= 100;
            EnglishYearIndex -= 100;
            CenturyIndex++;
          }
          while(MuslimYearIndex<0) {
            MuslimYearIndex += 100;
            EnglishYearIndex += 100;
            CenturyIndex--;
          }
        }
        absolute = oldAbsolute;

        if (debugging) alert("leaving AbsoluteToEnglish");
      }

      function AbsoluteToEnglish() {

        // start with july 16, 622 (Julian) or july 19, 622 (Gregorian)

        var year = 622;
        var month = july;
        var day = (CalendarType == gregorian) ? 18 : 15; // indexes correspond to 19 and 16
        var DaysIn400Years = (CalendarType == gregorian) ? DaysIn400GregorianYears : DaysIn400JulianYears;
        var oldAbsolute = absolute;

        // take care of negative years

        while (absolute < 0) {
          absolute += DaysIn400Years;
          year -= 400;
        }

        // process partial year 622

        while (true) {
          if (absolute < DaysInEnglishMonth(year, month) - day) {
            day += absolute;
            absolute = 0;
            break;
          } else {
            absolute -= (DaysInEnglishMonth(year, month) - day);
            day = 0;
            month++;
            if (month > december) {
              month = january;
              year++;
            }
          }
        }

        // factor out 400-year cycles for efficiency
        
        while (absolute >= DaysIn400Years) {
          absolute -= DaysIn400Years;
          year += 400;
        }

        // process full years

        while (absolute >= DaysInEnglishYear(year)) {
          absolute -= DaysInEnglishYear(year);
          year++;
        }

        // process last partial year

        while (absolute >= DaysInEnglishMonth(year, month)) {
          absolute -= DaysInEnglishMonth(year, month);
          month++;
        }
        day += absolute;


        // update display

        EnglishYearIndex = year - EnglishStartYear();
        EnglishMonthIndex = month;
        EnglishDayIndex = day;

        /* make sure century start is ok
         *   note: if the century was specified as being a muslim century, an extra
         *   year was put into the english year list so this conversion can never give an
         *   out-of-bound year.  Problem arises only when an english century was specified.
         */

        if (CenturyTypeIndex == englishCentury) {
          while(EnglishYearIndex>99) {
            EnglishYearIndex -= 100;
            MuslimYearIndex -= 100;
            CenturyIndex++;
          }
          while(EnglishYearIndex<0) {
            EnglishYearIndex += 100;
            MuslimYearIndex += 100;
            CenturyIndex--;
          }
        }
        absolute = oldAbsolute;

        if (debugging) alert("leaving AbsoluteToEnglish");
      }

