static class sDate {
    int day;		/* Day */
    int month;		/* Month */
    int year;		/* Year */
    int weekday;        /* Day of the week (0:Sunday, 1:Monday...) */
    int frm_numdays;    /* Number of days in specified input  month */
    int to_numdays;     /* Number of days in resulting output month */
    int to_numdays2;    /* Number of days in resulting output month+1 */
    String units;	/* Units used to denote before/after epoch */
    String frm_dname;	/* Converting from - Name of day */
    String frm_mname;	/* Converting from - Name of month */
    String frm_dname_sh;	/* Converting from - Name of day   in short format */
    String frm_mname_sh;	/* Converting from - Name of month in short format */
    String to_dname;	/* Converting to   - Name of day */
    String to_mname;	/* Converting to   - Name of month */
    String to_mname2;	/* Converting to   - Name of month+1 */
    String to_dname_sh;	/* Converting to   - Name of day   in short format */
    String to_mname_sh;	/* Converting to   - Name of month in short format */
    String[] event;	/* Important event pertaining to date at hand */

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        sDate that = (sDate) o;
        return day == that.day && month == that.month && year == that.year &&
                weekday == that.weekday &&
                (units != null ? units.equals(that.units) : that.units == null);
    }

    @Override
    public int hashCode() {
        int result = day;
        result = 31 * result + month;
        result = 31 * result + year;
        result = 31 * result + weekday;
        result = 31 * result + (units != null ? units.hashCode() : 0);
        return result;
    }
} static int fill_datestruct(sDate fdate,
                           int weekday,
                           int frm_month_num,
                           int to_month_num,
                           String[] frm_day,
                           String[] frm_day_short,
                           String[] frm_month,
                           String[] frm_month_short,
                           String[] to_day,
                           String[] to_day_short,
                           String[] to_month,
                           String[] to_month_short,
                           sEvent[] farr_table) {
    int error_event;

    fdate.frm_dname = frm_day[weekday];
    fdate.frm_dname_sh = frm_day_short[weekday];
    fdate.frm_mname = frm_month[frm_month_num];
    fdate.frm_mname_sh = frm_month_short[frm_month_num];

    fdate.to_dname = to_day[weekday];
    fdate.to_dname_sh = to_day_short[weekday];
    fdate.to_mname = to_month[to_month_num];
    fdate.to_mname_sh = to_month_short[to_month_num];

    if (to_month_num == 12)
        fdate.to_mname2 = to_month[1];
    else
        fdate.to_mname2 = to_month[to_month_num + 1];

    fdate.event = get_events(farr_table,
            fdate.day,
            fdate.month);
    error_event = 0;

    return (error_event);
} static final String[] g_day = {"Sunday", "Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday"}; static final String[] g_day_short = {"Sun", "Mon", "Tue", "Wed",
        "Thu", "Fri", "Sat"}; static final String[] g_month = {"skip",
        "January", "February", "March", "April",
        "May", "June", "July", "August",
        "September", "October", "November", "December"}; static final String[] g_month_short = {"skip",
        "Jan", "Feb", "Mar", "Apr",
        "May.sh", "Jun", "Jul", "Aug",
        "Sep", "Oct", "Nov", "Dec"}; static final String[] h_day = {"Ahad", "Ithnain", "Thulatha", "Arbiaa",
        "Khamees", "Jumaa", "Sabt"}; static final String[] h_day_short = {"Ahd", "Ith", "Tha", "Arb",
        "Kha", "Jum", "Sab"}; static final String[] h_month = {"skip",
        "Muharram", "Safar", "Rabi I", "Rabi II",
        "Jumada I", "Jumada II", "Rajab", "Shaaban",
        "Ramadan", "Shawwal", "Thul-Qiaadah", "Thul-Hijja"}; static final String[] h_month_short = {"skip",
        "Muh", "Saf", "Rb1", "Rb2",
        "Jm1", "Jm2", "Raj", "Shaa",
        "Ram", "Shaw", "Qid", "Hij"};
