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

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

#include <string.h>
#include <stdio.h>
#include <ctype.h>

#ifdef use_camllight

/* Camllight */
#include <mlvalues.h>
#include <alloc.h>
#include <memory.h>
#define add_fail

#else

/* Ocaml toutes versions */
#include <caml/mlvalues.h>
#include <caml/alloc.h>
#include <caml/memory.h>

#if OCAML_VERSION >= 202
#include <caml/fail.h>
#else
#define add_fail
#endif
#endif /* #ifdef use_camllight */

#ifdef add_fail
#ifdef __GNUC__
/* Works only in GCC 2.5 and later */
#define Noreturn __attribute ((noreturn))
#else
#define Noreturn
#endif

void failwith (char *) Noreturn;
#endif /* #ifdef add_fail */

#include "ml-alloc.h"
#include "ml-long_int.h"

  
             /* +-------------------------------------+
                |  Custom operations (Ocaml >= 3.00)  |
                +-------------------------------------+ */


#ifndef use_camllight
#if OCAML_VERSION >= 300

#include <caml/custom.h>

int xx(compare)(value a, value b) {return(xz(cmp)(Ent(a),Ent(b)));}

/* hashage inspiré de ocaml/byterun/hash.c */
long xx(hash)(value a) {
  entier *aa = Ent(a);
  unsigned long accu = aa->hd;
  longueur l = Lg(aa), i;
  for (i=0; i<l; accu = accu*65599 + (chiffre)aa->val[i++]);
  return(accu);
}

/* sérialisation : signe, longueur en chiffres de 16 bits, chiffres poids faible en tête */
extern void   serialize_int_1(int i);
extern void   serialize_int_2(int i);
extern void   serialize_int_4(int32 i);
extern int    deserialize_uint_1(void);
extern int    deserialize_uint_2(void);
extern uint32 deserialize_uint_4(void);

void xx(serialize)(value x, unsigned long *w32, unsigned long *w64) {
  entier  *a = Ent(x);
  chiffre *b,c=0;
  longueur l = Lg(a), lw, i;

  /* signe */
  serialize_int_1((Signe(a)) ? -1 : 0);

  /* longueur en nombre de mots de 16 bits */
  lw = l*HW;
  if (lw) for (lw-=HW, c=a->val[l-1]; c; c>>=1, lw++);
  lw = (lw + 15)/16;
#ifdef bits_64
  if (lw >= 0x10000000) failwith("number too big for serialization");
#endif
  serialize_int_4(lw);

  /* liste des chiffres par tranche de 16 bits */
  for (i=0, b=a->val; i<lw; i++) {
    if ((i%(HW/16)) == 0) c = *(b++);
    serialize_int_2(c&0xffff);
    c >>= 16;
  }

  /* nombre d'octets émis */
  *w32 = *w64 = 2*lw + 5;
}

unsigned long xx(deserialize)(void *x) {
  entier *a = (entier *)x;
  chiffre *b,c=0;
  longueur i,lw,s;
  int count;

  /* signe */
  s = (deserialize_uint_1()) ? SIGN_m : 0;

  /* longueur en nombre de mots de 16 bits */
  lw = deserialize_uint_4();

  /* constitue l'en-tête */
  a->hd = ((lw + HW/16 - 1)/(HW/16)) | s;

  /* récupère les chiffres par tranches de 16 bits */
  for (i=0, count=0, b=a->val; i<lw; i++) {
    c += deserialize_uint_2() << count;
    count += 16;
    if (count == HW) {*(b++) = c; c = 0; count = 0;}
  }
  if (count) *b = c;

  /* nombre d'octets lus */
  return(2*lw + 5);
}

/* Enregistrement des méthodes */
struct custom_operations xx(ops) = {
#ifdef use_slong
  "Numerix Slong Integer 0.19",/* identifier  */
#else
#ifdef use_dlong
  "Numerix Dlong Integer 0.19",/* identifier  */
#else
  "Numerix Clong Integer 0.19",/* identifier  */
#endif
#endif
  custom_finalize_default,     /* finalize    */
  xx(compare),                 /* compare     */
  xx(hash),                    /* hash        */
  xx(serialize),               /* serialise   */
  xx(deserialize)              /* deserialise */
};

void xx(register)() {
  static int unregistered = 1;
  if (unregistered) {
    register_custom_operations(&xx(ops));
    unregistered = 0;
  }
}
#endif
#endif



             /* +------------------------------------+
                |  Gestion des opérations sur place  |
                +------------------------------------+ */

/*
  copy_in(x,b) : x <- b
  copy_out(a)  : a     
*/

