/* The 'libkal' library for date conversion.
 * Copyright (C) 1996-1998 Petr Tomasek <tomasek@etf.cuni.cz>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


/*
 * Divide/modulo.
 */

long kal_dv(long a, long b) {
 long c;
 c=a/b;
 if (c<=0) if (b*c>a)  c--;
 return c;
 }
 
long kal_md(long a, long b) {
 return a-b*kal_dv(a,b);
 }

/* Just not to have to type a lot. Internal. */
#define dv(a,b) kal_dv(a,b)
#define md(a,b) kal_md(a,b)

/*
 * Internal.
 */ 

int s(int m, int p) {
 const int t[]={0,31,59,90,120,151,181,212,243,273,304,334}; 
 int b;
 if (m==13) return 365; else {
  m=md(m-1,12)+1; /* for stability reasons */
  b=t[m-1];
  if (m<3) b=b-p;
  return b; }
      }




/*
 *  Julianic calendar.   
 */

long kal_conv_jul_jd(int d,int m,int y) {
 long p1,p2,q;
 p1=dv(y,4);
 p2=md(y,4);
 q=0;if (p2==0) q=1;
 return d+s(m,q)+1461*p1+365*p2+1721058;
  }
  
void kal_conv_jd_jul(long jd,int *d,int *m,int *y) {
 long p1,p2,q1,q2,q;
 p1=dv(jd-1721058,1461);
 q1=md(jd-1721058,1461);
 if (q1==0) {*d=1; *m=1; *y=4*p1; }
  else {
   p2=dv(q1-1,365);
   q2=md(q1-1,365);
   *y=4*p1+p2;
   q=0; if (md(*y,4)==0) q=1;
   *m=1;
   while ((*m<12) && ((q2+1)>s(*m+1,q))) *m=*m+1;;
   *d=q2+1-s(*m,q);
   }
  }      

long kal_jul_east(int y) {  /* Computes jd of easter sunday for given year. */
 long p1,p2,q,ep,d;
 ep=md(11*md(y,19)+14,30);
 p1=dv(y,4);
 p2=md(y,4);
 q=0; if (p2==0) q=1;
 d=50-ep+s(3,q)+1461*p1+365*p2+1721058;
 return d+7-md(d+1,7);
 }



/*
 * Gregorianic calendar.
 */
         
long kal_conv_gre_jd(int d,int m,int y) {
 long m1,m2,n1,n2,q;
 m1=dv(y,400);
 m2=dv(y,100);
 n1=dv(y,4);
 n2=md(y,4);
 q=0; if (n2==0) q=1;
 if (md(y,100)==0) q=0;
 if (md(y,400)==0) q=1;
 return d+s(m,q)+m1-m2+1461*n1+365*n2+1721060;
  }
  
void kal_conv_jd_gre(long jd,int *d,int *m,int *y) {
 long qq,a1,a2,b1,b2,c1,c2,d1,d2,q;
 qq=jd-1721060;
 a1=dv(qq,146097);
 a2=md(qq,146097);
 b1=dv(a2,36524);
 b2=md(a2,36524);
 c1=dv(b2,1461);
 c2=md(b2,1461);

 if ((c2==0) && ((b2>0) || (a2==0)) ) {
       *d=1; *m=1; *y=400*a1+100*b1+4*c1; }
  else {
   d1=dv(c2-1,365);
   d2=md(c2-1,365);
   *y=400*a1+100*b1+4*c1+d1;
   q=0;
   if (md(*y,4)==0) q=1;
   if (md(*y,100)==0) q=0;
   if (md(*y,400)==0) q=1;
   *m=1;
   while ((*m<12) && ((d2+1)>s(*m+1,q))) *m=*m+1;
   *d=d2+1-s(*m,q);
   }
  } 


long kal_gre_east(int y) {  /* Computes jd of easter sunday for given year. */
 long p1,p2,q,ep,a,g,d,m1,m2;
 a=md(y,19);
 m1=dv(y,400);
 m2=dv(y,100);
 g=md(dv(y,300)+m1-m2+14,30);
 ep=md(11*a+g,30);
 if (ep==0) ep=1;
 if ((ep==1) && (a>10)) ep=2;
 p1=dv(y,4);
 p2=md(y,4);
 q=0; if (p2==0) q=1;
 d=50-ep+s(3,q)+m1-m2+1461*p1+365*p2+1721060;
 return d+7-md(d+1,7);
 }


 
/*
 * Arabic calendar.
 */
 
long kal_conv_ar_jd(int d,int m,int h) {
 long h1,h2;
 h1=dv(h-1,30);
 h2=md(h-1,30);
 return d+29*(m-1)+dv(m,2)+10631*h1+354*h2+dv(7*h2+8,19)+1948439;
  }
  
void kal_conv_jd_ar(long jd,int *d,int *m,int *h) {
 long h1,h2,q1,q2;
 h1=dv(jd-1948440,10631);
 q1=md(jd-1948440,10631);
  h2=-1;
  do h2++; while (354*h2+dv(7*h2+8,19)<=q1);
  h2--;
  *h=30*h1+h2+1;
  
  q2=q1-(354*h2+dv(7*h2+8,19));
  *m=0;
  do *m=*m+1; while (29*(*m-1)+dv(*m,2)<=q2);
  *m=*m-1;
  *d=q2-(29*(*m-1)+dv(*m,2))+1;
   }


