/*
   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                    |
   |                                                                        |
   |                 Conversion  entier <-> chaine décimale                 |
   |                                                                        |
   +------------------------------------------------------------------------+ */

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

#include "long_int.h"
#include "long_int-s.h"
#include <ctype.h>

           /* +-----------------------------------------+
              |  majorant de la taille de of_string(a)  |
              +-----------------------------------------+ */

#ifndef have_sz_size_of_string
longueur xz(size_of_string)(char *s) {
  longueur l;

  /* saute les blancs et le signe */
  while isspace(*s) s++;
  if ((*s == '+') || (*s == '-')) s++;

  /* compte le nombre de chiffres décimaux */
  for (l=0; (isdigit(s[l])); l++);

  /* nb de bits <= l/log(2), 8651/28738 = appr. par défaut de log(2) */
#ifdef bits_64
  /* pas de risque de débordement arithmétique tant que la longueur */
  /* de la chaîne est inférieure à 583 To.                          */
  l = (l*28738 + 8650)/8651;
#else
#ifdef use_dlong
    /* pas de risque de débordement arithmétique tant que la longueur */
    /* de la chaîne est inférieure à 1.2 Go.                           */
  l = (((ndouble)l)*28738 + 8650)/8651;
#else
  {
    /* pas de risque de débordement arithmétique tant que la longueur */
    /* de la chaîne est inférieure à 1233 Mo.                           */
    ndouble l0 = l & (BASE-1), l1 = l >> HW;
    l0 = 28738*l0 + 8650; l1 = 28738*l1 + (l0 >> HW); l0 &= BASE-1;
    l0 = (l0 + ((l1%8651) << HW))/8651; l1 /= 8651;
    l  = (l1 << HW) + l0;
  }
#endif
#endif

  /* convertit en chiffres 32 ou 64 bits */
  return((l+HW-1)/HW);
}
#endif


/* +-----------------------------------------------------------------+
   |  conversion string -> entier, capacité(a) >= size_of_string(s)  |
   +-----------------------------------------------------------------+ */

#ifndef have_sz_of_string
void xz(of_string)(char *s, entier *a) {
  longueur sa = 0;
  chiffre *b, *pow10, *tmp;
  long i,j,l,n;

  /* saute les blancs et le signe */
  while isspace(*s) s++;
  switch(*s) {
  case '-' : sa = SIGN_m;
  case '+' : s++;
  }

  /* alloue la mémoire de travail */
  for (l=0; isdigit(s[l]); l++);
  n = (l + Digits_in_chiffre - 1)/Digits_in_chiffre;
  if (n == 0) {a->hd = 0; return;}
  b = xn(alloc_tmp)(3*n);
  pow10 = b+n;
  tmp   = pow10+n;
  pow10[0] = Pow_ten_max;
  
  /* convertit les caractères par paquets */
  for (i=n-1, b[i] = 0, j = 1 + (l-1)%Digits_in_chiffre; l > 0; s++, l--, j--) {
    if (j==0) {j = Digits_in_chiffre; i--; b[i] = 0;}
    b[i] = b[i] * 10 + s[0] - '0';
  }

  /* regroupe les paquets deux par deux */
  for (l = 1; l < n; l <<= 1) {
    if (l > 1) {
      xn(sqr_k)(pow10,l/2,tmp);
      xn(cpy)(pow10,tmp,l);
    }
    for (i=0; i+2*l <= n; i+=2*l) {
    xn(mul_k)(b+i+l,l,pow10,l,tmp);
    xn(cpy)(b+i+l,tmp+l,l);
    xn(inc)(b+i,2*l,tmp,l);
    }
    if (i+l < n) {
      xn(mul_k)(b+i+l,n-i-l,pow10,l,tmp);
      xn(cpy)(b+i+l,tmp+l,n-i-l);
      xn(inc)(b+i,n-i,tmp,l);
    }
  }

  /* détermine la longueur et recopie le résultat */
  while ((n > 0) && (b[n-1] == 0)) n--;
  xn(cpy)(a->val,b,n);
  a->hd = (n) ? (n|sa) : 0;
  xn(free)(b);
}
#endif

