/*
   This file is part of Numerix.  Numerix is free software; you can
   redistribute it and/or modify it under the terms of the GNU General
   Public License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This program 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
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA 
*/

/* +------------------------------------------------------------------------+
   |                                                                        |
   |                      Entiers de longueur arbitraire                    |
   |                                                                        |
   |                             Division                                   |
   |                                                                        |
   +------------------------------------------------------------------------+ */

/* M. Quercia, 08/02/2001 */

#include "long_int.h"
#include "long_int-s.h"


         /* +---------------------------------------------+
            |  c <- a div b, retourne a mod b, b non nul  |
            +---------------------------------------------+ */

#ifndef have_sn_quo_2
ndouble xn(quo_2)(naturel a, longueur la, ndouble b, naturel c) {
  ndouble a0,b0,b1,q,u,v,ret;
  longueur i,l1,l2;

  if (b < BASE) { /* division à un chiffre ? */
    for (ret=0, i=la-1; i>=0; i--) {
      ret  = (ret << HW) + (ndouble)a[i];
      c[i] = ret/b;
      ret %= b;
    }
    return(ret);
  }

  /* évacue les petits quotients */
  if (la < 2) {
    if (la) {
      ret  = a[0];
      c[0] = 0;
      return(ret);
    } else {
      return(0);
    }
  }

  /* décale b pour avoir b1 >= BASE/2 */
  for (l1=0; b < (ndouble)(BASE/2 << HW); b <<= 1, l1++);
  b1 = b >> HW;
  b0 = b & (BASE-1);
  l2 = HW-l1;

  /* décale et divise les chiffres de a */
  a0 = a[la-1];
  c[la-1] = 0;
  u  = a0 >> l2;
  a0 = (a0 << HW) + (ndouble)a[la-2];
  u  = (u << HW) + ((a0 >> l2) & (BASE-1));
  for (i=la-2; i>=0; i--) {
    a0 = (a0 << HW) + ((i) ? (ndouble)a[i-1] : 0);
    q  = u/b1;
    u  = ((u - q*b1) << HW) + ((a0 >> l2) & (BASE-1));
    v  = q*b0;
    while (u < v) {q--; v -= u; u = b;}
    c[i] = q;
    u   -= v;
  }

  /* décale et retourne le reste */
  return(u >>= l1);
}
#endif

                        /* +---------------+
                           |  idem dans Z  |
                           +---------------+ */

#ifndef have_sz_quo_2
zdouble xz(quo_2)(entier *a, zdouble b, entier *c) {
  longueur la = Lg(a);
  longueur sa = Signe(a), sb = (b >= 0) ? 0 : SIGN_m;
  zdouble  r;

  if (sb) b = -b;
  r = xn(quo_2)(a->val,la,b,c->val);
  if ((sa^sb)&& (r)) {xn(inc_1)(c->val,la,1); r = b-r;}
  make_head(c,la,sa^sb);
  return((sb) ? -r : r);
}
#endif


/* +---------------------------------------------------------------------------+
   |  c <- a div b, d <- a mod b, lb >= 3, b[lb-1] non nul, algorithme en n^2  |
   +---------------------------------------------------------------------------+ */