void xx(copy_in)(value x, value b) {
  longueur l = Lg(Ent(b));
  Enlarge_1_1(x,l,b);
  memmove(Cont(x), Ent(b), l*sizeof(chiffre)+sizeof(longueur));
}

value xx(copy_out)(value a) {
  longueur la = Lg(Cont(a));
  value b;
  Alloc_1_1(b,la,a);
  memcpy(Ent(b), Cont(a), la*sizeof(chiffre)+sizeof(longueur));
  return(b);
}

             /* +-------------------------------------+
                |  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|
  of_int(a)     : a
  int_of(a)     : a
*/

value xx(nbits)(value a) {
  chiffre  x;
  longueur l = Lg(Ent(a));
  if (l) for (l--, x = Ent(a)->val[l], l *= HW; (x); x >>= 1, l++);
  return(Val_long(l));
}

value xx(lowbits)(value a) {
  longueur la = Lg(Ent(a));
  ndouble  x = (la == 0) ? 0
             : (la == 1) ? Ent(a)->val[0]
             : ((ndouble)Ent(a)->val[1] << HW) + (ndouble)Ent(a)->val[0];
  return(Val_long(x & 0x7fffffff));
}

value xx(highbits)(value a) {
  longueur la = Lg(Ent(a));
  ndouble  x,y;

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

  x = (ndouble)Ent(a)->val[la-1] << HW;
  if (la >= 2) x += (ndouble)Ent(a)->val[la-2];
  y = (la >= 3) ? Ent(a)->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(Val_long(x));
}

value xx(nth_word)(value a, value n) {
  longueur la = Lg(Ent(a));
  longueur nn = Long_val(n), n1,n2;

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

value xx(of_int)(value a) {
  value c;
  zdouble  aa = Long_val(a);
  longueur sa = (aa < 0) ? SIGN_m : 0;
  if (sa) aa = -aa;

  if (aa == 0)        {Alloc(c,0); Ent(c)->hd = 0;}
  else if (aa < BASE) {Alloc(c,1); Ent(c)->hd = 1|sa; Ent(c)->val[0] = aa;}
  else                {Alloc(c,2); Ent(c)->hd = 2|sa; Ent(c)->val[0] = aa; Ent(c)->val[1] = aa >> HW;}

  return(c);
}

value xx(int_of)(value a) {
  longueur la = Lg(Ent(a));
  ndouble aa = (la == 0) ? 0
             : (la == 1) ? Ent(a)->val[0]
             : ((ndouble)Ent(a)->val[1] << HW) + (ndouble)Ent(a)->val[0];
  if ((la > 2) | (aa > 0x7FFFFFFF)) failwith ("integer overflow");
  return(Val_long((Signe(Ent(a))) ? -aa : aa));
}


                            /* +----------------+
                               |  Comparaisons  |
                               +----------------+ */

value xx(eq)   (value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) == 0) ? Val_true  : Val_false);}
value xx(neq)  (value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) != 0) ? Val_true  : Val_false);}
value xx(inf)  (value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) <  0) ? Val_true  : Val_false);}
value xx(infeq)(value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) <= 0) ? Val_true  : Val_false);}
value xx(sup)  (value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) >  0) ? Val_true  : Val_false);}
value xx(supeq)(value a, value b) {return((xz(cmp)(Ent(a),Ent(b)) >= 0) ? Val_true  : Val_false);}
value xx(cmp)  (value a, value b) {return(Val_long(xz(cmp)(Ent(a),Ent(b))));}
value xx(sgn)  (value a)          {return(Val_long((Lg(Ent(a))) ? ((Signe(Ent(a))) ? -1 : 1) : 0));}


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

value xx(abs)(value a) {
  longueur la = Lg(Ent(a));
  value c;
  Alloc_1_1(c,la,a);
  memcpy(Ent(c)->val,Ent(a)->val,la*sizeof(chiffre));
  Ent(c)->hd = la;
  return(c);
}

void xx(abs_in)(value x, value a) {
  longueur la = Lg(Ent(a));
  Enlarge_1_1(x,la,a);
  if (Cval(x) != a) memcpy(Cont(x)->val,Ent(a)->val,la*sizeof(chiffre));
  Cont(x)->hd = la;
}

value xx(neg)(value a) {
  longueur la = Lg(Ent(a));
  value c;
  Alloc_1_1(c,la,a);
  memcpy(Ent(c)->val,Ent(a)->val,la*sizeof(chiffre));
  Ent(c)->hd = (la) ? (la | (Signe(Ent(a))^SIGN_m)) : 0;
  return(c);
}

