/*
   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                    |
   |                                                                        |
   |                                PGCD                                    |
   |                                                                        |
   +------------------------------------------------------------------------+ */

/* M. Quercia, 31/01/2001 */

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

/* change de signe si non nul */
#define NEG(x) if (x->hd) x->hd ^= SIGN_m

/* seuil au dessous du quel on applique l'algorithme de lehmer naïf */
#define lehmer_lim (8*klim)



                /* +------------------------------+
                   |  Multiplication de Strassen  |
                   +------------------------------+ */

/*
   [p u]    [a b] [p u]
   [q v] <- [c d]*[q v] avec 7 multiplications au lieu de 8.
   Pour le calcul du pgcd, on économise 5% de multiplications ->
   ça ne vaut pas le coup vu la consomation mémoire supplémentaire.
*/
#undef  use_strassen
#ifdef use_strassen
void xz(strassen_mul)(entier *a, entier *b, entier *c, entier *d,
		      entier *p, entier *q, entier *u, entier *v) {
  entier *buff,*p1,*p2,*p3,*p4,*p5,*p6,*p7;
  longueur la,lp,l;

  /* si [[a,b],[c,d]] = id ou [[p,q],[u,v]] = id, ne fait pas le calcul */
  if ((a->hd == 1) && (a->val[0] == 1) && (b->hd == 0) &&
      (d->hd == 1) && (d->val[0] == 1) && (c->hd == 0)) return;

  if ((p->hd == 1) && (p->val[0] == 1) && (u->hd == 0) &&
      (v->hd == 1) && (v->val[0] == 1) && (q->hd == 0)) {
    xz(cpy)(p,a,Lg(a)); xz(cpy)(u,b,Lg(b));
    xz(cpy)(q,c,Lg(c)); xz(cpy)(v,d,Lg(d));
    return;
  }

  /* sinon, algorithme de Strassen */
  la = Lg(a);
  if (la < Lg(b)) la = Lg(b);
  if (la < Lg(c)) la = Lg(c);
  if (la < Lg(d)) la = Lg(d);

  lp = Lg(p);
  if (lp < Lg(q)) lp = Lg(q);
  if (lp < Lg(u)) lp = Lg(u);
  if (lp < Lg(v)) lp = Lg(v);

  l = la+lp+3;
  buff = xz(alloc_tmp)(zmem(6,7*l));
  p1 = buff;
  p2 = zalign(p1,l);
  p3 = zalign(p2,l);
  p4 = zalign(p3,l);
  p5 = zalign(p4,l);
  p6 = zalign(p5,l);
  p7 = zalign(p6,l);

  /* p7 <- (d-b)(q+v) = -bq - bv + dq + dv */
  xz(sub)(d,b,p1); xz(add)(q,v,p2); xz(mul_k)(p1,p2,p7);

  /* p6 <- (a-c)(p+u) = ap + au -cp -cu */
  xz(sub)(a,c,p1); xz(add)(p,u,p2); xz(mul_k)(p1,p2,p6);

  /* p5 <- (a+d)(p+v) = ap + av + dp + dv */
  xz(add)(a,d,p1); xz(add)(p,v,p2); xz(mul_k)(p1,p2,p5);

  /* p4 <- (c+d)p = cp + dp */
  xz(add)(c,d,p1); xz(mul_k)(p1,p,p4);

  /* p3 <- d(p-q) = dp - dq */
  xz(sub)(p,q,p1); xz(mul_k)(d,p1,p3);

  /* p2 <- (a+b)v = av + bv */
  xz(add)(a,b,p1); xz(mul_k)(p1,v,p2);

  /* p1 <- a(u-v) = au - av */
  xz(sub)(u,v,u); xz(mul_k)(a,u,p1);

  /* p <- p5 - p7 - p2 - p3 = ap + bq */
  xz(sub)(p5,p7,p7); xz(sub)(p7,p2,p7); xz(sub)(p7,p3,p);

  /* q <- p4 - p3 = cp + dq */
  xz(sub)(p4,p3,q);

  /* u <- p1 + p2 = au + bv */
  xz(add)(p1,p2,u);

  /* v <- p5 - p6 - p4 + p1 = cu + dv */
  xz(sub)(p5,p6,p6); xz(sub)(p6,p4,p6); xz(add)(p6,p1,v);

  /* terminé */
  xz(free)(buff);

}
#endif

        /* +-----------------------------------------------+
           |  Développement en fraction continue approché  |
           +-----------------------------------------------+ */