#ifndef have_sn_quo_n2
void xn(quo_n2) (naturel a, longueur la, naturel b, longueur lb, naturel c, naturel d) {
  ndouble a0,b0,b1,b2,u,v,q,ret;
  zdouble s;
  int l1,l2;
  longueur j;

  /* Algorithme 4.3.1-D de Knuth avec décalage virtuel */

  /* d <- a, prolongé d'un zéro si nécessaire */
  xn(move)(d,a,la);
  if (la < lb) {
    xn(clear)(d+la,lb-la);
    c[0] = 0;
    return;
  }
  if (d[la-1] >= b[lb-1]) {d[la] = 0; la++;}
  else if (la == lb) {
    c[0] = 0;
    return;
  }
  else c[la-lb] = 0;

  /* D1. Normaliser pour avoir b[lb-1] >= BASE/2 */
  for (l1=0, b2 = ((ndouble)b[lb-1] << HW) + (ndouble)b[lb-2];
       b2 < (ndouble)(BASE/2 << HW);
       b2 <<= 1, l1++);
  l2 = HW-l1;
  b2 += (ndouble)b[lb-3] >> l2;
  b1 = b2 >> HW;
  b0 = b2 & (BASE-1);

  /* D2. Boucler sur les chiffres de d */
  for (la -= lb, d += la-1; (la); la--, d--) {

    /* Décale les quatre chiffres de tête de a et divise par b1:b0 */
    ret= ((ndouble)d[lb-3]) << l1;
    a0 = (((ndouble)d[lb-2]) << l1) + (ret >> HW);
    u  = ((((ndouble)d[lb] << HW) + (ndouble)d[lb-1]) << l1) + (a0 >> HW);
    q  = u/b1;
    u  = ((u-q*b1) << HW) + (a0 & (BASE-1));
    v  = q*b0;
    while (u < v) {q--; v -= u; u=b2;}
    
    /* D4. d <- d - qb */
    for (ret=0, j=0; j < lb; j++) {
      ret += q * (ndouble)b[j];
      s    = (ndouble)d[j] - (ret & (BASE-1));
      d[j] = s;
      ret >>= HW;
      if (s < 0) ret++;
    }

    /* D5-D6. si d < qb ajoute b */
    if (d[lb] != ret) {
      q--;
      for (ret=0, j=0; j < lb; j++) {
	ret += (ndouble)d[j] + (ndouble)b[j];
	d[j] = ret;
        ret >>= HW;
      }
    }

    /* range le quotient */
    c[la-1] = q;
  }
}
#endif


                        /* +---------------+
                           |  idem dans Z  |
                           +---------------+ */


#ifndef have_sz_quo_n2
void xz(quo_n2)(entier *a, entier *b, entier *c, entier *d) {
  longueur la = Lg(a), lb = Lg(b), lc, lbuff;
  longueur sa = Signe(a), sb = Signe(b);
  chiffre  *bb, *dd, *buff, *buff_save;
  ndouble  b2,r;

  /* aiguillage selon la taille du diviseur */
  switch (lb) {
  case 0:
    xn(fatal_err)("\nquo_k, division by zero\n");

  case 1:
    r = xn(quo_2)(a->val,la,b->val[0],c->val);
    if ((sa^sb) && (r)) {xn(inc_1)(c->val,la,1); r = b->val[0]-r;}
    make_head(c,la, sa^sb);
    if (d) {d->val[0] = r; make_head(d,1,sb);}
    return;

  case 2:
    b2 = (((ndouble)b->val[1]) << HW) + (ndouble)b->val[0];
    r = xn(quo_2)(a->val,la,b2, c->val);
    if ((sa^sb) && (r)) {xn(inc_1)(c->val,la,1); r = b2-r;}
    make_head(c,la, sa^sb);
    if (d) {d->val[0] = r; d->val[1] = r >> HW; make_head(d,2,sb);}
    return;
  }

  lc = max(la-lb+1,1);

  /* mémoire auxilliaire */
  lbuff = 0;
  if ((b == c) || (b == d)) lbuff += lb;
  if (d == NULL) lbuff += max(la,lb)+2;
  if (lbuff) {buff_save = xn(alloc_tmp)(lbuff); buff = buff_save;}
  else {buff_save = buff = NULL;}
  
  if ((b == c) || (b == d)) {xn(cpy)(buff,b->val,lb); bb = buff; buff += lb;}
  else {bb = b->val;}
  if (d) {dd = d->val;} else {dd = buff;}

  /* division avec reste */
  xn(quo_n2)(a->val,la,bb,lb,c->val,dd);

  /* correction si ab < 0 */
  if ((sa^sb) && (xn(cmp)(dd,lb,NULL,0))) {
    xn(inc_1)(c->val,lc,1);
    if (d) xn(sub)(bb,lb,dd,lb,dd);
  }
  
  make_head(c,lc, sa^sb);
  if (d) make_head(d,lb,sb);
  if (buff_save) xn(free)(buff_save);
  
}
#endif

         /* +---------------------------------------------+
            |  c <- max(a div b, BASE^lc-1), a <- a - bc  |
            |      retourne un majorant de lg(a)          |
            +---------------------------------------------+ */


