/*
   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 extensibles de longueur arbitraire                 |
   |                                                                        |
   |                      Interface publique C                              |
   |                                                                        |
   +------------------------------------------------------------------------+ */

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


#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "c-long_int.h"

                               /* +-----------+
                                  |  Erreurs  |
                                  +-----------+ */
static void failwith(char *msg) {
  fprintf(stderr,"\n%s\n",msg);
  fflush(stderr);
  exit(1);
}

                           /* +-------------------+
                              |  Gestion mémoire  |
                              +-------------------+ */

/* alloue l chiffres */
#define Alloc(a,l) {                          \
    longueur alloc_size = l;                  \
    a = (xint)malloc(alloc_size*sizeof(chiffre) + 2*sizeof(longueur)); \
    if (a == NULL) failwith("out of memory"); \
    a->lmax = alloc_size;                     \
}				         

/* agrandit x au besoin à l chiffres et retourne le nouveau pointeur dans a */
#define Enlarge(a,x,l) {                      \
  if ((*x == NULL) || ((*x)->lmax < (l))) {   \
    longueur alloc_size = 2*(l)+1;            \
    a = (xint)malloc(alloc_size*sizeof(chiffre) + 2*sizeof(longueur)); \
    if (a == NULL) failwith("out of memory"); \
    a->lmax = alloc_size;                     \
  } else a = *x;                              \
}				         

/* création et initialisation */
xint xx(of_int)(long a) {
  xint c;
  longueur sa = (a < 0) ? SIGN_m : 0;
  if (sa) a = -a;

  Alloc(c,2);
  if (a == 0)         {c->e.hd = 0;}
#ifndef use_dlong
  else if (a >= BASE) {c->e.hd = 2|sa; c->e.val[0] = a; c->e.val[1] = a >> HW;}
#endif
  else                {c->e.hd = 1|sa; c->e.val[0] = a;}
  return(c);
}

xint xx(of_string)(char *s) {
  xint a;
  longueur la = xz(size_of_string)(s);
  Alloc(a,la);
  xz(of_string)(s,&(a->e));
  return(a);
}

xint xx(of_xint)(xint b) {
  xint a;
  longueur l = Lgx(b);
  Alloc(a,l);
  memmove(&(a->e), &(b->e), l*sizeof(chiffre)+sizeof(longueur));
  return(a);
}

/* x <- b */
void xx(copy)(xint *x, xint b) {
  xint a;
  longueur l = Lgx(b);
  Enlarge(a,x,l);
  memmove(&(a->e), &(b->e), l*sizeof(chiffre)+sizeof(longueur));
  if (a != *x) {xx(free)(x); *x = a;}
}

/* x <- a */
void xx(copy_int)(xint *x, long a) {
  xint c;
  longueur sa = (a < 0) ? SIGN_m : 0;
  if (sa) a = -a;

  Enlarge(c,x,2);
  if (a == 0)         {c->e.hd = 0;}
#ifndef use_dlong
  else if (a >= BASE) {c->e.hd = 2|sa; c->e.val[0] = a; c->e.val[1] = a >> HW;}
#endif
  else                {c->e.hd = 1|sa; c->e.val[0] = a;}
  if (c != *x) {xx(free)(x); *x = c;}
}

/* x <- val(s) */
void xx(copy_string)(xint *x, char *s) {
  xint a;
  longueur la = xz(size_of_string)(s);
  Enlarge(a,x,la);
  xz(of_string)(s,&(a->e));
  if (a != *x) {xx(free)(x); *x = a;}
}


             /* +-------------------------------------+
                |  Accès à la représentation binaire  |
                +-------------------------------------+ */

/*
  nbits(a)      : taille de a en base 2
  lowbits(a)    : bits 0..31 de |a|
  highbits(a)   : bits l-31..l-1 de |a|, l = nbits(a)
  nth_word(a,n) : bits 16*(n-1)..16*n-1 de |a|
  int_of(a)     : a
*/

long xx(nbits)(xint a) {
  chiffre  x;
  longueur l = Lgx(a);
  if (l) for (l--, x = a->e.val[l], l *= HW; (x); x >>= 1, l++);
  return(l);
}

long xx(lowbits)(xint a) {
  longueur la = Lgx(a);
  ndouble  x = (la == 0) ? 0
             : (la == 1) ? a->e.val[0]
             : ((ndouble)a->e.val[1] << HW) + (ndouble)a->e.val[0];
  return(x & 0x7fffffff);
}

