/*
   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                |
   |                                                                        |
   |                                    Version C                           |
   |                                                                        |
   +------------------------------------------------------------------------+ */

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

/* ne pas lire ce fichier deux fois */
#ifndef __long_int__
#define __long_int__

/* options de compilation :

   bits_32 => architecture 32 bits
   bits_64 => architecture 64 bits

   use_clong => arithmétique en C, un chiffre = 1/2 mot
   use_dlong => arithmétique en C, un chiffre = 1 mot
   use_slong => arithmétique en C et assembleur, un chiffre = 1 mot
*/

/* slong => dlong */
#ifdef use_slong
#ifndef use_dlong
#define use_dlong
#endif
#endif

/* par défaut clong */
#ifndef use_dlong
#ifndef use_clong
#define use_clong
#endif
#endif

/* architecture 64 bits */
#ifdef bits_64

#ifdef use_slong
#define HW       64                   /* longueur chiffre       */          
#define BASE     0x10000000000000000LL/* base de numération     */          
#define SIGN_m   0x8000000000000000L  /* masque bit de signe    */
#define LONG_m   0x7fffffffffffffffL  /* masque bits de longueur*/
#define chiffre  unsigned long        /* un chiffre             */          
#define ndouble  unsigned long long   /* grand entier non signé */          
#define zdouble           long long   /* grand entier signe     */          
#define longueur          long        /* longueur des tableaux  */          
#define Digits_in_chiffre 19          /* nb chiffres dans un mot*/
#define Pow_ten_max 10000000000000000000 /* 10^Digits_in_chiffre*/
#define klim     32                   /* limite de Karatsuba    */          
#define kprof    63                   /* prof maxi de récursion */          
#define xn(nom)  sn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  sz_##nom 

#else
#ifdef use_dlong
#define HW       64                   /* longueur chiffre       */          
#define BASE     0x10000000000000000LL/* base de numération     */          
#define SIGN_m   0x8000000000000000L  /* masque bit de signe    */
#define LONG_m   0x7fffffffffffffffL  /* masque bits de longueur*/
#define chiffre  unsigned long        /* un chiffre             */          
#define ndouble  unsigned long long   /* grand entier non signé */          
#define zdouble           long long   /* grand entier signe     */          
#define longueur          long        /* longueur des tableaux  */          
#define Digits_in_chiffre 19          /* nb chiffres dans un mot*/
#define Pow_ten_max 10000000000000000000 /* 10^Digits_in_chiffre*/
#define klim     32                   /* limite de Karatsuba    */          
#define kprof    63                   /* prof maxi de récursion */          
#define xn(nom)  dn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  dz_##nom 

#else
#define HW       32                   /* longueur chiffre       */
#define BASE     0x100000000L         /* base de numération     */
#define SIGN_m   0x8000000000000000L  /* masque bit de signe    */
#define LONG_m   0x7fffffffffffffffL  /* masque bits de longueur*/
#define chiffre  unsigned int         /* un chiffre             */
#define ndouble  unsigned long        /* grand entier non signé */
#define zdouble           long        /* grand entier signe     */
#define longueur          long        /* longueur des tableaux  */
#define Digits_in_chiffre 9           /* nb chiffres dans un mot*/
#define Pow_ten_max 1000000000        /* 10^Digits_in_chiffre   */
#define klim     32                   /* limite de Karatsuba    */
#define kprof    63                   /* prof maxi de récursion */
#define xn(nom)  cn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  cz_##nom 
#endif
#endif

/* architecture 32 bits */
#else
#ifdef bits_32

#ifdef use_slong
#define HW       32                   /* longueur chiffre       */                          
#define BASE     0x100000000LL        /* base de numération     */          
#define SIGN_m   0x80000000L          /* masque bit de signe    */
#define LONG_m   0x7fffffffL          /* masque bits de longueur*/
#define chiffre  unsigned long        /* un chiffre             */          
#define ndouble  unsigned long long   /* grand entier non signé */          
#define zdouble           long long   /* grand entier signe     */          
#define longueur          long        /* longueur des tableaux  */          
#define Digits_in_chiffre 9           /* nb chiffres dans un mot*/
#define Pow_ten_max 1000000000        /* 10^Digits_in_chiffre   */
#define klim     32                   /* limite de Karatsuba    */          
#define kprof    31                   /* prof maxi de récursion */          
#define xn(nom)  sn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  sz_##nom 