void xx(neg_in)(value x, value a) {
  longueur la = Lg(Ent(a));
  Enlarge_1_1(x,la,a);
  if (Cval(x) != a) memcpy(Cont(x)->val,Ent(a)->val,la*sizeof(chiffre));
  Cont(x)->hd = (la) ? (la | (Signe(Ent(a))^SIGN_m)) : 0;
}


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

value xx(add)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Alloc_1_2(c,max(la,lb)+1,a,b);
  xz(add)(Ent(a),Ent(b),Ent(c));
  return(c);
}

value xx(sub)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Alloc_1_2(c,max(la,lb)+1,a,b);
  xz(sub)(Ent(a),Ent(b),Ent(c));
  return(c);
}

void xx(add_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Enlarge_1_2(x,max(la,lb)+1,a,b);
  xz(add)(Ent(a),Ent(b),Cont(x));
}

void xx(sub_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Enlarge_1_2(x,max(la,lb)+1,a,b);
  xz(sub)(Ent(a),Ent(b),Cont(x));
}


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


value xx(shl)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a)), lc;
  longueur bb = Long_val(b);

  lc = (bb >= 0) ? la + bb/HW + 1 : la - (-bb/HW);
  if (lc > 0) {
    Alloc_1_1(c,lc,a);
    xz(shift)(Ent(a), bb, Ent(c));
  } else {
    Alloc(c,0);
    Ent(c)->hd = 0;
  }
  return(c);
}

value xx(shr)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a)), lc;
  longueur bb = -Long_val(b);

  lc = (bb >= 0) ? la + bb/HW + 1 : la - (-bb/HW);
  if (lc > 0) {
    Alloc_1_1(c,lc,a);
    xz(shift)(Ent(a), bb, Ent(c));
  } else {
    Alloc(c,0);
    Ent(c)->hd = 0;
  }
  return(c);
}

void xx(shl_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lc;
  longueur bb = Long_val(b);

  lc = (bb >= 0) ? la + bb/HW + 1 : la - (-bb/HW);
  if (lc > 0) {
    Enlarge_1_1(x,lc,a);
    xz(shift)(Ent(a), bb, Cont(x));
  } else {
    Cont(x)->hd = 0;
  }
}

void xx(shr_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lc;
  longueur bb = -Long_val(b);

  lc = (bb >= 0) ? la + bb/HW + 1 : la - (-bb/HW);
  if (lc > 0) {
    Enlarge_1_1(x,lc,a);
    xz(shift)(Ent(a), bb, Cont(x));
  } else {
    Cont(x)->hd = 0;
  }
}


value xx(split)(value a, value b) {
  value c,d;
  longueur la = Lg(Ent(a));
  longueur bb = Long_val(b);
  longueur lc = max(la-bb/HW+1,0);
  longueur ld = min(la,bb/HW+1);
  value res;

  if (bb < 0) failwith("negative split index");
  AllocN_2_1(res,2,c,lc,d,ld,a);
  xz(split)(Ent(a),bb,Ent(c),Ent(d));
  Field(res,0) = c;
  Field(res,1) = d;
  return(res);
}

void xx(split_in)(value x, value y, value a, value b) {
  longueur la = Lg(Ent(a));
  longueur bb = Long_val(b);
  longueur lc = max(la-bb/HW,0);
  longueur ld = min(la,(bb+HW-1)/HW);

  if (bb < 0) failwith("negative split index");
  if (x == y)  failwith("remainder and quotient share the same memory");
  Enlarge_2_1(x,lc,y,ld,a);
  xz(split)(Ent(a),bb,Cont(x),Cont(y));
}

value xx(join)(value a, value b, value c) {
  long     cc = Long_val(c);
  value    d;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), ld;

  if (cc < 0) failwith("negative join index");
  ld = max(la,lb+cc/HW+1)+1;
  Alloc_1_2(d,ld,a,b);
  xz(join)(Ent(a),Ent(b),cc,Ent(d));
  return(d);
}

void xx(join_in)(value x, value a, value b, value c) {
  long     cc = Long_val(c);
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), ld;

  if (cc < 0) failwith("negative join index");
  ld = max(la,lb+cc/HW+1)+1;
  Enlarge_1_2(x,ld,a,b);
  xz(join)(Ent(a),Ent(b),cc,Cont(x));
}

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

value xx(mul_1)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a));
  long bb = Long_val(b);

  Alloc_1_1(c,la+2,a);
  xz(mul_2)(Ent(a), bb, Ent(c));
  return(c);
}    

void xx(mul_1_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a));
  long bb = Long_val(b);

  Enlarge_1_1(x,la+2,a);
  xz(mul_2)(Ent(a), bb, Cont(x));
}    