#ifndef have_sn_hquo
longueur xn(hquo)(naturel a, longueur la, naturel b, longueur lb, naturel c, longueur lc) {
  ndouble ah,b0,b1,b2,q,v;
  zdouble ret;
  longueur i,j;
  int biga;

  /* saute les zéros de tête et teste si a >= b*BASE^lc */
  while ((la > lb+lc) && (a[la-1] == 0)) la--;
  biga = (la > lb+lc);
  if (!biga) {
    for (i=lb-1; (i>=0) && (a[i+lc] == b[i]); i--);
    biga = (i>=0) && (a[i+lc] > b[i]);
  }

  /* si a est trop grand tronque le quotient */
  if (biga) {
    for (i=0; i < lc; i++) c[i] = BASE-1;
    for (v=0, i=0; i < lb; i++) {
      v += (ndouble)a[i] + (ndouble)b[i];
      a[i] = v;
      v >>= HW;
    }
    if (v) {
      while (a[i] == BASE-1) {a[i++] = 0;}
      a[i]++;
    }
    for (i=0, j=lc, ret=0; i < lb; i++,j++) {
      ret += (ndouble)a[j] - (ndouble)b[i];
      a[j] = ret;
      ret >>= HW;
    }
    for (; (ret); j++) {
      ret += (ndouble)a[j];
      a[j] = ret;
      ret >>= HW;
    }
  }

  /* a < b*BASE^lc : effectue la division */   
  else {
    b0 = b[lb-1]; b1 = b[lb-2]; b2 = (b0 << HW) + b1;
    for (i=0, a+=lc-1, c+=lc-1, la -= lc-1; i < lc; i++, a--, c--, la++) {

      if (la < lb) c[0] = 0;
      else {

	/* divise les trois chiffres de tête de a par b0:bl */
	ah = (la <= lb) ? 0 : a[lb];
	ah = (ah << HW) + (ndouble)a[lb-1];
	q  = ah/b0; if (q >= BASE) q = BASE-1;
	ah -= q*b0;
	if (ah < BASE) {
	  ah = (ah << HW) + (ndouble)a[lb-2];
	  v  = q*b1;
	  while (ah < v) {q--; v -= ah; ah = b2;}
	}
	
	/* a <- a - q*b */
	for (v=0, j=0; j < lb; j++) {
	  v   += q * (ndouble)b[j];
	  ret  = (ndouble)a[j] - (v & (BASE-1));
	  a[j] = ret;
	  v  >>= HW;
	  if (ret < 0) v++;
	}
	ret = (ndouble)a[lb] - v;
	a[lb] = ret;
	ret  >>= HW;
	
	/* si < 0 diminue q et ajoute b */
	if (ret) {
	  q--;
	  for (v=0, j=0; j < lb; j++) {
	    v += (ndouble)a[j] + (ndouble)b[j];
	    a[j] = v;
	    v >>= HW;
	  }
	  a[lb] += v;
	  la = lb;
	}
	c[0] = q;
      }
    }
    a++; la--;
  }

  /* longueur du reste */
  while ((la > 0) && (a[la-1] == 0)) la--;
  return(la);
}
#endif

        /* +-----------------------------------------------+
           |  Division dans N : c <- a div b, d <- a - bc  |
           +-----------------------------------------------+ */

/*
  mode = 0 -> 0 <= d < b
  mode = 1 -> -b < d <= 0
  capacité(c) >= max(1,lb-la+1), capacité(d) >= max(la,lb)+2
  si d = NULL, ne calcule que c
  b est sauvegardé s'il doit être écrasé
*/