#else
#ifdef use_dlong
#define HW       32                   /* longueur chiffre       */          
#define BASE     0x100000000LL        /* base de numération     */          
#define SIGN_m   0x80000000L          /* masque bit de signe    */
#define LONG_m   0x7fffffffL          /* masque bits de longueur*/
#define chiffre  unsigned long        /* un chiffre             */          
#define ndouble  unsigned long long   /* grand entier non signé */          
#define zdouble           long long   /* grand entier signe     */          
#define longueur          long        /* longueur des tableaux  */          
#define Digits_in_chiffre 9           /* nb chiffres dans un mot*/
#define Pow_ten_max 1000000000        /* 10^Digits_in_chiffre   */
#define klim     32                   /* limite de Karatsuba    */          
#define kprof    31                   /* prof maxi de récursion */          
#define xn(nom)  dn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  dz_##nom 

#else
#define HW       16                   /* longueur chiffre       */          
#define BASE     0x10000L             /* base de numération     */          
#define SIGN_m   0x80000000L          /* masque bit de signe    */
#define LONG_m   0x7fffffffL          /* masque bits de longueur*/
#define chiffre  unsigned short       /* un chiffre             */          
#define ndouble  unsigned long        /* grand entier non signé */          
#define zdouble           long        /* grand entier signe     */          
#define longueur          long        /* longueur des tableaux  */          
#define Digits_in_chiffre 4           /* nb chiffres dans un mot*/
#define Pow_ten_max 10000             /* 10^Digits_in_chiffre   */
#define klim     16                   /* limite de Karatsuba    */          
#define kprof    31                   /* prof maxi de récursion */          
#define xn(nom)  cn_##nom             /* préfixe des fonctions publiques  */
#define xz(nom)  cz_##nom 
#endif
#endif

/* pb: la taille des mots n'est pas connue */
#else
#error "Unknown word size, define bits_64 or bits_32"
#endif
#endif

/*
 * Structures de données :
 *
 * Un entier naturel est représenté par un tableau de chiffres en base
 * BASE dont la longueur, l, est passée en paramètre. Le chiffre de poids
 * faible a l'indice zéro, celui de poids fort a l'indice l-1, il peut
 * être nul. Lorsque l = 0, l'entier est nul. La plus grande longueur
 * admissible est le plus grand entier positif représentable en machine,
 * dans une variable de type "longueur".
 *
 * Un entier relatif est représenté par un mot de tête contenant le signe
 * (bit de poids fort) et la longueur (bits précédents) suivi du tableau
 * de chiffres codant la valeur absolue. Si l est la longueur, le chiffre
 * d'indice l-1 est non nul. L'entier nul est caractérisé par l=0, le bit
 * de signe est ignoré, mais toutes les fonctions xz_yyy décrites ci-dessous
 * retournent des zéros positifs (mot de tête = 0). La plus grande longueur
 * admissible est le 16ème de la longueur autorisée pour les naturels (de
 * sorte que les buffers temporaires créés en cours de calcul aient des
 * longueur admissibles).
 */


typedef chiffre * naturel;
typedef struct {
  longueur hd;      /*  longueur et signe         */
  chiffre  val[1];  /* 1 pour faire plaisir à gcc */
} entier;

/* macros d'accès au signe et à la longueur */
#define Signe(a) (((unsigned longueur)a->hd > (unsigned longueur)SIGN_m) ? SIGN_m : 0)
#define Lg(a)    ((a->hd) & LONG_m)

/* fixe l'entête d'un résultat */
#define make_head(x,l,s) {                                             \
  longueur head_index = (l)-1;                                         \
  while ((head_index >= 0) && (x->val[head_index] == 0)) head_index--; \
  x->hd = (head_index >= 0) ? (head_index+1)|(s) : 0;                  \
}

/* attention, x ou y est évalué deux fois */
#define max(x,y) (((x) > (y)) ? (x) : (y))
#define min(x,y) (((x) < (y)) ? (x) : (y))

/* +------------------------------------------------------------------------+
   |                      opérations sur les naturels                       |
   +------------------------------------------------------------------------+ */