value xx(mul_k)(value a, value b) {
  value c;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Alloc_1_2(c,la+lb+1,a,b);
  if ((la < klim) && (lb < klim)) xz(mul_n2)(Ent(a),Ent(b),Ent(c)); else xz(mul_k)(Ent(a),Ent(b),Ent(c));
  return(c);
}

void xx(mul_k_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b));
  Enlarge_1_2(x,la+lb+1,a,b);
  if ((la < klim) && (lb < klim)) xz(mul_n2)(Ent(a),Ent(b),Cont(x)); else xz(mul_k)(Ent(a),Ent(b),Cont(x));
}


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

value xx(sqr_k)(value a) {
  value c;
  longueur la = Lg(Ent(a));

  Alloc_1_1(c,2*la,a);
  xz(sqr_k)(Ent(a),Ent(c));
  return(c);
}

void xx(sqr_k_in)(value x, value a) {
  longueur la = Lg(Ent(a));

  Enlarge_1_1(x,2*la,a);
  xz(sqr_k)(Ent(a),Cont(x));
}

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


value xx(quo_1)(value a, value b) {
  value    q;
  value    res;
  zdouble  bb = Long_val(b);
  zdouble  r;
  longueur la = Lg(Ent(a));

  AllocN_1_1(res,2,q,max(la,2),a);
  r = xz(quo_2)(Ent(a),bb,Ent(q));
  Field(res,0) = q;
  Field(res,1) = Val_long(r);
  return(res);
}

value xx(quo_1_in)(value x,value a, value b) {
  zdouble  bb = Long_val(b);
  zdouble  r;
  longueur la = Lg(Ent(a));

  Enlarge_1_1(x,max(la,2),a);
  r = xz(quo_2)(Ent(a),bb,Cont(x));
  return(Val_long(r));
}

value xx(quo_k)(value a, value b) {
  value    q,r;
  value    res;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), lq, lr;

  lq = (lb > 2) ? max(la-lb+1,1) : max(la,1);
  lr = max(la+1,lb);
  AllocN_2_2(res,2,q,lq,r,max(la+1,lb),a,b);
  if (la < 2*klim) xz(quo_n2)(Ent(a),Ent(b),Ent(q),Ent(r)); else xz(quo_k)(Ent(a),Ent(b),Ent(q),Ent(r));
  Field(res,0) = q;
  Field(res,1) = r;
  return(res);
}


void xx(quo_k_in)(value x, value y, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(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_2_2(x,lq,y,lr,a,b);
  if (la < 2*klim) xz(quo_n2)(Ent(a),Ent(b),Cont(x),Cont(y)); else xz(quo_k)(Ent(a),Ent(b),Cont(x),Cont(y));
}

value xx(quo_knr)(value a, value b) {
  value q;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), lq;

  lq = (lb > 2) ? max(la-lb+1,1) : max(la,1);
  Alloc_1_2(q,lq,a,b);
  if (la < 2*klim) xz(quo_n2)(Ent(a),Ent(b),Ent(q),NULL); else xz(quo_k)(Ent(a),Ent(b),Ent(q),NULL);
  return(q);
}


void xx(quo_knr_in)(value x, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), lq;

  lq = (lb > 2) ? max(la-lb+1,1) : max(la,1);
  Enlarge_1_2(x,lq,a,b);
  if (la < 2*klim) xz(quo_n2)(Ent(a),Ent(b),Cont(x),NULL); else xz(quo_k)(Ent(a),Ent(b),Cont(x),NULL);
}

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

value xx(sqrt_k)(value a) {
  value c;
  longueur la = Lg(Ent(a)), lc=(la+1)/2;

  Alloc_1_1(c,lc+1,a);
  xz(sqrt_k)(Ent(a),Ent(c));
  return(c);
}

void xx(sqrt_k_in)(value x, value a) {
  longueur la = Lg(Ent(a)), lc=(la+1)/2;

  Enlarge_1_1(x,lc+1,a);
  xz(sqrt_k)(Ent(a),Cont(x));
}

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

value xx(cfrac)(value a, value b) {
  value p,q,u,v,d;
  value res;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), l = max(la,lb)+2;

  AllocN_5_2(res,5,p,l,q,l,u,l,v,l,d,l,a,b);
  Field(res,0) = p;
  Field(res,1) = q;
  Field(res,2) = u;
  Field(res,3) = v;
  Field(res,4) = d;
  xz(cfrac_k)(Ent(a),Ent(b),Ent(p),Ent(q),Ent(u),Ent(v),Ent(d));
  return(res);
}