import org.arabeyes.itl.hijri.HijriModule.sDate;
import static java.lang.Math.floor;
import static org.arabeyes.itl.hijri.HijriModule.fill_datestruct;
import static org.arabeyes.itl.hijri.HijriModule.g_day;
import static org.arabeyes.itl.hijri.HijriModule.g_day_short;
import static org.arabeyes.itl.hijri.HijriModule.g_month;
import static org.arabeyes.itl.hijri.HijriModule.g_month_short;
import static org.arabeyes.itl.hijri.HijriModule.h_day;
import static org.arabeyes.itl.hijri.HijriModule.h_day_short;
import static org.arabeyes.itl.hijri.HijriModule.h_month;
import static org.arabeyes.itl.hijri.HijriModule.h_month_short;
/*  Copyright (c) 2004, Arabeyes, Fayez Alhargan
 *
 *    This is a program that computes the Hijary dates for Umm-AlQura
 *    calendar the official calendar of the Kingdom of Saudi Arabia.
 *
 *      King Abdulaziz City for Science and Technology
 *      Computer and Electronics Research Institute
 *      Riyadh, Saudi Arabia
 *      alhargan-at-kacst.edu.sa
 *      Tel:4813770 Fax:4813764
 *
 *    Fayez Alhargan version: opn1.2
 *    Fayez Alhargan last modified 22-1-2003
 *
 * (www.arabeyes.org - under LGPL license - see COPYING file)
 */
package org.arabeyes.itl.hijri;



class UmmAlquraModule {

    private static final int HStartYear = 1420;
    private static final int HEndYear = 1450;

    private static final int MonthMap[] = {19410,
            19396, 19337, 19093, 13613, 13741, 15210, 18132, 19913, 19858, 19110,
            18774, 12974, 13677, 13162, 15189, 19114, 14669, 13469, 14685, 12986,
            13749, 17834, 15701, 19098, 14638, 12910, 13661, 15066, 18132, 18085
    };