/* +---------------------------------------------------------------------+
   |  conversion entier -> chaine, la chaine doit être libérée par free  |
   +---------------------------------------------------------------------+ */

#ifndef have_sz_string_of
char * xz(string_of)(entier *a) {
  longueur la = Lg(a), sa = Signe(a);
  chiffre *pow10, *digits, *tmp;
  longueur l10[kprof];
  char *s,*ss;
  long i,j,k,l;
  ndouble n,u,p10;

  if (la == 0) {
    ss = (char *)malloc(2);
    if (ss == NULL) xn(fatal_err)("\nstring_of, out of memory\n");
    ss[0] = '0';
    ss[1] = 0;
    return(ss);
  }

  /* alloue la mémoire de travail */
  pow10 = xn(alloc_tmp)(4*la+kprof+1);
  digits = pow10  + 2*la + 1;
  tmp    = digits + la + kprof;

  /* calcule les puissances de 10 jusqu'à dépasser a^1/2 */
  pow10[0] = Pow_ten_max;
  l10[0]   = 1;
  for (i=0; 2*l10[i] <= la; i++) {
    xn(sqr_k)(pow10,l10[i],pow10+l10[i]);
    pow10 += l10[i];
    for (l=2*l10[i]; pow10[l-1] == 0; l--);
    l10[i+1] = l;
  }
  /* si on a dépassé a alors recule d'un cran */
  if ((xn(cmp)(a->val,la,pow10,l10[i]) < 0) && (i)) {
    i--;
    pow10 -= l10[i];
  }

  /* divise a par les puissances calculées */
  xn(quo_k)(a->val,la,pow10,l10[i],tmp,digits);
  k = la - l10[i]+1; while ((k > 0) && (tmp[k-1] == 0)) k--;
  digits += l10[i];
  for (l=1, j=i-1; j >=0; j--) {
    pow10 -= l10[j];
    l <<= 1;
    if (xn(cmp)(tmp,k,pow10,l10[j]) >= 0) {
      l++;
      xn(cpy)(digits,tmp,k);
      xn(quo_k)(digits,k,pow10,l10[j],tmp,digits);
      k = k - l10[j] + 1; while ((k > 0) && (tmp[k-1] == 0)) k--;
      digits += l10[j];
    }
  }

  /* le dernier quotient peut faire 0,1 ou 2 chiffres */
  /* cadre à deux chiffres dans tous les cas          */
  switch(k) {
  case 0: n = digits[-1]; l = 0; break;
  case 1: n = tmp[0]; break;
  default:n = (((ndouble)tmp[1]) << HW) + (ndouble)tmp[0]; break;
  }

  /* alloue la chaine résultat et place les premiers caractères */
  for (k=0, u=n; u > 0; k++, u /= 10);
  ss = (char *)malloc(k+l*Digits_in_chiffre+2);
  if (ss == NULL) xn(fatal_err)("\nstring_of, out of memory\n");
  s = ss;
  if (sa) *(s++) = '-';
  for (j=k-1; j>=0; j--) {s[j] = n%10 + '0'; n /= 10;}
  s += k;

  /* continue les divisions et convertit en caractères au fur et à mesure */
  for (; l > 0; l--) {

    /* divise un nouveau bloc */
    for (k=l, j=0; (k&1) == 0; j++, k>>=1) pow10 += l10[j];
    while (j > 0) {
      pow10  -= l10[j-1];
      digits -= l10[j];
      xn(quo_k)(digits,l10[j],pow10,l10[j-1],tmp,digits);
      digits +=l10[j-1];
      xn(cpy)(digits,tmp,l10[j-1]);
      digits +=l10[j-1];
      j--;
    }

    /* convertit le dernier quotient en caractères */
    digits --;
    for (p10 = Pow_ten_max/10; p10 > 0; p10 /= 10) {
      *(s++) = digits[0]/p10 + '0';
      digits[0] %= p10;
    }
  }

  /* terminé */
  xn(free)(pow10);
  s[0] = 0;
  return(ss);

}
#endif