value xx(gcd_ex)(value a, value b) {
  value  u,v,d;
  value res;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), l = max(la,lb)+2;

  AllocN_3_2(res,3,u,l,v,l,d,l,a,b);
  Field(res,0) = u;
  Field(res,1) = v;
  Field(res,2) = d;
  xz(cfrac_k)(Ent(a),Ent(b),NULL,NULL,Ent(u),Ent(v),Ent(d));
  return(res);
}

value xx(gcd)(value a, value b) {
  value d;
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), l = max(la,lb)+2;

  Alloc_1_2(d,l,a,b);
  xz(cfrac_k)(Ent(a),Ent(b),NULL,NULL,NULL,NULL,Ent(d));
  return(d);
}

void xx(cfrac_in)(value r, value s, value x, value y, value z, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(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_in");
  Enlarge_5_2(r,l,s,l,x,l,y,l,z,l,a,b);
  xz(cfrac_k)(Ent(a),Ent(b),Cont(r),Cont(s),Cont(x),Cont(y),Cont(z));
}

void xx(gcd_ex_in)(value x, value y, value z, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), l = max(la,lb)+2;

  if ((x == y) || (x == z) || (x == z))
    failwith("result sharing with gcd_ex_in");
  Enlarge_3_2(x,l,y,l,z,l,a,b);
  xz(cfrac_k)(Ent(a),Ent(b),NULL,NULL,Cont(x),Cont(y),Cont(z));
}

void xx(gcd_in)(value z, value a, value b) {
  longueur la = Lg(Ent(a)), lb = Lg(Ent(b)), l = max(la,lb)+2;

  Enlarge_1_2(z,l,a,b);
  xz(cfrac_k)(Ent(a),Ent(b),NULL,NULL,NULL,NULL,Cont(z));
}

void xx(cfrac_in_bytecode)(value *argv, int argn) {
  xx(cfrac_in)(argv[0],argv[1],argv[2],argv[3],argv[4],argv[5],argv[6]);
}

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

value xx(pow_k)(value a, value b) {
  value c;
  longueur lc;
  long bb = Long_val(b);

  if (bb < 0) failwith("negative exponent");
  lc = xz(size_pow_k)(Ent(a),bb);
  if (lc < 0) failwith("create too big a number");
  Alloc_1_1(c,lc+1,a);
  xz(pow_k)(Ent(a),bb,Ent(c),lc);
  return(c);
}

void xx(pow_k_in)(value x, value a, value b) {
  longueur lc;
  long bb = Long_val(b);

  if (bb < 0) failwith("negative exponent");
  lc = xz(size_pow_k)(Ent(a),bb);
  if (lc < 0) failwith("create too big a number");
  Enlarge_1_1(x,lc+1,a);
  xz(pow_k)(Ent(a),bb,Cont(x),lc);
}

value xx(powmod)(value a, value b, value c) {
  value r;

  if (Signe(Ent(b)))   failwith("negative exponent");
  if (Lg(Ent(c)) == 0) failwith("division by zero");
  Alloc_1_3(r,Lg(Ent(c)),a,b,c);
  xz(powmod)(Ent(a),Ent(b),Ent(c),Ent(r));
  return(r);
}

void xx(powmod_in)(value x, value a, value b, value c) {

  if (Signe(Ent(b)) < 0) failwith("negative exponent");
  if (Lg(Ent(c)) == 0)   failwith("division by zero");
  Enlarge_1_3(x,Lg(Ent(c)),a,b,c);
  xz(powmod)(Ent(a),Ent(b),Ent(c),Cont(x));
}

value xx(fact_k)(value a) {
  value b;
  long aa = Long_val(a);
  longueur lb = xz(size_fact_k)(aa);

  if (lb < 0) failwith("create too big a number");
  Alloc(b,lb+1);
  xz(fact_k)(aa,Ent(b),lb);
  return(b);
}

void xx(fact_k_in)(value x, value a) {
  long aa = Long_val(a);
  longueur lb = xz(size_fact_k)(aa);

  if (lb < 0) failwith("create too big a number");
  Enlarge(x,lb+1);
  xz(fact_k)(aa,Cont(x),lb);
}

   

                   /* +------------------------+
                      |  Conversion en chaine  |
                      +------------------------+ */

value xx(of_string)(value s) {
  char *ss = (char *)s;
  value a;
  longueur la = xz(size_of_string)(ss);
  Alloc_1_1(a,la,s);
  xz(of_string)(ss,Ent(a));
  return(a);
}

value xx(string_of)(value a) {
  char *s = xz(string_of)(Ent(a));
  value res = copy_string(s);
  free(s);
  return(res);
}