/*
  Les noms des fonctions opérant sur les naturels commencent par le
  préfixe "sn_" (version assembleur), "dn_" (version C long long) ou
  "cn_" (version C standard). La macro xn forme le préfixe approprié en
  fonction des options de compilation use_slong, ...

  cmp(a,la,b,lb):  compare a et b et retourne la longueur maxi de a-b
                    avec le signe + si a >= b, le signe - si a <= b.

  add(a,la,b,lb,c):          c <- a + b                  
  inc_1(a,la,b):             a <- a + b, retourne la retenue 
  inc(a,la,b,lb):            a <- a + b, retourne la retenue 
  sub(a,la,b,lb,c):          c <- a - b
  dec_1(a,la,b):             a <- a - b, retourne la retenue 
  dec(a,la,b,lb):            a <- a - b, retourne la retenue 
         
  shl(a,la,b,c):             c <- a << b
  shr(a,la,b,c):             c <- a >> b
      
  mul_2(a,la,b,c):           c <- a*b
  mul_n2(a,la,b,lb,c):       c <- a*b
  mul_k(a,la,b,lb,c):        c <- a*b
  sqr_n2(a,la,c):            c <- a^2
  sqr_k(a,la,c):             c <- a^2
  quo_2(a,la,b,c):           c <- a div b, retourne a mod b
  quo_n2(a,la,b,lb,c,d):     c <- a div b, d <- a mod b
  quo_k(a,la,b,lb,c,d):      c <- a div b, d <- a mod b
  quo_knr(a,la,b,lb,c):      c <- a div b, retourne (reste != 0)
  hquo(a,la,b,lb,c,lc):      division tronquée (usage interne uniquement)
  sqrt_n2(a,la,c):           c <- trunc(a^1/2)
  sqrt_k(a,la,c):            c <- trunc(a^1/2)

  restrictions et capacité minimale du résultats
  (tous les chiffres correspondants sont correctement affectés,
  non volatil = ne doit pas être confondu avec un résultat)

  cmp:     -
  add:     lc >= max(la,lb)+1
  inc_1:   -
  inc:     -
  sub:     a >= b, lc >= lb
  dec_1:   -
  dec:     la >= lb
  shl:     b >= 0, lc >= la + (b+HW-1)/HW
  shr:     b >= 0, la >= b/HW, lc >= la - b/HW
  mul_2:   lc >= la+2
  mul_n2:  lc >= la + lb, a et b non volatils
  mul_k:   lc >= la + lb, a et b non volatils
  sqr_n2:  lc >= 2*la, a non volatil
  sqr_k:   lc >= 2*la, a non volatil
  quo_2:   b > 0, lc >= la
  quo_n2:  lb >= 3, b[lb-1] > 0, lc >= la-lb+1, ld >= la+1, b non volatil
  quo_k:   lb >= klim, la >= lb+klim, b[lb-1] > 0, lc >= la-lb+1, ld >= la+2, b non volatil
  quo_knr: lb >= klim, la>=lb+klim, b[lb-1] > 0, lc >= la-lb+1, b non volatil
  sqrt_n2: a[la-1] > 0, lc >= (la+1)/2
  sqrt_k:  a[la-1] > 0, lc >= (la+1)/2

*/


longueur xn(cmp)    (naturel a, longueur la, naturel b, longueur lb);
void     xn(add)    (naturel a, longueur la, naturel b, longueur lb, naturel c);
ndouble  xn(inc_1)  (naturel a, longueur la, chiffre b);
ndouble  xn(inc)    (naturel a, longueur la, naturel b, longueur lb);
void     xn(sub)    (naturel a, longueur la, naturel b, longueur lb, naturel c);
zdouble  xn(dec_1)  (naturel a, longueur la, chiffre b);
zdouble  xn(dec)    (naturel a, longueur la, naturel b, longueur lb);
void     xn(shr)    (naturel a, longueur la, longueur compte, naturel c);
void     xn(shl)    (naturel a, longueur la, longueur compte, naturel c);
void     xn(mul_2)  (naturel a, longueur la, ndouble b, naturel c);
void     xn(mul_n2) (naturel a, longueur la, naturel b, longueur lb, naturel c);
void     xn(mul_k)  (naturel a, longueur la, naturel b, longueur lb, naturel c);
void     xn(sqr_n2) (naturel a, longueur la, naturel c);
void     xn(sqr_k)  (naturel a, longueur la,                         naturel c);
ndouble  xn(quo_2)  (naturel a, longueur la, ndouble b, naturel c);
void     xn(quo_k)  (naturel a, longueur la, naturel b, longueur lb, naturel c, naturel d);
void     xn(quo_n2) (naturel a, longueur la, naturel b, longueur lb, naturel c, naturel d);
longueur xn(quo_knr)(naturel a, longueur la, naturel b, longueur lb, naturel c);
longueur xn(hquo)(   naturel a, longueur la, naturel b, longueur lb, naturel c, longueur lc);
void     xn(sqrt_n2)(naturel a, longueur la, naturel c);
void     xn(sqrt_k) (naturel a, longueur la, naturel c);

