/*                             _ f _ p o w 1 0                             */

/* Floating point powers of ten.
 *
 * __gpow10 computes 10**y where y is an integer. It returns the
 * result as a guarded longdouble.
 *
 * __pow10 is the same as __gpow10 but returns an unguarded longdouble.
 *
 * __mpow10 is the smallest power available from the table. __Mpow10 is
 * the largest power available from the table. These may underflow or
 * overflow, respectively.
 *
 * __fpow10 is a pointer into the small power table for floating point
 * versions of __ipow10. At least __Mipow10 values are available.
 */

#include "stdiolib.h"
#include <math.h>

/*LINTLIBRARY*/

/* Floating point power table indices
 *
 * These indices yield important offsets into the power tables:
 *
 *	N_SMALLPOWERS	number of longdoubles in the small power table
 *	N_LARGEPOWERS	number of longdoubles in the large power table
 *	B_SMALLPOWERS	longdouble index to locate 1e0
 *	B_LARGEPOWERS	longdouble index to locate largest negative power
 *	M_SMALLPOWERS	largest power in small power table plus one
 *	M_LARGEPOWERS	largest power in large power table
 */
#define N_SMALLPOWERS	(sizeof(smallpowers) / sizeof(longdouble))
#define B_SMALLPOWERS	((N_SMALLPOWERS - 1) / 3 * 2)
#define M_SMALLPOWERS	((N_SMALLPOWERS - 1) / 3 + 1)
#define N_LARGEPOWERS	(sizeof(largepowers) / sizeof(longdouble))
#define B_LARGEPOWERS	(N_LARGEPOWERS / 2 - 2)
#define M_LARGEPOWERS	(N_LARGEPOWERS / 4 * M_SMALLPOWERS)

#ifdef	POWERS
  static int smallpowers[] = {
#define SMALL
#include "powers.h"
  };
  static int largepowers[] = {
#define LARGE
#include "powers.h"
  };
#else
#ifndef GENERIC
#define	NEGEXPONENT	0
#define	POSEXPONENT	0
#endif
  static longdouble smallpowers[] = {
    1e-15, 0e0,   1e-14, 0e0,   1e-13, 0e0,   1e-12, 0e0,
    1e-11, 0e0,   1e-10, 0e0,   1e-9,  0e0,   1e-8,  0e0,
    1e-7,  0e0,   1e-6,  0e0,   1e-5,  0e0,   1e-4,  0e0,
    1e-3,  0e0,   1e-2,  0e0,   1e-1,  0e0,
    1e0,   1e1,   1e2,   1e3,   1e4,   1e5,   1e6,   1e7,
    1e8,   1e9,   1e10,  1e11,  1e12,  1e13,  1e14,  1e15,
  };
  static longdouble largepowers[] = {
                 1e-304, 0e0, 1e-288, 0e0, 1e-272, 0e0,
    1e-256, 0e0, 1e-240, 0e0, 1e-224, 0e0, 1e-208, 0e0,
    1e-192, 0e0, 1e-176, 0e0, 1e-160, 0e0, 1e-144, 0e0,
    1e-128, 0e0, 1e-112, 0e0, 1e-96,  0e0, 1e-80,  0e0,
    1e-64,  0e0, 1e-48,  0e0, 1e-32,  0e0, 1e-16,  0e0,
    1e016,  0e0, 1e032,  0e0, 1e048,  0e0, 1e064,  0e0,
    1e080,  0e0, 1e096,  0e0, 1e112,  0e0, 1e128,  0e0,
    1e144,  0e0, 1e160,  0e0, 1e176,  0e0, 1e192,  0e0,
    1e208,  0e0, 1e224,  0e0, 1e240,  0e0, 1e256,  0e0,
    1e272,  0e0, 1e288,  0e0, 1e304,  0e0,
  };
#endif

int __mpow10 = 1 - M_LARGEPOWERS - M_SMALLPOWERS;
#ifdef QDOS
static
#endif /* QDOS */
int __Mpow10 = M_LARGEPOWERS + M_SMALLPOWERS - 1;

/* Small floating point powers of ten
 *
 * Ensure that at least __Mipow10 values are available. There is no
 * executable code, and the value of __Mipow10 is not known until
 * run time, so the assertion is in __xpow10.
 */

longdouble *__fpow10 = &((longdouble *) smallpowers)[B_SMALLPOWERS];

static longdouble __xpow10 F2(int, exp, longguard *, r)

{
  unsigned int aexp;			/* absolute value of exponent */
  int seg;				/* large power index */
  int off;				/* small power index */
  longdouble *p;			/* array pointer */
  longguard n;				/* guarded multiplicand */
  longguard m;				/* guarded multiplier */

  ASSERT(B_SMALLPOWERS > __Mipow10);
  ASSERT(__Mpow10 >= LDBL_MAX_10_EXP);
  ASSERT(__mpow10 <= LDBL_MIN_10_EXP);

  aexp = exp < 0 ? -exp : exp;

  if (aexp < M_SMALLPOWERS) {
    if (exp < 0)
      exp *= 2;
    p = &((longdouble *) smallpowers)[exp + B_SMALLPOWERS];
    if (r == NULL)
      return exp < 0 ? p[0] + p[1] : p[0];
    else {
      r->number   = p[0];
      r->guard    = exp < 0 ? p[1] : 0.0;
      r->exponent = 0;
      if (exp > HALFMANTDIGITS)
	__gguard(r);
    }
  }

  else if (exp >= __mpow10 && exp <= __Mpow10) {

    seg = aexp / M_SMALLPOWERS;
    off = aexp - (seg * M_SMALLPOWERS);

    if (exp >= 0)
      m.exponent = POSEXPONENT;
    else {
      seg  = 1 - seg;
      off  = -off;
      off *= 2;
      m.exponent  = NEGEXPONENT;
    }
    p = &((longdouble *) smallpowers)[off + B_SMALLPOWERS];
    n.number   = p[0];
    n.guard    = off < 0 ? p[1] : 0.0;
    n.exponent = 0;
    __gguard(&n);

    p = &((longdouble *) largepowers)[2*seg + B_LARGEPOWERS];
    m.number = p[0];
    m.guard  = p[1];

    if (r == NULL)
      return __gmul(&n, &m);
    else {
      __ggmul(r, &n, &m);
      __gguard(r);
    }
  }

  else if (r == NULL)
    return HUGE_VAL;
  else {
    r->number   = HUGE_VAL;
    r->guard    = 0.0;
    r->exponent = 0;
  }
  return 0.0;
}

void __gpow10 F2(int, exp, longguard *, r)

{
  (void) __xpow10(exp, r);
}

longdouble __pow10 F1(int, exp)

{
  return __xpow10(exp, (longguard *) NULL);
}