/* algorithme de Burnikel et Ziegler itératif */
#ifndef have_sn_bzquo
void xn(bzquo)(naturel a, longueur la, naturel b, longueur lb,
	       naturel c, naturel d, int mode) {
  longueur lbuff, bloc,lc,nb,i,j,k,l,gap,pos,ib,jb;
  naturel buff,buff_save;
  ndouble bh;
  zdouble ret;
  int sh,copy_a,copy_b,nr;

  /* longueur quotient */
  lc  = la-lb+1;

  /* normaliser b pour avoir b1 >= BASE/2 */
  for (sh=0, bh = b[lb-1];  bh < BASE/2; bh <<= 1, sh++);

  /* allocation de mémoire auxilliaire */
  lbuff  = 2*lb;
  copy_b = (sh) | (b==c) | (b==d);
  copy_a = (sh) || (a != d) || (la < lb+2);
  nr     = (d == NULL);
  if (copy_b) lbuff += lb+1;
  if (nr)     lbuff += max(la,lb)+2;

  /* recopie a et b avec décalage si besoin */
  buff_save = xn(alloc_tmp)(lbuff);
  buff = buff_save;
  if (copy_b) {xn(shl)(b,lb,sh,buff); b = buff; buff += lb;}
  if (nr)     {d = buff, buff += max(la,lb)+2;}
  if (copy_a) {xn(shl)(a,la,sh,d); if (d[la]) la++;}

  /* prolonge d d'un zéro si son chiffre de tête est supérieur à celui de b */
  if (d[la-1] >= b[lb-1]) {d[la++] = 0;}

  /* détermine la taille des blocs de façon à avoir un nombre de */
  /* blocs légèrement inférieur à une puissance de 2             */
  for (bloc=lb, nb=1; bloc >= klim; bloc >>= 1, nb <<= 1);
  if (lb%nb) bloc++;

  /* cadre c et d de façon à terminer sur une puissance de 2 (division */
  /* avec reste) ou une puissance de 2 - 1 (division sans reste)       */
  while (lc > la-lb) c[--lc] = 0;
  if (nr) {d  -= bloc; c -= bloc; la += bloc; lc += bloc;}
  l   = nb*bloc/2;
  k   = (lc+l-1)/l;
  gap = k*l-lc;

  /* calcule le quotient bloc par bloc */
  b += lb; c += lc; d += la-bloc; la = bloc;
  for (i=lc; i > ((nr) ? bloc : 0);) {

    /* calcule le bloc suivant dans c */
    l = i%bloc; if (l == 0) l = bloc;
    d -= l; c -= l;
    la = xn(hquo)(d,la+l,b-bloc,bloc,c,l);
    i -= l;

    /* corrige le reste en doublant à chaque fois le nombre de blocs */
    ib = 2*(lc+gap-i)/bloc;
    for (jb=1, j=bloc; ((ib&jb) == 0) && (j < lb); jb<<=1, j<<=1) {
      k  = min(j, lc-i);
      l  = min(j, lb-j);
      xn(mul_k)(c,k, b-j-l,l, buff);
      pos = l+j-bloc;
      if (la+pos < k+l) la=k+l-pos;
      ret = xn(dec)(d-pos,la+pos,buff,k+l);
      while (ret) {
	xn(dec_1)(c,j,1);
	while (la+pos < j+l) d[la++] = BASE-1;
	ret += xn(inc)(d-pos,la+pos, b-j-l,j+l);
	la = j+l-pos;
      }
    }
  } /* for i */

  /* dernières corrections, pour la division sans reste.            */
  /* arrête dès que le reste peut absorber les retenues éventuelles */
  /* sans atteindre zéro                                            */

  if (nr) {
    for (nb=0, j=2*bloc; j < lb; nb++, j<<=1);
    for (j=2*bloc; j < lb; j<<=1) {

      l = min(0,lc-j);
      while ((la > l) && d[la-1] == 0) la--;
      if ((la > l+1) || ((la == l+1) && (d[la-1] > nb))) break;
      la = max(la,bloc);

      k  = min(j, lc) - bloc;
      l  = min(j, lb-j);
      pos = l+j-bloc;
      xn(mul_k)(c,k, b-j-l,l, buff);
      if (la+pos < k+l) la=k+l-pos;
      ret = xn(dec)(d-pos,la+pos,buff,k+l);
      while (ret) {
	xn(dec_1)(c,j-bloc,1);
	while (la+pos < j+l) d[la++] = BASE-1;
	ret += xn(inc)(d-pos,la+pos, b-j-l,j+l);
	la = bloc;
      }
      nb--;
    }
  } /* if (nr) */

  /* mise à jour longueur reste */
  d -= lb-bloc; la += lb-bloc;
  while ((la > 0) && d[la-1] == 0) la--;

  /* division supérieure : si d <> 0 incrémente le quotient et corrige le reste */
  if ((mode) && (la)) {
    xn(inc_1)(c,lc,1);
    if (!nr) xn(sub)(b-lb,lb,d,la,d);
  }

  /* décaler le reste */
  if ((sh) && (!nr)) xn(shr)(d,lb,sh,d);

  /* terminé */
  xn(free)(buff_save);

}
#endif

       /* +------------------------------------------------+
          |  Division adaptée à la longueur des opérandes  |
          +------------------------------------------------+ */

