/*
   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                    |
   |                                                                        |
   |                        Exponentiation, Factorielle                     |
   |                                                                        |
   +------------------------------------------------------------------------+ */

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

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

      /* +---------------------------------------------------+
         |  majorant de la taille de a^b, -1 si débordement  |
         +---------------------------------------------------+ */

#ifndef have_sz_size_pow_k
longueur xz(size_pow_k)(entier *a, unsigned long b) {
  longueur la = Lg(a);
  ndouble  n,p;
  chiffre  bigp[4];

       if (b  == 0) {return(1);}
  else if (b  == 1) {return(la);}
  else if (la == 0) {return(0);}
  else if ((la == 1) && (a->val[0] == 1)) {return(1);}

  for (p = (la-1)*HW, n=a->val[la-1]; (n); n >>= 1, p++);
  bigp[0] = p; bigp[1] = p >> HW;
  xn(mul_2)(bigp,2,(ndouble)b,bigp);
  xn(quo_2)(bigp,4,HW,bigp);
  xn(inc_1)(bigp,4,1);

#ifdef use_dlong
  return(((bigp[0] & SIGN_m) || (bigp[1]) || (bigp[2]) || (bigp[3])) ?
	 -1 : bigp[0]);
#else
  return(((bigp[1] & SIGN_m) || (bigp[2]) || (bigp[3])) ?
	 -1 : (ndouble) bigp[0] + ((ndouble)bigp[1] << HW));
#endif
}
#endif

       /* +------------------------------------------------+
          |  c <- a^b, capacité(c) > lc = size_pow_k(a,b)  |
          +------------------------------------------------+ */

#ifndef have_sz_pow_k
void xz(pow_k)(entier *a, unsigned long b, entier *c, longueur lc) {
  longueur la = Lg(a);
  longueur sa = Signe(a);
  unsigned long n;
  chiffre *buff, *buff_save, *aa, *x, *y, *z;

  /* élimine les cas particuliers */
  if (b  == 0)      {c->hd = 1; c->val[0] = 1; return;}
  else if (b  == 1) {xz(move)(c,a,la);         return;}
  else if (la == 0) {c->hd = 0;                return;}
  else if ((la == 1) && (a->val[0] == 1)) {
    c->hd = (b%2) ? 1|sa : 1;
    c->val[0] = 1;
    return;
  }

  /* mémoire de travail */
  n = lc + 1;
  if (a == c) n += la;
  buff_save = xn(alloc_tmp)(n);
  x = c->val;
  y = buff_save;
  buff = y+lc+1;
  if (a == c) {
    xn(cpy)(buff,a->val,la);
    aa = buff;
  } else aa = a->val;
  
  /* puissance dichotomique */
  xn(move)(x,aa,la); lc = la;
  for (n=SIGN_m; (n & b) == 0; n >>= 1);

  for (n >>= 1; (n); n >>= 1) {
    xn(sqr_k)(x,lc,y);
    lc <<= 1; while (y[lc-1] == 0) lc--;
    if (n & b) {
      xn(mul_k)(aa,la,y,lc,x);
      lc += la; while (x[lc-1] == 0) lc--;
    }
    else {z = x; x = y; y = z;}
  }

  /* recopie le résultat si besoin */
  if (x != c->val) {xn(cpy)(c->val,x,lc);}
  c->hd = lc | ((b%2) ? sa : 0);
  xn(free)(buff_save);

}
#endif