long xx(highbits)(xint a) {
  longueur la = Lgx(a);
  ndouble  x,y;

  if (la == 0) return(0);

  x = (ndouble)a->e.val[la-1] << HW;
  if (la >= 2) x += (ndouble)a->e.val[la-2];
  y = (la >= 3) ? a->e.val[la-3] : 0;
  while (x < (ndouble)0x40000000) {
    x <<= 1;
    y <<= 1;
    if (y >= BASE) {x++; y -= BASE;}
  }
  while (x >= (ndouble)0x80000000) x >>= 1;
  return(x);
}

long xx(nth_word)(xint a, long n) {
  longueur la = Lgx(a);
  longueur n1,n2;

  if (n < 0) failwith("negative word count");
  n1 = n/(sizeof(chiffre)/2);
  n2 = 16*(n%(sizeof(chiffre)/2));
  return((n1 < la) ? (a->e.val[n1] >> n2) & 0xffff : 0);
}


long xx(int_of)(xint a) {
  longueur la = Lgx(a);
  ndouble aa = (la == 0) ? 0
             : (la == 1) ? a->e.val[0]
             : ((ndouble)a->e.val[1] << HW) + (ndouble)a->e.val[0];
  if ((la > 2) | (aa & SIGN_m)) failwith ("integer overflow");
  return((Sgx(a)) ? -aa : aa);
}
                        /* +------------------------+
                           |  Comparaison xint int  |
                           +------------------------+ */

int xx(cmp_1)(xint a, long b) {
  xint x = xx(of_int)(b);
  int res = xz(cmp)(&(a->e), &(x->e));
  xx(free)(&x);
  return(res);
}


                       /* +--------------------------+
                          |  Valeur absolue, opposé  |
                          +--------------------------+ */

void xx(abs)(xint *x, xint a) {
  longueur la = Lgx(a);
  xint c;
  Enlarge(c,x,la);
  if (c != a) memcpy(c->e.val,a->e.val,la*sizeof(chiffre));
  c->e.hd = la;
  if (c != *x) {xx(free)(x); *x = c;}
}

void xx(neg)(xint *x, xint a) {
  longueur la = Lgx(a);
  xint c;
  Enlarge(c,x,la);
  if (c != a) memcpy(c->e.val,a->e.val,la*sizeof(chiffre));
  c->e.hd = (la) ? (la | (Sgx(a)^SIGN_m)) : 0;
  if (c != *x) {xx(free)(x); *x = c;}
}


                        /* +-------------------------+
                           |  Addition/soustraction  |
                           +-------------------------+ */