/* Multiplication par FFT */
void xn(sc_fftmul)(naturel a, longueur la, naturel b, longueur lb, naturel c);
void xn(sc_fftsqr)(naturel a, longueur la, naturel b);

#define cn_fftmul cn_sc_fftmul
#define dn_fftmul dn_sc_fftmul
#define sn_fftmul sn_sc_fftmul
#define cn_fftsqr cn_sc_fftsqr
#define dn_fftsqr dn_sc_fftsqr
#define sn_fftsqr sn_sc_fftsqr

#define flim (30000/HW)



/* +------------------------------------------------------------------------+
   |                   opérations sur les relatifs                          |
   +------------------------------------------------------------------------+ */

/*
  Les noms des fonctions opérant sur les entiers relatifs commencent par le
  préfixe "sz_" (version assembleur), "dz_" (version C long long) ou
  "cz_" (version C standard). La macro xz forme le préfixe approprié en
  fonction des options de compilation use_slong, ...

  cmp(a,b):          -1 si a < b, 0 si a = b, 1 si a > b
  add(a,b,c):        c <- a + b
  sub(a,b,c):        c <- a - b
   
  shift(a,b,c):      c <- a << b (b >= 0) ou a >> -b (b < 0)
  split(a,b,c,d):    c <- a div 2^b, d <- a mod 2^b mêmes signes que a
  join(a,b,c,d):     d <- a + b*2^c
   
  mul_2(a,b,c):      c <- a*b
  mul_n2(a,b,c):     c <- a*b
  mul_k(a,b,c):      c <- a*b
  sqr_k(a,b):        b <- a^2
  quo_2(a,b,c):      c <- a div b, retourne le reste
  quo_n2(a,b,c,d):   c <- a div b, d <- a mod b
  quo_k(a,b,c,d):    c <- a div b, d <- a mod b
  sqrt_k(a,b):       b <- trunc(a^1/2)
  powmod(a,b,c,d):   d <- a^b mod c

  cfrac_k(a,b,p,q,u,v,d) d <- pgcd(a,b),
                     u,v <- coefficients de Bézout : ua-vb = d,
                     p/q <- fraction réduite équivalente à a/b

  size_pow_k(a,b):   majorant de la taille de a^b, -1 si trop grand
  size_fact_k(a):    majorant de la taille de a!, -1 si trop grand
  pow_k(a,b,c,l):    c <- a^b
  fact_k(a,b,l):     b <- a!

  size_of_string(s): majorant de la taille de val(s)
  of_string(s,a):    a <- val(s)
  string_of(a):      convertit a en chaine décimale

  restrictions et capacité minimale des résultats

  cmp:       -
  add:       lc >= max(la,lb)+1
  sub:       lc >= max(la,lb)+1
  add_2:     lc >= la+2
   	     
  shift:     lc >= la + (b+HW-1)/HW (b >= 0)
             lc >= la - (-b)/HW     (b <= 0)
  split:     b >= 0, lc >= la - b/HW, ld >= (b+HW-1)/HW
  join:      c >= 0, ld >= max(la,lb+(c+HW-1)/HW) + 1
   	     
  mul_2:     lc >= la+2
  mul_k:     lc >= la+lb
  sqr_k:     lb >= 2*la
  quo_2:     lc >= la
  quo_n2:    lc >= la-lb+1, ld >= la+2
  quo_k:     lc >= la-lb+1, ld >= la+2
  quo_knr:   lc >= la-lb+1
  sqrt_k:    lb >= (la+1)/2
  powmod:    b >= 0, c <> 0, ld >= lc
  cfrac_k:   lp,lq,lu,lv,ld >= max(la,lb)+2
  	     
  pow_k:     b >= 0, lc >  l = size_pow_k(a,b)
  fact_k:    a >= 0, lb >= l = size_fact_k(a)
	     
  of_string: la >= size_of_string(a)
  string_of: la chaine doit être libérée par free

*/

long xz(cmp)(entier *a, entier *b);

/* addition/soustraction : deux fonctions en assembleur, */
/* une seule avec paramètre supplémentaire en C.         */
#ifdef use_slong
void sz_add(entier *a, entier *b, entier *c);
void sz_sub(entier *a, entier *b, entier *c);
#else
void xz(addsub)(entier *a, entier *b, entier *c, longueur s);
#define cz_add(a,b,c) xz(addsub)(a,b,c,0)
#define dz_add(a,b,c) xz(addsub)(a,b,c,0)
#define cz_sub(a,b,c) xz(addsub)(a,b,c,SIGN_m)
#define dz_sub(a,b,c) xz(addsub)(a,b,c,SIGN_m)
#endif