/*
  a,b sont positifs ou nuls.

  Développe a/(b+1) et (a+1)/b en fraction continue tant que les
  quotients sont les mêmes et la précision des nombres résiduels
  est d'au moins deux chiffres. Retourne a',b',p,q,u,v tels que :

     [ u  -v][a]      [a']
     [-q   p][b]   =  [b']

     up - vq = 1,
     p,q,u,v >= 0
     a' >= v,
     b' >= q,

     -(q+u) <= a'-b' <= p+v
     ou (a-v)/(b+p) et (a+u)/(b-q) ont moins de deux chiffres
     significatifs en commun (estimation approximative).

  a' et b' sont retournés dans a et b
  capacité(a,b,p,q,u,v) >= max(Lg(a),Lg(b)+2

*/

void xz(lehmer)(entier *a, entier *b,
		entier *p, entier *q, entier *u, entier *v) {
  entier *buff, *x,*y,*z,*a0,*b0,*p0,*q0,*u0,*v0;
  ndouble a1,b1,p1,q1,u1,v1,x1;
  longueur l,l0,l1,l2;
  int i,needdiv;

  /* initialisation : a'=a, b'=b, [[u,-v],[-q,p]] = id. */
  /* classe a et b pour avoir a >= b.                   */
  /* si b-a <= 1, c'est fini.                           */

  p->hd = u->hd = 1; p->val[0] = u->val[0] = 1;
  xz(sub)(a,b,q);
  if (Signe(q)) {z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z; z->hd &= LONG_m;}
  else z=q;
  if (xz(cmp)(z,p) <= 0) {q->hd = v->hd = 0; return;} else q->hd = v->hd = 0;

  /* mémoire auxilliaire */
  l = Lg(a)+2;
  if (Lg(b) <= lehmer_lim) {
    buff = xz(alloc_tmp)(zmem(1,2*l));
    x  = buff;
    y  = zalign(x, l);
#ifdef useless_init
    a0 = b0 = p0 = q0 = u0 = v0 = NULL;
#endif
  } else {
    buff = xz(alloc_tmp)(zmem(7,8*l));
    x  = buff;
    y  = zalign(x, l);
    a0 = zalign(y, l);
    b0 = zalign(a0,l);
    p0 = zalign(b0,l);
    q0 = zalign(p0,l);
    u0 = zalign(q0,l);
    v0 = zalign(u0,l);
  }
  
  for (l0=0; l0+2 <= Lg(b);) {

    /* 
       l0 = nb de chiffres de poids faible douteux :
       a+u <= ((a-v)/BASE^l0 + 1)*BASE^l0
       b+p <= ((b-q)/BASE^l0 + 1)*BASE^l0
    */

    /* si b est assez grand, dichotomise */
    if (Lg(b)-l0 > lehmer_lim) {

      if (l0 == 0) l0 = (Lg(b)+1)/2;
      xz(split)(a,l0*HW,a0,a);
      xz(split)(b,l0*HW,b0,b);
      xz(lehmer)(a0,b0,p0,q0,u0,v0);

      /* si on n'a pas avancé, il faudra faire une division */
      if ((q0->hd == 0) && (v0->hd == 0)) needdiv = 1;

      /* sinon répercute sur les parties basses et sur p,q,u,v */
      else {
	needdiv = 0;

	xz(mul_k)(a,q0,x); xz(mul_k)(b,v0,y);
	xz(mul_k)(a,u0,a); xz(mul_k)(b,p0,b);
	xz(sub)(a,y,a);    xz(sub)(b,x,b);

#ifdef use_strassen
	xz(strassen_mul)(u0,v0,q0,p0,u,q,v,p);
#else	
	xz(mul_k)(u,q0,x); xz(mul_k)(q,v0,y);
	xz(mul_k)(u,u0,u); xz(mul_k)(q,p0,q);
	xz(add)(u,y,u);    xz(add)(q,x,q);

	xz(mul_k)(v,q0,x); xz(mul_k)(p,v0,y);
	xz(mul_k)(v,u0,v); xz(mul_k)(p,p0,p);
	xz(add)(v,y,v);    xz(add)(p,x,p);
#endif
      }

      /* recolle les parties hautes et basses */
      xz(join)(a,a0,l0*HW,a);
      xz(join)(b,b0,l0*HW,b);

    } /* if (Lg(b) > lehmer_lim) */

    /* si a et b ont même longueur à 1 près, travaille avec   */
    /* les chiffres de poids fort                             */
    else if (Lg(a) - Lg(b) <= 1) {
      l = Lg(a)-1;
      a1 = ((ndouble)(a->val[l]) << HW) + (ndouble)(a->val[l-1]);
      b1 = ((Lg(a) == Lg(b)) ? (ndouble)(b->val[l]) << HW : 0)
	 + (ndouble)(b->val[l-1]);

      /* s'il y a au moins trois chiffres disponibles, décale */
      /* a1 et b1 pour incorporer les bits suivants.          */
      if (l >= l0+2) {
	x1 = (ndouble)(a->val[l]);
	for (i=0; (x1 & (BASE/2)) == 0; i++, x1<<=1);
	if (i) {
	  a1 = (a1 << i) + ((ndouble)(a->val[l-2]) >> (HW-i));
	  b1 = (b1 << i) + ((ndouble)(b->val[l-2]) >> (HW-i));
	}
      }

      /* effectue la première division à la main pour le cas où */
      /* a1+1 dépasserait la capacité d'un ndouble              */
      p1 = 1; u1 = 1; q1=0;
      v1 = a1/(b1+1); a1 -= v1*b1;

      /* continue jusqu'à ce que -(q1+u1) <= a1-b1 <= p1+v1 */
      while (b1-q1 > a1+u1) {
	x1 = (b1-q1)/(a1+u1); b1 -= x1*a1; p1 += x1*v1; q1 += x1*u1;
	if (a1-v1 <= b1+p1) break;
	x1 = (a1-v1)/(b1+p1); a1 -= x1*b1; u1 += x1*q1; v1 += x1*p1;
      }

      /* si on n'a pas avancé, il faudra faire une division */
      if ((q1 == 0) && (v1 == 0)) needdiv = 1;
      else {
	needdiv = 0;

	/* effectue la transformation sur a et b */
	xn(mul_2)(a->val,Lg(a),q1,x->val); make_head(x,Lg(a)+2,0);
	xn(mul_2)(b->val,Lg(b),v1,y->val); make_head(y,Lg(b)+2,0);
	xn(mul_2)(a->val,Lg(a),u1,a->val); make_head(a,Lg(a)+2,0);
	xn(mul_2)(b->val,Lg(b),p1,b->val); make_head(b,Lg(b)+2,0);
	xz(sub)(a,y,a);
	xz(sub)(b,x,b);

      /* répercute sur p,q,u,v */
	xn(mul_2)(p->val,Lg(p),v1,x->val); make_head(x,Lg(p)+2,0);
	xn(mul_2)(v->val,Lg(v),q1,y->val); make_head(y,Lg(v)+2,0);
	xn(mul_2)(p->val,Lg(p),p1,p->val); make_head(p,Lg(p)+2,0);
	xn(mul_2)(v->val,Lg(v),u1,v->val); make_head(v,Lg(v)+2,0);
	xz(add)(p,y,p);
	xz(add)(v,x,v);
      
	xn(mul_2)(q->val,Lg(q),v1,x->val); make_head(x,Lg(q)+2,0);
	xn(mul_2)(u->val,Lg(u),q1,y->val); make_head(y,Lg(u)+2,0);
	xn(mul_2)(q->val,Lg(q),p1,q->val); make_head(q,Lg(q)+2,0);
	xn(mul_2)(u->val,Lg(u),u1,u->val); make_head(u,Lg(u)+2,0);
	xz(add)(q,y,q);
	xz(add)(u,x,u);
      }

    } else {needdiv = 1;} /* if Lg(a) - Lg(b) <= 1 */

    /* si on n'a pas avancé, divise a-v par b+p et retourne les fractions */
    if (needdiv) {
      xz(sub)(a,v,a); xz(add)(b,p,y);
      xz(quo_k)(a,y,x,a);
      xz(mul_k)(q,x,y); xz(add)(u,y,u);
      xz(mul_k)(p,x,y); xz(add)(v,y,v); xz(add)(a,v,a);
    }

    /* reclasse a et b et sort si a-b <= p+v */
    xz(sub)(a,b,x);
    if (Signe(x)) {z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z; x->hd &= LONG_m;}
    xz(add)(p,v,y);
    if (xz(cmp)(x,y) <= 0) break;
	
    /* mise à jour de l0 */
    for (l1=Lg(u); (l1 < Lg(a)) && ((ndouble)a->val[l1] == BASE-1); l1++);
    for (l2=Lg(v); (l2 < Lg(a)) && ((ndouble)a->val[l2] == 0);      l2++);
    l0 = max(l1,l2);
    for (l1=Lg(p); (l1 < Lg(b)) && ((ndouble)b->val[l1] == BASE-1); l1++);
    for (l2=Lg(q); (l2 < Lg(b)) && ((ndouble)b->val[l2] == 0);      l2++);
    l1 = max(l1,l2);
    l0 = max(l0,l1) + 1;
    
  } /* while (l0+2 <= Lg(b)) */

  xz(free)(buff);
  
}


         /* +--------------------------------------------+
            |  Développement en fraction continue exact  |
            +--------------------------------------------+ */