    /* makes it circular m[0]=m[12] & m[13]=m[1] */
    private static final int[] gmonth = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31};

    /* makes it circular m[0]=m[12] & m[13]=m[1]  */
    private static final int[] smonth = {31, 30, 30, 30, 30, 30, 29, 31, 31, 31, 31, 31, 31, 30};

    private UmmAlquraModule() {
    }

    /**
     * Name:    Hsub2G
     * Type:    Procedure                                                       *
     * Purpose: Finds Gdate(year,month,day) for Hdate(year,month,day)           *
     * Arguments:                                                               *
     * Input: Hijri date: year:yh, month:mh                                     *
     * Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek  *
     * and returns flag found:1 not found:0
     */
    private static int Hsub2G(sDate mydate, int mh, int yh) {
        int flag;
        int JD;
        double GJD;

        /* Make sure that the date is within the range of the tables */
        if (mh < 1) {
            mh = 12;
        }
        if (mh > 12) {
            mh = 1;
        }
        if (yh < HStartYear) {
            yh = HStartYear;
        }
        if (yh > HEndYear) {
            yh = HEndYear;
        }

        GJD = HCalendarToJDA(yh, mh, 1);
        JDToGCalendar(GJD, mydate);
        JD = (int) GJD;
        mydate.weekday = (JD + 1) % 7;
        /* NOTE: so when is flag = 0 ? */
        flag = 1; /* date has been found */

        return (flag);
    }

    /**
     * Name:    HCalendarToJDA
     * Type:    Function
     * Purpose: convert Hdate(year,month,day) to Exact Julian Day
     * Arguments:
     * Input : Hijrah  date: year:yh, month:mh, day:dh
     * Output:  The Exact Julian Day: JD
     */
    private static double HCalendarToJDA(int yh, int mh, int dh) {
        int flag, Dy, m, b;
        int JD;
        double GJD;

        /* estimate JD of the beginning of year */
        JD = (int) HCalendarToJD(yh, 1, 1);
        Dy = MonthMap[yh - HStartYear] / 4096;  /* Mask 1111000000000000 */
        GJD = JD - 3 + Dy;   /* correct the JD value from stored tables  */
        b = MonthMap[yh - HStartYear];
        b = b - Dy * 4096;
        for (m = 1; m < mh; m++) {
            flag = b % 2;  /* Mask for the current month */
            if (flag != 0)
                Dy = 30;
            else
                Dy = 29;
            GJD = GJD + Dy;   /* Add the months lengths before mh */
            b = (b - flag) / 2;
        }
        GJD = GJD + dh - 1;

        return (GJD);
    }

    /**
     * Name:    HMonthLength
     * Type:    Function
     * Purpose: Obtains the month length
     * Arguments:
     * Input : Hijrah  date: year:yh, month:mh
     * Output:  Month Length
     */
    private static int HMonthLength(int yh, int mh) {
        int flag, Dy, m, b;

        if (yh < HStartYear || yh > HEndYear) {
            flag = 0;
            Dy = 0;
        } else {
            Dy = MonthMap[yh - HStartYear] / 4096;  /* Mask 1111000000000000 */
            b = MonthMap[yh - HStartYear];
            b = b - Dy * 4096;
            for (m = 1; m <= mh; m++) {
                flag = b % 2;  /* Mask for the current month */
                if (flag != 0)
                    Dy = 30;
                else
                    Dy = 29;
                b = (b - flag) / 2;
            }
        }
        return (Dy);
    }

    /**
     * Name:    DayInYear
     * Type:    Function
     * Purpose: Obtains the day number in the yea
     * Arguments:
     * Input : Hijrah  date: year:yh, month:mh  day:dh
     * Output:  Day number in the Year
     */
    private static int DayinYear(int yh, int mh, int dh) {
        int flag, Dy, m, b, DL;

        if (yh < HStartYear || yh > HEndYear) {
            flag = 0;
            DL = 0;
        } else {
            Dy = MonthMap[yh - HStartYear] / 4096;  /* Mask 1111000000000000 */
            b = MonthMap[yh - HStartYear];
            b = b - Dy * 4096;
            DL = 0;
            for (m = 1; m <= mh; m++) {
                flag = b % 2;  /* Mask for the current month */
                if (flag != 0)
                    Dy = 30;
                else
                    Dy = 29;
                b = (b - flag) / 2;
                DL = DL + Dy;
            }
            DL = DL + dh;
        }
        return (DL);
    }

    /**
     * Name:    HYearLength
     * Type:    Function
     * Purpose: Obtains the year length
     * Arguments:
     * Input : Hijrah  date: year:yh
     * Output:  Year Length
     */
    private static int HYearLength(int yh) {
        int flag, Dy, m, b, YL;

        if (yh < HStartYear || yh > HEndYear) {
            flag = 0;
            YL = 0;
        } else {
            Dy = MonthMap[yh - HStartYear] / 4096;  /* Mask 1111000000000000 */
            b = MonthMap[yh - HStartYear];
            b = b - Dy * 4096;
            flag = b % 2;  /* Mask for the current month */
            if (flag != 0)
                YL = 30;
            else
                YL = 29;

            for (m = 2; m <= 12; m++) {
                flag = b % 2;  /* Mask for the current month */
                if (flag != 0)
                    Dy = 30;
                else
                    Dy = 29;
                b = (b - flag) / 2;
                YL = YL + Dy;
            }
        }
        return (YL);
    }

    /**
     * Name:    G2H
     * Type:    Procedure
     * Purpose: convert Gdate(year,month,day) to Hdate(year,month,day)
     * Arguments:
     * Input: Gregorian date: year:yg, month:mg, day:dg
     * Output: Hijrah  date: year:yh, month:mh, day:dh, day of week:dayweek
     * and returns flag found:1 not found:0
     */
    static int G2H(sDate mydate, int dg, int mg, int yg) {
        int yh2, mh2;
        int df;
        int flag = 1;
        int J;
        double GJD, HJD;
        final sDate tmpdate = new sDate();
        final sDate tmpdate2 = new sDate();
        /*int error_fill;*/

        GJD = GCalendarToJD(yg, mg, dg + 0.5);  /* find JD of Gdate */

        /* estimate the Hdate that correspond to the Gdate */
        JDToHCalendar(GJD, tmpdate);

        /* get the exact Julian Day */
        HJD = HCalendarToJDA(tmpdate.year, tmpdate.month, tmpdate.day);
        df = (int) (GJD - HJD);
        tmpdate.day += df;
        while (tmpdate.day > 30) {
            tmpdate.day -= HMonthLength(tmpdate.year, tmpdate.month);
            tmpdate.month++;
            if (tmpdate.month > 12) {
                tmpdate.year++;
                tmpdate.month = 1;
            }
        }
        if (tmpdate.day == 30) {
            yh2 = tmpdate.year;
            mh2 = tmpdate.month + 1;
            if (mh2 > 12) {
                yh2++;
                mh2 = 1;
            }
            Hsub2G(tmpdate2, mh2, yh2);

            /* Make sure that the month is 30days if not make adjustment */
            if (dg == tmpdate2.day) {
                tmpdate.year = yh2;
                tmpdate.month = mh2;
                tmpdate.day = 1;
            }
        }

        J = (int) (GCalendarToJD(yg, mg, dg) + 2);
        mydate.weekday = J % 7;
        mydate.to_numdays = 1;	/* this needs to be fixed !! */
        mydate.year = tmpdate.year;
        mydate.month = tmpdate.month;
        mydate.day = tmpdate.day;

        /* Fill-in the structure with various nicities a user might need */
        fill_datestruct(mydate, mydate.weekday, mg, mydate.month,
                g_day, g_day_short, g_month, g_month_short,
                h_day, h_day_short, h_month, h_month_short,