/* 
 * Turkish calendar 
 */

/*  
 *  THIS IS (afaik) BUGGY !!!!! 
 *
long kal_conv_tur_jd(int d,int m,int h) {
 long h1,h2;
 h1=dv(h-1,8);
 h2=md(h-1,8);
 return d+29*(m-1)+dv(m,2)+2835*h1+354*h2+dv(3*h2+1,8)+1948440;
  }
  
void kal_conv_jd_tur(long jd,int *d,int *m,int *h) {
 long h1,h2,q1,q2;
 h1=dv(jd-1948441,2835);
 q1=md(jd-1948441,2835);
  h2=-1;
  do h2++; while (354*h2+dv(3*h2+1,8)<=q1);
  h2--;
  *h=8*h1+h2+1;
  
  q2=q1-(354*h2+dv(3*h2+1,8));
  *m=0;
  do *m=*m+1; while (29*(*m-1)+dv(*m,2)<=q2);
  *m=*m-1;
  *d=q2-(29*(*m-1)+dv(*m,2))+1;
   }
  *
  *
  */



/*
 * Jewish calendar.
 */

int prest(int w2) {
 int q=0;
 if (w2>0) q=dv(7*w2+1,19)-dv(7*w2-6,19);
 return q;
  }

int uuw(int u2,long u3,int w2,int y) {
 int uu=0;
 switch (u2) {
  case 2: case 4: case 6: uu=1; break;
  case 1: if ((u3>=16404) && (prest(w2)==0)) uu=2; break;
  case 0: if ((u3>=23269) && (prest(md(y-2,19))==1)) uu=1; break;
  }
 return uu;
 }
    
long kal_jew_rosh_hashana(int y) {
 long w1,w2,u2,w,uu,u1,u3;
  w1=dv(y-1,19);
  w2=md(y-1,19);
  w=235*w1+12*w2+dv(7*w2+1,19);
  u3=md(13753*w+12084,25920);
  uu=29*w+dv(13753*w+12084,25920);
  u2=md(uu,7);
  u1=dv(uu,7);
  return 347998+7*u1+u2+uuw(u2,u3,w2,y);
  }
  
int z_s(int m,int p,int q) {
 int ss;
 ss=(m-1)*29;
 if ((p==0) && (m>7)) ss=ss-29;
 if (m>6) ss=ss+3+dv(m-7,2);
  else ss=ss+dv(m,2);
 if ((p==1) && (m>6)) ss++;
 if ((q==0) && (m>3)) ss--;
 if ((q==2) && (m>2)) ss++;
 return ss;
  }        
  
long kal_conv_jew_jd(int d, int m, int y) {
 long j1,j2;
 int j,p,q;
 j1=kal_jew_rosh_hashana(y);
 j2=kal_jew_rosh_hashana(y+1);
 j=j2-j1;
 if (j>380) { p=1; q=j-383; } 
       else { p=0; q=j-353; }  
 return j1+z_s(m,p,q)+d-1;
   }

void kal_conv_jd_jew(long jd,int *d, int *m, int *y) {
 long w1,w2,ww,jd1,jd2;
 int j,p,q;
/* ww=(long)((float)((jd-347998)/29.5305941)); */
 ww=(((jd-347998)/29.5305941)); 
 if (ww<0) ww--;
 w1=dv(ww,235);
 w2=dv(md(ww,235),12);
 *y=19*w1+w2-1;
 jd2=kal_jew_rosh_hashana(*y);
 do {jd1=jd2; *y=*y+1; jd2=kal_jew_rosh_hashana(*y); } while (jd2<=jd);
 *y=*y-1;
 j=jd2-jd1;
 if (j>380) {p=1; q=j-383;}
   else {p=0; q=j-353;}
 j=jd-jd1;*m=0;
 do {*m=*m+1; } while (j>=z_s(*m,p,q));
 *m=*m-1;
 *d=j-z_s(*m,p,q)+1;
  }

/* is the year leap? */
int kal_jew_leap( int y) {
return ((kal_jew_rosh_hashana(y+1)-        /* Is the number of days */
	 kal_jew_rosh_hashana(y))>360);   /*  in the year > 360 ?  */
}


/*
 * Days of week  
 */

int kal_day_of_week(long jd) {
return md(jd+1,7); 
}

long kal_prev_dw (long jd, int dw) {
return (7*dv(jd+1-dw,7)+dw-1);
}

long kal_prev_before_dw (long jd, int dw) {
return (7*dv(jd-dw,7)+dw-1);
}

long kal_next_dw (long jd, int dw) {
return (dw-1-7*dv(dw-1-jd,7));
}

long kal_next_after_dw (long jd, int dw) {
return (dw-1-7*dv(dw-2-jd,7));
}


/*
 *  Year adjustments.
 *
 */

int kal_year_code(int y,int a) {
   if (a) return 1-y;
     else return y; 
    }

int kal_year_decode(int y, int *a) {
   if (y<1) { *a=1; return 1-y;} 
     else { *a=0; return y;}
      }