/*
  a,b sont positifs ou nuls. Développe a/b en fraction continue et
  retourne a',b',u,v,p,q tels que :

     [ u  -v][a]      [a']
     [-q   p][b]   =  [b']

     up - vq = 1,
     p,q,u,v >= 0
     a'b' = 0

  a' et b' sont retournés dans a et b, capacité(a,b)  >= max(Lg(a),Lg(b)+2.
  Si p = q = u = v = NULL alors p,q,u,v ne sont pas calculés,
  sinon capacité(p,q,u,v) >= max(Lg(a),Lg(b)+2

*/

void xz(cfrac)(entier *a, entier *b,
	       entier *p, entier *q, entier *u, entier *v) {

  entier *buff,*x,*y,*z,*a0,*b0,*p0,*q0,*u0,*v0;
  ndouble a1,b1,p1,q1,u1,v1,x1;
  longueur l,l0;
  int i,needdiv;

  /* init transfo = id et classe a et b pour avoir a >= b */
  if (p) {p->hd = u->hd = 1; p->val[0] = u->val[0] = 1;  q->hd = v->hd = 0;}
  if (xz(cmp)(a,b) < 0) {z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z;}

  /* mémoire auxilliaire */
  l = Lg(a)+2;
  if (Lg(b) <= lehmer_lim) {
    buff = xz(alloc_tmp)(zmem(1,2*l));
    x  = buff;
    y = zalign(x,l);
#ifdef useless_init
    a0 = b0 = p0 = q0 = u0 = v0 = NULL;
#endif
  } else {
    buff = xz(alloc_tmp)(zmem(7,8*l));
    x  = buff;
    y  = zalign(x, l);
    a0 = zalign(y, l);
    b0 = zalign(a0,l);
    p0 = zalign(b0,l);
    q0 = zalign(p0,l);
    u0 = zalign(q0,l);
    v0 = zalign(u0,l);
  }

  /* si b est assez grand, dichotomise */
  if (Lg(b) > lehmer_lim) {

    for (i=0; (i<2) && (Lg(b) > 10); i++) { /* deux tours maxi */
      l0 = (i==0) ? Lg(b)/2 : Lg(b)/3;

      xz(split)(a,l0*HW,a0,a);
      xz(split)(b,l0*HW,b0,b);
      xz(lehmer)(a0,b0,p0,q0,u0,v0);

      /* si on n'a pas avancé, divise a par b */
      if ((q0->hd == 0) && (v0->hd == 0)) {
	xz(join)(a,a0,l0*HW,a);
	xz(join)(b,b0,l0*HW,b);
	xz(quo_k)(a,b,v0,a);
      }

      /* sinon répercute sur les parties basses */
      else {
	xz(mul_k)(a,q0,x); xz(mul_k)(b,v0,y);
	xz(mul_k)(a,u0,a); xz(mul_k)(b,p0,b);
	xz(sub)(a,y,a);    xz(sub)(b,x,b);
	xz(join)(a,a0,l0*HW,a);
	xz(join)(b,b0,l0*HW,b);
      }

      /* mise à jour de p,q,u,v */
      if (p) {
#ifdef use_strassen
	xz(strassen_mul)(u0,v0,q0,p0,u,q,v,p);
#else	
	xz(mul_k)(u,q0,x); xz(mul_k)(q,v0,y);
	xz(mul_k)(u,u0,u); xz(mul_k)(q,p0,q);
	xz(add)(u,y,u);    xz(add)(q,x,q);

	xz(mul_k)(v,q0,x); xz(mul_k)(p,v0,y);
	xz(mul_k)(v,u0,v); xz(mul_k)(p,p0,p);
	xz(add)(v,y,v);    xz(add)(p,x,p);
#endif
      }

      /* reclasse a et b */
      if (xz(cmp)(a,b) < 0) {z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z;}
    }

    /* termine récursivement */
    if (Lg(b)) {
      if (p) {
	xz(cfrac)(a,b,p0,q0,u0,v0);
#ifdef use_strassen
	xz(strassen_mul)(u0,v0,q0,p0,u,q,v,p);
#else	
	xz(mul_k)(u,q0,x); xz(mul_k)(q,v0,y);
	xz(mul_k)(u,u0,u); xz(mul_k)(q,p0,q);
	xz(add)(u,y,u);    xz(add)(q,x,q);

	xz(mul_k)(v,q0,x); xz(mul_k)(p,v0,y);
	xz(mul_k)(v,u0,v); xz(mul_k)(p,p0,p);
	xz(add)(v,y,v);    xz(add)(p,x,p);
#endif
      }
      else xz(cfrac)(a,b,p,q,u,v);
    }
    xz(free)(buff);
    return;
  } /* if (Lg(b) > lehmer_lim) */

  /* sinon, algorithme de Lehmer */
  while (Lg(b) > 2) {

    /* si a et b ont même longueur à 1 près, travaille avec   */
    /* les chiffres de poids fort                             */
    if (Lg(a) - Lg(b) <= 1) {
      l = Lg(a)-1;
      a1 = ((ndouble)(a->val[l]) << HW) + (ndouble)(a->val[l-1]);
      b1 = ((Lg(a) == Lg(b)) ? (ndouble)(b->val[l]) << HW : 0)
	+ (ndouble)(b->val[l-1]);

      /* s'il y a au moins trois chiffres disponibles, décale */
      /* a1 et b1 pour incorporer les bits suivants.          */
      if (l >= 2) {
	x1 = (ndouble)(a->val[l]);
	for (i=0; (x1 & (BASE/2)) == 0; i++, x1<<=1);
	if (i) {
	  a1 = (a1 << i) + ((ndouble)(a->val[l-2]) >> (HW-i));
	  b1 = (b1 << i) + ((ndouble)(b->val[l-2]) >> (HW-i));
	}
      }

      /* effectue la première division à la main pour le cas où */
      /* a1+1 dépasserait la capacité d'un ndouble              */
      p1 = 1; u1 = 1; q1=0;
      v1 = a1/(b1+1); a1 -= v1*b1;

      /* continue jusqu'à ce que -(q1+u1) <= a1-b1 <= p1+v1 */
      while (b1-q1 > a1+u1) {
	x1 = (b1-q1)/(a1+u1); b1 -= x1*a1; p1 += x1*v1; q1 += x1*u1;
	if (a1-v1 <= b1+p1) break;
	x1 = (a1-v1)/(b1+p1); a1 -= x1*b1; u1 += x1*q1; v1 += x1*p1;
      }

    /* si on n'a pas avancé, il faudra faire une division */
      if ((q1 == 0) && (v1 == 0)) needdiv = 1;
      else {
	needdiv = 0;

	/* effectue la transformation sur a et b */
	xn(mul_2)(a->val,Lg(a),q1,x->val); make_head(x,Lg(a)+2,0);
	xn(mul_2)(b->val,Lg(b),v1,y->val); make_head(y,Lg(b)+2,0);
	xn(mul_2)(a->val,Lg(a),u1,a->val); make_head(a,Lg(a)+2,0);
	xn(mul_2)(b->val,Lg(b),p1,b->val); make_head(b,Lg(b)+2,0);
	xz(sub)(a,y,a);
	xz(sub)(b,x,b);
      
	/* répercute sur p,q,u,v */
	if (p) {
	  xn(mul_2)(p->val,Lg(p),v1,x->val); make_head(x,Lg(p)+2,0);
	  xn(mul_2)(v->val,Lg(v),q1,y->val); make_head(y,Lg(v)+2,0);
	  xn(mul_2)(p->val,Lg(p),p1,p->val); make_head(p,Lg(p)+2,0);
	  xn(mul_2)(v->val,Lg(v),u1,v->val); make_head(v,Lg(v)+2,0);
	  xz(add)(p,y,p);
	  xz(add)(v,x,v);
	
	  xn(mul_2)(q->val,Lg(q),v1,x->val); make_head(x,Lg(q)+2,0);
	  xn(mul_2)(u->val,Lg(u),q1,y->val); make_head(y,Lg(u)+2,0);
	  xn(mul_2)(q->val,Lg(q),p1,q->val); make_head(q,Lg(q)+2,0);
	  xn(mul_2)(u->val,Lg(u),u1,u->val); make_head(u,Lg(u)+2,0);
	  xz(add)(q,y,q);
	  xz(add)(u,x,u);
	}

	if (xz(cmp)(a,b) < 0) {z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z;}
      }
    } else needdiv = 1;

    if (needdiv) {
      xz(quo_k)(a,b,x,a);
      if (p) {
	xz(mul_k)(q,x,y); xz(add)(u,y,u);
	xz(mul_k)(p,x,y); xz(add)(v,y,v);
      }
      z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z;
    }
  } /* while(Lg(b) > 2) */

  /* Lg(b) <= 2 : termine à la main */
  if (Lg(b)) {

    /* réduit a si Lg(a) > 2 */
    if (Lg(a) > 2) {
      xz(quo_k)(a,b,x,a);
      if (p) {
	xz(mul_k)(q,x,y); xz(add)(u,y,u);
	xz(mul_k)(p,x,y); xz(add)(v,y,v);
      }
      z=a;a=b;b=z; z=p;p=u;u=z; z=q;q=v;v=z;
    }

    /* algorithme d'Euclide sur les deux chiffres restant */
    if (Lg(b)) {
      a1 = ((Lg(a) > 1) ? (ndouble)(a->val[1]) << HW : 0) + (ndouble)(a->val[0]);
      b1 = ((Lg(b) > 1) ? (ndouble)(b->val[1]) << HW : 0) + (ndouble)(b->val[0]);
      p1 = u1 = 1; q1 = v1 = 0;

      while(b1) {
	x1 = a1/b1; a1 -= x1*b1; u1 += x1*q1; v1 += x1*p1; if (a1 == 0) break;
	x1 = b1/a1; b1 -= x1*a1; p1 += x1*v1; q1 += x1*u1;
      }

      /* mise à jour a,b,p,q,u,v */
      a->val[0] = a1; a->val[1] = a1>>HW; make_head(a,2,0);
      b->val[0] = b1; b->val[1] = b1>>HW; make_head(b,2,0);
      
      if (p) {
	xn(mul_2)(p->val,Lg(p),v1,x->val); make_head(x,Lg(p)+2,0);
	xn(mul_2)(v->val,Lg(v),q1,y->val); make_head(y,Lg(v)+2,0);
	xn(mul_2)(p->val,Lg(p),p1,p->val); make_head(p,Lg(p)+2,0);
	xn(mul_2)(v->val,Lg(v),u1,v->val); make_head(v,Lg(v)+2,0);
	xz(add)(p,y,p);
	xz(add)(v,x,v);
	
	xn(mul_2)(q->val,Lg(q),v1,x->val); make_head(x,Lg(q)+2,0);
	xn(mul_2)(u->val,Lg(u),q1,y->val); make_head(y,Lg(u)+2,0);
	xn(mul_2)(q->val,Lg(q),p1,q->val); make_head(q,Lg(q)+2,0);
	xn(mul_2)(u->val,Lg(u),u1,u->val); make_head(u,Lg(u)+2,0);
	xz(add)(q,y,q);
	xz(add)(u,x,u);
      }
    } /* if (Lg(b) (interne) */
  }   /* if (Lg(b) (externe) */

  xz(free)(buff);
}