#ifndef have_sn_quo_k
void xn(quo_k)(naturel a, longueur la, naturel b, longueur lb, naturel c, naturel d) {
  ndouble r;

  if ((lb >= klim) && (la-lb >= klim)) xn(bzquo) (a,la,b,lb,c,d,0);
  else if (lb >= 3)                    xn(quo_n2)(a,la,b,lb,c,d);
  else {
    r = (lb == 1) ? b[0] : (ndouble)b[0] + (((ndouble)b[1]) << HW);
    r = xn(quo_2)(a,la,r,c);
    d[0] = r;
    d[1] = r >> HW;
  }
}
#endif

                  /* +--------------------------+
                     |  Division rapide dans Z  |
                     +--------------------------+ */

#ifndef have_sz_quo_k
void xz(quo_k)(entier *a, entier *b, entier *c, entier *d) {
  longueur la = Lg(a), lb = Lg(b), lc, lbuff;
  longueur sa = Signe(a), sb = Signe(b);
  chiffre  *bb, *dd, *buff, *buff_save;
  ndouble  b2,r;

  /* aiguillage selon la taille du diviseur */
  switch (lb) {
  case 0:
    xn(fatal_err)("\nquo_k, division by zero\n");

  case 1:
    r = xn(quo_2)(a->val,la,b->val[0],c->val);
    if ((sa^sb) && (r)) {xn(inc_1)(c->val,la,1); r = b->val[0]-r;}
    make_head(c,la, sa^sb);
    if (d) {d->val[0] = r; make_head(d,1,sb);}
    return;

  case 2:
    b2 = (((ndouble)b->val[1]) << HW) + (ndouble)b->val[0];
    r = xn(quo_2)(a->val,la,b2, c->val);
    if ((sa^sb) && (r)) {xn(inc_1)(c->val,la,1); r = b2-r;}
    make_head(c,la, sa^sb);
    if (d) {d->val[0] = r; d->val[1] = r >> HW; make_head(d,2,sb);}
    return;
  }

  lc = max(la-lb+1,1);

  /* petit diviseur ou petit quotient -> quo_n2 */
  if ((lb < klim) || (la-lb < klim)) {

    /* mémoire auxilliaire */
    lbuff = 0;
    if ((b == c) || (b == d)) lbuff += lb;
    if (d == NULL) lbuff += max(la,lb)+2;
    if (lbuff) {buff_save = xn(alloc_tmp)(lbuff); buff = buff_save;}
    else {buff_save = buff = NULL;}

    if ((b == c) || (b == d)) {xn(cpy)(buff,b->val,lb); bb = buff; buff += lb;}
    else {bb = b->val;}
    if (d) {dd = d->val;} else {dd = buff;}

    /* division avec reste */
    xn(quo_n2)(a->val,la,bb,lb,c->val,dd);

    /* correction si ab < 0 */
    if ((sa^sb) && (xn(cmp)(dd,lb,NULL,0))) {
      xn(inc_1)(c->val,lc,1);
      if (d) xn(sub)(bb,lb,dd,lb,dd);
    }
  
    make_head(c,lc, sa^sb);
    if (d) make_head(d,lb,sb);
    if (buff_save) xn(free)(buff_save);
    return;
  }
  
  /* grande division */
  else {
    if (d) {dd = d->val;} else dd = NULL;
    xn(bzquo)(a->val,la, b->val,lb, c->val, dd, (sa != sb));
    make_head(c,lc, sa^sb);
    if (d) make_head(d,lb,sb);
    return;
  }
  
}
#endif