void xx(add)(xint *x, xint a, xint b) {
  xint c;
  longueur la = Lgx(a), lb = Lgx(b);
  Enlarge(c,x,max(la,lb)+1);
  xz(add)(&(a->e),&(b->e),&(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}

void xx(add_1)(xint *x, xint a, long b){
  xint bb = xx(of_int)(b);
  xx(add)(x,a,bb);
  xx(free)(&bb);
}

void xx(sub)(xint *x, xint a, xint b) {
  xint c;
  longueur la = Lgx(a), lb = Lgx(b);
  Enlarge(c,x,max(la,lb)+1);
  xz(sub)(&(a->e),&(b->e),&(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}

void xx(sub_1)(xint *x, xint a, long b){
  xint bb = xx(of_int)(b);
  xx(sub)(x,a,bb);
  xx(free)(&bb);
}


                              /* +-------------+
                                 |  Décalages  |
                                 +-------------+ */


void xx(shl)(xint *x, xint a, long b) {
  xint c;
  longueur la = Lgx(a), lc;

  lc = (b >= 0) ? la + b/HW + 1 : la - (-b/HW);
  if (lc > 0) {
    Enlarge(c,x,lc);
    xz(shift)(&(a->e), b, &(c->e));
    if (c != *x) {xx(free)(x); *x = c;}
  } else {
    (*x)->e.hd = 0;
  }
}

void xx(shr)(xint *x, xint a, long b) {
  xint c;
  longueur la = Lgx(a), lc;

  lc = (b <= 0) ? la + (-b)/HW + 1 : la - b/HW;
  if (lc > 0) {
    Enlarge(c,x,lc);
    xz(shift)(&(a->e), -b, &(c->e));
    if (c != *x) {xx(free)(x); *x = c;}
  } else {
    (*x)->e.hd = 0;
  }
}


void xx(split)(xint *x, xint *y, xint a, long b) {
  xint c,d;
  longueur la = Lgx(a);
  longueur lc = max(la-b/HW,0);
  longueur ld = min(la,(b+HW-1)/HW);

  if (b < 0)  failwith("negative split index");
  if (x == y) failwith("remainder and quotient share the same memory");
  Enlarge(c,x,lc);
  Enlarge(d,y,ld);
  xz(split)(&(a->e),b,&(c->e),&(d->e));
  if (c != *x) {xx(free)(x); *x = c;}
  if (d != *y) {xx(free)(y); *y = d;}
}

void xx(join)(xint *x, xint a, xint b, long c) {
  xint d;
  longueur la = Lgx(a), lb = Lgx(b), ld;

  if (c < 0) failwith("negative join index");
  ld = max(la,lb+c/HW+1)+1;
  Enlarge(d,x,ld);
  xz(join)(&(a->e),&(b->e),c,&(d->e));
  if (d != *x) {xx(free)(x); *x = d;}
}

                           /* +------------------+
                              |  Multiplication  |
                              +------------------+ */

void xx(mul_1)(xint *x, xint a, long b) {
  xint c;
  longueur la = Lgx(a);

  Enlarge(c,x,la+2);
  xz(mul_2)(&(a->e), b, &(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}    

void xx(mul)(xint *x, xint a, xint b) {
  xint c;
  longueur la = Lgx(a), lb = Lgx(b);
  Enlarge(c,x,la+lb+1);
  if ((la < klim) && (lb < klim)) xz(mul_n2)(&(a->e),&(b->e),&(c->e));
  else                            xz(mul_k) (&(a->e),&(b->e),&(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}


                                /* +---------+
                                   |  Carré  |
                                   +---------+ */

void xx(sqr)(xint *x, xint a) {
  xint c;
  longueur la = Lgx(a);
  Enlarge(c,x,2*la);
  xz(sqr_k)(&(a->e),&(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}

  
                              /* +------------+
                                 |  Division  |
                                 +------------+ */


long xx(quomod_1)(xint *x, xint a, long b) {
  xint     q;
  zdouble  r;
  longueur la = Lgx(a);

  Enlarge(q,x,max(la,2));
  r = xz(quo_2)(&(a->e),b,&(q->e));
  if (q != *x) {xx(free)(x); *x = q;}
  return(r);
}


void xx(quomod)(xint *x, xint *y, xint a, xint b) {
  xint     q, r;
  longueur la = Lgx(a), lb = Lgx(b), lq,lr;

  if (x == y)  failwith("remainder and quotient share the same memory");
  lq = (lb > 2) ? max(la-lb+1,1) : max(la,1);
  lr = max(la+1,lb);
  Enlarge(q,x,lq);
  Enlarge(r,y,lr);
  if (la < 2*klim) xz(quo_n2)(&(a->e),&(b->e),&(q->e),&(r->e));
  else             xz(quo_k) (&(a->e),&(b->e),&(q->e),&(r->e));
  if (q != *x) {xx(free)(x); *x = q;}
  if (r != *y) {xx(free)(y); *y = r;}
}

void xx(quo)(xint *x, xint a, xint b) {
  xint     q;
  longueur la = Lgx(a), lb = Lgx(b), lq;

  lq = (lb > 2) ? max(la-lb+1,1) : max(la,1);
  Enlarge(q,x,lq);
  if (la < 2*klim) xz(quo_n2)(&(a->e),&(b->e),&(q->e),NULL);
  else             xz(quo_k) (&(a->e),&(b->e),&(q->e),NULL);
  if (q != *x) {xx(free)(x); *x = q;}
}



                            /* +-----------------+
                               |  Racine carrée  |
                               +-----------------+ */

void xx(sqrt)(xint *x, xint a) {
  xint c;
  longueur la = Lgx(a), lc=(la+1)/2;

  Enlarge(c,x,lc+1);
  xz(sqrt_k)(&(a->e),&(c->e));
  if (c != *x) {xx(free)(x); *x = c;}
}

                        /* +---------------+
                           |  Racine pème  |
                           +---------------+ */

/* itération de Newton */
void xx(root_newton)(xint *x, xint a, long p) {
  xint y  = xx(new)();
  xint z  = xx(new)();
  xint t  = xx(new)();

  while(1) {
    xx(pow)(&y,*x,p-1);
    xx(mul)(&z,y,*x);
    xx(sub)(&z,a,z);
    if ((Lgx(z)==0) || Sgx(z) == Sgx(a)) break;
    xx(mul_1)(&y,y,p);
    if (Sgx(a)) {xx(add)(&z,z,y); xx(sub_1)(&z,z,1);}
    xx(quo)(&t,z,y);
    xx(add)(x,*x,t);
  }

  if ((Sgx(a)) && (Lgx(z))) xx(sub_1)(x,*x,1);

  xx(free)(&y);
  xx(free)(&z);
  xx(free)(&t);
}

/* racine récursive */
void xx(root_rec)(xint *x, xint a, long p) {
  longueur n = Lgx(a), s = Sgx(a);
  longueur q = n/(2*p);

  /* valeur initiale : puissance de 2 si a est petit, */
  /* sinon racine p-ème de la partie haute            */
  if (q < 2) {
    q = (xx(nbits)(a)+p-1)/p;
    (*x)->e.hd = 1 | s;
    (*x)->e.val[0] = 1;
    xx(shl)(x,*x,q);
  } else {
    xint b = xx(new)();
    xx(shr)(&b,a,p*q*HW);
    xx(root_rec)(x,b,p);
    xx(free)(&b);
    if (s) xx(sub_1)(x,*x,1); else xx(add_1)(x,*x,1);
    xx(shl)(x,*x,q*HW);
  }

  xx(root_newton)(x,a,p);

}

/* racine pème */
void xx(root)(xint *x, xint a, long p) {
  longueur la = Lgx(a);

  if (p <= 0)                   failwith("invalid root exponent");
  if ((Sgx(a)) && ((p&1) == 0)) failwith("2pth-root of a negative number");
  if (la) {
    xint r = xx(new)();

    if (a == *x) {Enlarge(r, &r, (la+p-1)/p+1);}
    else         {Enlarge(r, x,  (la+p-1)/p+1);}
    xx(root_rec)(&r,a,p);

    if (r != *x) {xx(free)(x); *x=r;}
  } else {(*x)->e.hd = 0;}
}


                           /* +--------+
                              |  PGCD  |
                              +--------+ */


void xx(cfrac)(xint *r, xint *s, xint *x, xint *y, xint *z, xint a, xint b) {
  xint p,q,u,v,d;
  longueur la = Lgx(a), lb = Lgx(b), l = max(la,lb)+2;

  if ((r == s) || (r == x) || (r == y) || (r == z) ||
                  (s == x) || (s == y) || (s == z) ||
                              (x == y) || (x == z) ||
                                          (x == z))
    failwith("result sharing with cfrac");
  Enlarge(p,r,l);
  Enlarge(q,s,l);
  Enlarge(u,x,l);
  Enlarge(v,y,l);
  Enlarge(d,z,l);
  xz(cfrac_k)(&(a->e),&(b->e),&(p->e),&(q->e),&(u->e),&(v->e),&(d->e));
  if (p != *r) {xx(free)(r); *r = p;}
  if (q != *s) {xx(free)(s); *s = q;}
  if (u != *x) {xx(free)(x); *x = u;}
  if (v != *y) {xx(free)(y); *y = v;}
  if (d != *z) {xx(free)(z); *z = d;}
}

void xx(gcd_ex)(xint *x, xint *y, xint *z, xint a, xint b) {
  xint u,v,d;
  longueur la = Lgx(a), lb = Lgx(b), l = max(la,lb)+2;

  if ((x == y) || (x == z) || (x == z))
    failwith("result sharing with gcd_ex");
  Enlarge(u,x,l);
  Enlarge(v,y,l);
  Enlarge(d,z,l);
  xz(cfrac_k)(&(a->e),&(b->e),NULL,NULL,&(u->e),&(v->e),&(d->e));
  if (u != *x) {xx(free)(x); *x = u;}
  if (v != *y) {xx(free)(y); *y = v;}
  if (d != *z) {xx(free)(z); *z = d;}
}

void xx(gcd)(xint *z, xint a, xint b) {
  xint d;
  longueur la = Lgx(a), lb = Lgx(b), l = max(la,lb)+2;

  Enlarge(d,z,l);
  xz(cfrac_k)(&(a->e),&(b->e),NULL,NULL,NULL,NULL,&(d->e));
  if (d != *z) {xx(free)(z); *z = d;}
}



                /* +-------------------------------+
                   |  Exponentiation, Factorielle  |
                   +-------------------------------+ */

void xx(pow)(xint *x, xint a, long b) {
  xint c;
  longueur lc;

  if (b < 0) failwith("negative exponent");
  lc = xz(size_pow_k)(&(a->e),b);
  if (lc < 0) failwith("create too big a number");
  Enlarge(c,x,lc+1);
  xz(pow_k)(&(a->e),b,&(c->e),lc);
  if (c != *x) {xx(free)(x); *x = c;}
}

void xx(powmod)(xint *x, xint a, xint b, xint c) {
  xint r;

  if (Sgx(b) <  0) failwith("negative exponent");
  if (Lgx(c) == 0) failwith("division by zero");
  Enlarge(r,x,Lgx(c));
  xz(powmod)(&(a->e),&(b->e),&(c->e),&(r->e));
  if (r != *x) {xx(free)(x); *x = r;}
}

void xx(fact)(xint *x, long a) {
  xint b;
  longueur lb = xz(size_fact_k)(a);

  if (lb < 0) failwith("create too big a number");
  Enlarge(b,x,lb+1);
  xz(fact_k)(a,&(b->e),lb);
  if (b != *x) {xx(free)(x); *x = b;}
}

   
 