/* +------------------------------------------------------------------------+
   |                       [u -v]                        [u -v][a]   [x]    |
   |   calcule une matrice [-q p] unimodulaire telle que [-q p][b] = [0]    |
   |   où x est le pcgd positif de a et b. p et q ont les signes de a et b. |
   |   capacité(u,p,v,q,x) >= max(la,lb)+2                                  |
   +------------------------------------------------------------------------+ */
                      
void xz(cfrac_k)(entier *a, entier *b,
		 entier *p, entier *q, entier *u, entier *v, entier *d) {
  entier *buff,*z;
  longueur l = max(Lg(a),Lg(b)), lbuff, sa,sb;

  /* mémoire auxilliaire */
  lbuff = zmem(1,2*(l+2));
  if ((u) && (p==NULL))    lbuff += zmem(2,2*(l+2));

  if (lbuff) buff = xz(alloc_tmp)(lbuff); else buff = NULL;
  z = buff;
  xz(cpy)(z,a,Lg(a)); a=z; z = zalign(a,l+2);
  xz(cpy)(z,b,Lg(b)); b=z; z = zalign(b,l+2);
  if ((u) && (p==NULL)) {p = z; q = zalign(p,l+2); z=zalign(q,l+2);}

  /* retire les signes et décompose a/b en fraction continue */
  sa = Signe(a); a->hd &= LONG_m;
  sb = Signe(b); b->hd &= LONG_m;
  xz(cfrac)(a,b,p,q,u,v);

  /* le pgcd est dans a ou b */
  if (Lg(b) == 0) xz(cpy)(d,a,Lg(a));
  else {
    if (u) {
      xz(cpy)(a,p,Lg(p)); xz(cpy)(p,v,Lg(v)); xz(cpy)(v,a,Lg(a)); 
      xz(cpy)(a,q,Lg(q)); xz(cpy)(q,u,Lg(u)); xz(cpy)(u,a,Lg(a)); 
      NEG(u); NEG(v);
    }
    xz(cpy)(d,b,Lg(b));
  }

  /* fixe les signes */
  if (u) {
    if (sa) {NEG(u); NEG(p);}
    if (sb) {NEG(v); NEG(q);}
  }

  /* terminé ... */
  if (lbuff) xz(free)(buff);

}