void     xz(shift)  (entier *a, longueur compte,       entier *c);
void     xz(split)  (entier *a, longueur b,entier *c,  entier *d);
void     xz(join)   (entier *a, entier *b, longueur c, entier *d);
void     xz(mul_2)  (entier *a, zdouble b, entier *c);
void     xz(mul_n2) (entier *a, entier *b, entier *c);
void     xz(mul_k)  (entier *a, entier *b, entier *c);
void     xz(sqr_k)  (entier *a,            entier *c);
zdouble  xz(quo_2)  (entier *a, zdouble b, entier *c);
void     xz(quo_n2) (entier *a, entier *b, entier *c,  entier *d);
void     xz(quo_k)  (entier *a, entier *b, entier *c,  entier *d);
void     xz(sqrt_k) (entier *a,            entier *c);
void     xz(powmod) (entier *a, entier *b, entier *c,  entier *d);

void     xz(cfrac_k)(entier *a, entier *b, entier *p, entier *q, entier *u, entier *v, entier *x);

longueur xz(size_pow_k) (entier *a, unsigned long b);
longueur xz(size_fact_k)(unsigned long a);
void     xz(pow_k) (entier *a, unsigned long b, entier *c, longueur lc);
void     xz(fact_k)(long a, entier *b, longueur lb);


longueur xz(size_of_string)(char *s);
void     xz(of_string)(char *s, entier *a);
char *   xz(string_of)(entier *a);


/* erreur fatale */
void xn(fatal_err)(char *msg);

/* allocation/désallocation */
#include <stdlib.h>
#include <string.h>

naturel xn(alloc_tmp)(longueur l);
void xn(free)(naturel a);
#define cn_free(x) free(x)
#define dn_free(x) free(x)
#define sn_free(x) free(x)
#define cn_cpy(x,y,l)  memcpy(x,y,(l)*sizeof(chiffre))
#define dn_cpy(x,y,l)  memcpy(x,y,(l)*sizeof(chiffre))
#define sn_cpy(x,y,l)  memcpy(x,y,(l)*sizeof(chiffre))
#define cn_move(x,y,l) memmove(x,y,(l)*sizeof(chiffre))
#define dn_move(x,y,l) memmove(x,y,(l)*sizeof(chiffre))
#define sn_move(x,y,l) memmove(x,y,(l)*sizeof(chiffre))
#define cn_clear(x,l)  memset(x,0,(l)*sizeof(chiffre))
#define dn_clear(x,l)  memset(x,0,(l)*sizeof(chiffre))
#define sn_clear(x,l)  memset(x,0,(l)*sizeof(chiffre))

entier *xz(alloc_tmp)(longueur l);
#define cz_free(x) free(x)
#define dz_free(x) free(x)
#define sz_free(x) free(x)
#define cz_cpy(x,y,l)  memcpy( x,y, (l)*sizeof(chiffre) + sizeof(longueur))
#define dz_cpy(x,y,l)  memcpy( x,y, (l)*sizeof(chiffre) + sizeof(longueur))
#define sz_cpy(x,y,l)  memcpy( x,y, (l)*sizeof(chiffre) + sizeof(longueur))
#define cz_move(x,y,l) memmove(x,y, (l)*sizeof(chiffre) + sizeof(longueur))
#define dz_move(x,y,l) memmove(x,y, (l)*sizeof(chiffre) + sizeof(longueur))
#define sz_move(x,y,l) memmove(x,y, (l)*sizeof(chiffre) + sizeof(longueur))

/*
  zmem(n,l)   = taille totale en nb de chiffres pour faire tenir l chiffres
                et n headers en tenant compte des contraintes d'alignement.
  zalign(x,l) = position d'un header bien aligné après le l-ème chiffre de x.
*/
#ifdef use_clong
#define zmem(n,l)   (l + 3*n)
#define zalign(x,l) (entier *)(((x)->val)+(l)+(l)%2)
#else
#define zmem(n,l)   (l + n)
#define zalign(x,l) (entier *)(((x)->val)+(l))
#endif

/* déboguage */
void xn(dump)(naturel a, longueur la);
void xz(dump)(entier *a);

#endif /* ifndef __long_int__ */

