#include <limits.h>
#include <stdint.h>
#include <math.h>

#include "gdtoa.h"

#if !(defined __x86_64) && !(defined __i386__)
#error "An architecture with extended precision long double required!"
#endif

/* IEEE extended precision floating point format, little endian byte ordering */
union ieee_ext
{
  struct
  {
    uint32_t mt0:32;
    uint32_t mt1:32;
    uint32_t exp:15;
    uint32_t sgn:1;
    uint32_t etc:16;
  } ieee;
  long double d;
};

/*

   __ldtoa: wrapper for long double conversion using gdtoa()
  
   NOTE: This only works for IEEE extended precision long doubles.
  
   Input:
     ld       - a pointer to the value to convert

     The rest of the parameters are the analogous to the corresponding
     parameters to gdtoa().
  
   Returns:
     This function returns a string that is to be free'd with freedtoa().

*/
char *
__ldtoa(long double *ld,
        int mode,
        int ndigits,
        int *decpt,
        int *sign,
        char **rve)
{
  FPI fpi =
  {
    64,                 /* nbits */
    1 - 16383 - 63,     /* emin  */
    32766 - 16383 - 63, /* emax  */
    1,                  /* rounding */
    0                   /* sudden_underflow */
  };
  union ieee_ext *l;
  uint32_t bits[2];
  int exp, kind;
  char *ret;

  l = (union ieee_ext *) ld;
  *sign = l->ieee.sgn;
  exp = l->ieee.exp;
  bits[0] = l->ieee.mt0;
  bits[1] = l->ieee.mt1;

  if (isnan(*ld))        /* NaN                */
      kind = STRTOG_NaN;
  else if (isinf(*ld))   /* Infinity           */
      kind = STRTOG_Infinite;
  else if (exp == 0)     /* Denormalized       */
  {
    kind = STRTOG_Denormal;
    exp = 1;
  }
  else                   /* Normalized or zero */
  {
    if (bits[0] | bits[1])
      kind = STRTOG_Normal;
    else
      kind = STRTOG_Zero;
  }

  exp -= 16383 + 63;

  ret = gdtoa(&fpi, exp, &bits[0], &kind, mode, ndigits, decpt, rve);

  if (*decpt == -32768)
    *decpt = INT_MAX;

  return ret;
}
