/* tsprintf.c -- test file for mpfr_sprintf, mpfr_vsprintf, mpfr_snprintf,
   and mpfr_vsnprintf

Copyright 2007-2022 Free Software Foundation, Inc.
Contributed by the AriC and Caramba projects, INRIA.

This file is part of the GNU MPFR Library.

The GNU MPFR Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

The GNU MPFR Library 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 Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */

/* Needed due to the tests on HAVE_STDARG and MPFR_USE_MINI_GMP */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#if defined(HAVE_STDARG) && !defined(MPFR_USE_MINI_GMP)
#include <stdarg.h>

#include <float.h>
#include <errno.h>

#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

#include "mpfr-test.h"

const int prec_max_printf = 5000; /* limit for random precision in
                                     random_double() */
#define BUF_SIZE 65536

const char pinf_str[] = "inf";
const char pinf_uc_str[] = "INF";
const char minf_str[] = "-inf";
const char minf_uc_str[] = "-INF";
const char nan_str[] = "nan";
const char nan_uc_str[] = "NAN";

int randsize;

/* 1. compare expected string with the string BUFFER returned by
   mpfr_sprintf(buffer, fmt, x)
   2. then test mpfr_snprintf (buffer, p, fmt, x) with a random p. */
static int
check_sprintf (const char *expected, const char *fmt, mpfr_srcptr x)
{
  int n0, n1;
  char buffer[BUF_SIZE];

  /* test mpfr_sprintf */
  n0 = mpfr_sprintf (buffer, fmt, x);
  if (strcmp (buffer, expected) != 0)
    {
      printf ("Error in mpfr_sprintf (s, \"%s\", x);\n", fmt);
      printf ("expected: \"%s\"\ngot:      \"%s\"\n", expected, buffer);

      exit (1);
    }

  /* test mpfr_snprintf */
  randsize = (int) (randlimb () % (n0 + 3)) - 3;  /* between -3 and n0 - 1 */
  if (randsize < 0)
    {
      n1 = mpfr_snprintf (NULL, 0, fmt, x);
    }
  else
    {
      buffer[randsize] = 17;
      n1 = mpfr_snprintf (buffer, randsize, fmt, x);
      if (buffer[randsize] != 17)
        {
          printf ("Buffer overflow in mpfr_snprintf for randsize = %d!\n",
                  randsize);
          exit (1);
        }
    }
  if (n0 != n1)
    {
      printf ("Error in mpfr_snprintf (s, %d, \"%s\", x) return value\n",
              randsize, fmt);
      printf ("expected: %d\ngot:      %d\nx='", n0, n1);
      mpfr_printf (fmt, x);
      printf ("'\n");
      exit (1);
    }
  if ((randsize > 1 && strncmp (expected, buffer, randsize - 1) != 0)
      || (randsize == 1 && buffer[0] != '\0'))
    {
      char part_expected[BUF_SIZE];
      strncpy (part_expected, expected, randsize);
      part_expected[randsize - 1] = '\0';
      printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...);\n",
              randsize, fmt);
      printf ("expected: \"%s\"\ngot:      \"%s\"\n", part_expected, buffer);
      exit (1);
    }
  return n0;
}

/* 1. compare expected string with the string BUFFER returned by
   mpfr_vsprintf(buffer, fmt, ...)
   2. then, test mpfr_vsnprintf. */
static int
check_vsprintf (const char *expected, const char *fmt, ...)
{
  int n0, n1;
  char buffer[BUF_SIZE];
  va_list ap0, ap1;

  va_start (ap0, fmt);
  n0 = mpfr_vsprintf (buffer, fmt, ap0);
  va_end (ap0);

  if (strcmp (buffer, expected) != 0)
    {
      printf ("Error in mpfr_vsprintf (s, \"%s\", ...);\n", fmt);
      printf ("expected: \"%s\"\ngot:      \"%s\"\n", expected, buffer);
      exit (1);
    }

  va_start (ap1, fmt);

  /* test mpfr_snprintf */
  randsize = (int) (randlimb () % (n0 + 3)) - 3;  /* between -3 and n0 - 1 */
  if (randsize < 0)
    {
      n1 = mpfr_vsnprintf (NULL, 0, fmt, ap1);
    }
  else
    {
      buffer[randsize] = 17;
      n1 = mpfr_vsnprintf (buffer, randsize, fmt, ap1);
      if (buffer[randsize] != 17)
        {
          printf ("Buffer overflow in mpfr_vsnprintf for randsize = %d!\n",
                  randsize);
          exit (1);
        }
    }

  va_end (ap1);

  if (n0 != n1)
    {
      printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...) return value\n",
              randsize, fmt);
      printf ("expected: %d\ngot:      %d\n", n0, n1);
      exit (1);
    }
  if ((randsize > 1 && strncmp (expected, buffer, randsize - 1) != 0)
      || (randsize == 1 && buffer[0] != '\0'))
    {
      char part_expected[BUF_SIZE];

      strncpy (part_expected, expected, randsize);
      part_expected[randsize - 1] = '\0';
      printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...);\n",
              randsize, fmt);
      printf ("expected: \"%s\"\ngot:      \"%s\"\n", part_expected, buffer);
      exit (1);
    }

  return n0;
}

static void
native_types (void)
{
  int c = 'a';
  int i = -1;
  unsigned int ui = 1;
  double d[] = { -1.25, 7.62939453125e-6 /* 2^(-17) */ };
  char s[] = "test";
  char buf[255];
  int k;

  sprintf (buf, "%c", c);
  check_vsprintf (buf, "%c", c);

  sprintf (buf, "%d", i);
  check_vsprintf (buf, "%d", i);

  check_vsprintf ("0", "%d", 0);
  check_vsprintf ("", "%.d", 0);
  check_vsprintf ("", "%.0d", 0);

  sprintf (buf, "%i", i);
  check_vsprintf (buf, "%i", i);

  check_vsprintf ("0", "%i", 0);
  check_vsprintf ("", "%.i", 0);
  check_vsprintf ("", "%.0i", 0);

  for (k = 0; k < numberof(d); k++)
    {
      sprintf (buf, "%e", d[k]);
      check_vsprintf (buf, "%e", d[k]);

      sprintf (buf, "%E", d[k]);
      check_vsprintf (buf, "%E", d[k]);

      sprintf (buf, "%f", d[k]);
      check_vsprintf (buf, "%f", d[k]);

      sprintf (buf, "%g", d[k]);
      check_vsprintf (buf, "%g", d[k]);

      sprintf (buf, "%G", d[k]);
      check_vsprintf (buf, "%G", d[k]);

#if __MPFR_STDC (199901L)

      gmp_sprintf (buf, "%a", d[k]);
      check_vsprintf (buf, "%a", d[k]);

      gmp_sprintf (buf, "%A", d[k]);
      check_vsprintf (buf, "%A", d[k]);

      gmp_sprintf (buf, "%la", d[k]);
      check_vsprintf (buf, "%la", d[k]);

      gmp_sprintf (buf, "%lA", d[k]);
      check_vsprintf (buf, "%lA", d[k]);

      sprintf (buf, "%le", d[k]);
      check_vsprintf (buf, "%le", d[k]);

      sprintf (buf, "%lE", d[k]);
      check_vsprintf (buf, "%lE", d[k]);

      sprintf (buf, "%lf", d[k]);
      check_vsprintf (buf, "%lf", d[k]);

      sprintf (buf, "%lg", d[k]);
      check_vsprintf (buf, "%lg", d[k]);

      sprintf (buf, "%lG", d[k]);
      check_vsprintf (buf, "%lG", d[k]);

#endif
    }

  sprintf (buf, "%o", i);
  check_vsprintf (buf, "%o", i);

  sprintf (buf, "%s", s);
  check_vsprintf (buf, "%s", s);

  sprintf (buf, "--%s++", "");
  check_vsprintf (buf, "--%s++", "");

  sprintf (buf, "%u", ui);
  check_vsprintf (buf, "%u", ui);

  sprintf (buf, "%x", ui);
  check_vsprintf (buf, "%x", ui);
}

static int
decimal (void)
{
  mpfr_prec_t p = 128;
  mpfr_t x, y, z;

  /* specifier 'P' for precision */
  check_vsprintf ("128", "%Pu", p);
  check_vsprintf ("00128", "%.5Pu", p);
  check_vsprintf ("  128", "%5Pu", p);
  check_vsprintf ("000128", "%06Pu", p);
  check_vsprintf ("128    :", "%-7Pu:", p);
  check_vsprintf ("000128:", "%-2.6Pd:", p);
  check_vsprintf ("  000128:", "%8.6Pd:", p);
  check_vsprintf ("000128  :", "%-8.6Pd:", p);
  check_vsprintf ("+128:", "%+Pd:", p);
  check_vsprintf (" 128:", "% Pd:", p);
  check_vsprintf ("80:", "% Px:", p);
  check_vsprintf ("0x80:", "% #Px:", p);
  check_vsprintf ("0x80:", "%0#+ -Px:", p);
  check_vsprintf ("0200:", "%0#+ -Po:", p);
  check_vsprintf ("+0000128 :", "%0+ *.*Pd:", -9, 7, p);
  check_vsprintf ("+12345   :", "%0+ -*.*Pd:", -9, -3, (mpfr_prec_t) 12345);
  check_vsprintf ("0", "%Pu", (mpfr_prec_t) 0);
  /* Do not add a test like "%05.1Pd" as MS Windows is buggy: when
     a precision is given, the '0' flag must be ignored. */

  /* specifier 'P' with precision field 0 */
  check_vsprintf ("128", "%.Pu", p);
  check_vsprintf ("128", "%.0Pd", p);
  /* check_vsprintf ("", "%.Pu", (mpfr_prec_t) 0); */
  check_vsprintf ("", "%.0Pd", (mpfr_prec_t) 0);

  mpfr_init (z);
  mpfr_init2 (x, 128);

  /* special numbers */
  mpfr_set_inf (x, 1);
  check_sprintf (pinf_str, "%Re", x);
  check_sprintf (pinf_str, "%RUe", x);
  check_sprintf (pinf_uc_str, "%RE", x);
  check_sprintf (pinf_uc_str, "%RDE", x);
  check_sprintf (pinf_str, "%Rf", x);
  check_sprintf (pinf_str, "%RYf", x);
  check_sprintf (pinf_uc_str, "%RF", x);
  check_sprintf (pinf_uc_str, "%RZF", x);
  check_sprintf (pinf_str, "%Rg", x);
  check_sprintf (pinf_str, "%RNg", x);
  check_sprintf (pinf_uc_str, "%RG", x);
  check_sprintf (pinf_uc_str, "%RUG", x);
  check_sprintf ("       inf", "%010Re", x);
  check_sprintf ("       inf", "%010RDe", x);

  mpfr_set_inf (x, -1);
  check_sprintf (minf_str, "%Re", x);
  check_sprintf (minf_str, "%RYe", x);
  check_sprintf (minf_uc_str, "%RE", x);
  check_sprintf (minf_uc_str, "%RZE", x);
  check_sprintf (minf_str, "%Rf", x);
  check_sprintf (minf_str, "%RNf", x);
  check_sprintf (minf_uc_str, "%RF", x);
  check_sprintf (minf_uc_str, "%RUF", x);
  check_sprintf (minf_str, "%Rg", x);
  check_sprintf (minf_str, "%RDg", x);
  check_sprintf (minf_uc_str, "%RG", x);
  check_sprintf (minf_uc_str, "%RYG", x);
  check_sprintf ("      -inf", "%010Re", x);
  check_sprintf ("      -inf", "%010RZe", x);

  mpfr_set_nan (x);
  check_sprintf (nan_str, "%Re", x);
  check_sprintf (nan_str, "%RNe", x);
  check_sprintf (nan_uc_str, "%RE", x);
  check_sprintf (nan_uc_str, "%RUE", x);
  check_sprintf (nan_str, "%Rf", x);
  check_sprintf (nan_str, "%RDf", x);
  check_sprintf (nan_uc_str, "%RF", x);
  check_sprintf (nan_uc_str, "%RYF", x);
  check_sprintf (nan_str, "%Rg", x);
  check_sprintf (nan_str, "%RZg", x);
  check_sprintf (nan_uc_str, "%RG", x);
  check_sprintf (nan_uc_str, "%RNG", x);
  check_sprintf ("       nan", "%010Re", x);

  /* positive numbers */
  mpfr_set_str (x, "18993474.61279296875", 10, MPFR_RNDN);
  mpfr_init2 (y, 59);
  mpfr_set (y, x, MPFR_RNDN);
  mpfr_set_ui (z, 0, MPFR_RNDD);

  /* simplest case right justified */
  check_sprintf ("1.899347461279296875000000000000000000000e+07", "%30Re", x);
  check_sprintf ("      1.899347461279296875e+07", "%30Re", y);
  check_sprintf ("                         2e+07", "%30.0Re", x);
  check_sprintf ("               18993474.612793", "%30Rf", x);
  check_sprintf ("              18993474.6127930", "%30.7Rf", x);
  check_sprintf ("                   1.89935e+07", "%30Rg", x);
  check_sprintf ("                         2e+07", "%30.0Rg", x);
  check_sprintf ("          18993474.61279296875", "%30.19Rg", x);
  check_sprintf ("        0.0000000000000000e+00", "%30Re", z);
  check_sprintf ("                      0.000000", "%30Rf", z);
  check_sprintf ("                             0", "%30Rg", z);
  check_sprintf ("                       0.00000", "%#30Rg", z);
  check_sprintf ("                         0e+00", "%30.0Re", z);
  check_sprintf ("                             0", "%30.0Rf", z);
  check_sprintf ("                        0.0000", "%30.4Rf", z);
  check_sprintf ("                             0", "%30.0Rg", z);
  check_sprintf ("                             0", "%30.4Rg", z);
  /* sign or space, pad with leading zeros */
  check_sprintf (" 1.899347461279296875000000000000000000000E+07", "% 030RE", x);
  check_sprintf (" 000001.899347461279296875E+07", "% 030RE", y);
  check_sprintf (" 0000000000000000001.89935E+07", "% 030RG", x);
  check_sprintf (" 0000000000000000000000002E+07", "% 030.0RE", x);
  check_sprintf (" 0000000000000000000000000E+00", "% 030.0RE", z);
  check_sprintf (" 00000000000000000000000000000", "% 030.0RF", z);
  /* sign + or -, left justified */
  check_sprintf ("+1.899347461279296875000000000000000000000e+07", "%+-30Re", x);
  check_sprintf ("+1.899347461279296875e+07     ", "%+-30Re", y);
  check_sprintf ("+2e+07                        ", "%+-30.0Re", x);
  check_sprintf ("+0e+00                        ", "%+-30.0Re", z);
  check_sprintf ("+0                            ", "%+-30.0Rf", z);
  /* decimal point, left justified, precision and rounding parameter */
  check_vsprintf ("1.9E+07   ", "%#-10.*R*E", 1, MPFR_RNDN, x);
  check_vsprintf ("2.E+07    ", "%#*.*R*E", -10, 0, MPFR_RNDN, x);
  check_vsprintf ("2.E+07    ", "%#-10.*R*G", 0, MPFR_RNDN, x);
  check_vsprintf ("0.E+00    ", "%#-10.*R*E", 0, MPFR_RNDN, z);
  check_vsprintf ("0.        ", "%#-10.*R*F", 0, MPFR_RNDN, z);
  check_vsprintf ("0.        ", "%#-10.*R*G", 0, MPFR_RNDN, z);
  /* sign or space */
  check_sprintf (" 1.899e+07", "% .3RNe", x);
  check_sprintf (" 2e+07",     "% .0RNe", x);
  /* sign + or -, decimal point, pad with leading zeros */
  check_sprintf ("+0001.8E+07", "%0+#11.1RZE", x);
  check_sprintf ("+00001.E+07", "%0+#11.0RZE", x);
  check_sprintf ("+0000.0E+00", "%0+#11.1RZE", z);
  check_sprintf ("+00000000.0", "%0+#11.1RZF", z);
  /* pad with leading zero */
  check_sprintf ("1.899347461279296875000000000000000000000e+07", "%030RDe", x);
  check_sprintf ("0000001.899347461279296875e+07", "%030RDe", y);
  check_sprintf ("00000000000000000000000001e+07", "%030.0RDe", x);
  /* sign or space, decimal point, left justified */
  check_sprintf (" 1.8E+07   ", "%- #11.1RDE", x);
  check_sprintf (" 1.E+07    ", "%- #11.0RDE", x);
  /* large requested precision */
  check_sprintf ("18993474.61279296875", "%.2147483647Rg", x);

  /* negative numbers */
  mpfr_mul_si (x, x, -1, MPFR_RNDD);
  mpfr_mul_si (z, z, -1, MPFR_RNDD);

  /* sign + or - */
  check_sprintf ("  -1.8e+07", "%+10.1RUe", x);
  check_sprintf ("    -1e+07", "%+10.0RUe", x);
  check_sprintf ("    -0e+00", "%+10.0RUe", z);
  check_sprintf ("        -0", "%+10.0RUf", z);


  /* neighborhood of 1 */
  mpfr_set_str (x, "0.99993896484375", 10, MPFR_RNDN);
  mpfr_set_prec (y, 43);
  mpfr_set (y, x, MPFR_RNDN);
  check_sprintf ("9.999389648437500000000000000000000000000E-01", "%-20RE", x);
  check_sprintf ("9.999389648437500000000000000000000000000E-01", "%-20.RE", x);
  check_sprintf ("9.9993896484375E-01 ", "%-20RE", y);
  check_sprintf ("9.9993896484375E-01 ", "%-20.RE", y);
  check_sprintf ("1E+00               ", "%-20.0RE", x);
  check_sprintf ("1.0E+00             ", "%-20.1RE", x);
  check_sprintf ("1.00E+00            ", "%-20.2RE", x);
  check_sprintf ("9.999E-01           ", "%-20.3RE", x);
  check_sprintf ("9.9994E-01          ", "%-20.4RE", x);
  check_sprintf ("0.999939            ", "%-20RF", x);
  check_sprintf ("0.999939            ", "%-20.RF", x);
  check_sprintf ("1                   ", "%-20.0RF", x);
  check_sprintf ("1.0                 ", "%-20.1RF", x);
  check_sprintf ("1.00                ", "%-20.2RF", x);
  check_sprintf ("1.000               ", "%-20.3RF", x);
  check_sprintf ("0.9999              ", "%-20.4RF", x);
  check_sprintf ("0.999939            ", "%-#20RF", x);
  check_sprintf ("0.999939            ", "%-#20.RF", x);
  check_sprintf ("1.                  ", "%-#20.0RF", x);
  check_sprintf ("1.0                 ", "%-#20.1RF", x);
  check_sprintf ("1.00                ", "%-#20.2RF", x);
  check_sprintf ("1.000               ", "%-#20.3RF", x);
  check_sprintf ("0.9999              ", "%-#20.4RF", x);
  check_sprintf ("1                   ", "%-20.0RG", x);
  check_sprintf ("1                   ", "%-20.1RG", x);
  check_sprintf ("1                   ", "%-20.2RG", x);
  check_sprintf ("1                   ", "%-20.3RG", x);
  check_sprintf ("0.9999              ", "%-20.4RG", x);
  check_sprintf ("0.999939            ", "%-#20RG", x);
  check_sprintf ("0.999939            ", "%-#20.RG", x);
  check_sprintf ("1.                  ", "%-#20.0RG", x);
  check_sprintf ("1.                  ", "%-#20.1RG", x);
  check_sprintf ("1.0                 ", "%-#20.2RG", x);
  check_sprintf ("1.00                ", "%-#20.3RG", x);
  check_sprintf ("0.9999              ", "%-#20.4RG", x);

  /* powers of 10 */
  mpfr_set_str (x, "1e17", 10, MPFR_RNDN);
  check_sprintf ("1.000000000000000000000000000000000000000e+17", "%Re", x);
  check_sprintf ("1.000e+17", "%.3Re", x);
  check_sprintf ("100000000000000000", "%.0Rf", x);
  check_sprintf ("100000000000000000.0", "%.1Rf", x);
  check_sprintf ("100000000000000000.000000", "%'Rf", x);
  check_sprintf ("100000000000000000.0", "%'.1Rf", x);

  mpfr_ui_div (x, 1, x, MPFR_RNDN); /* x=1e-17 */
  check_sprintf ("1.000000000000000000000000000000000000000e-17", "%Re", x);
  check_sprintf ("0.000000", "%Rf", x);
  check_sprintf ("1e-17", "%Rg", x);
  check_sprintf ("0.0", "%.1RDf", x);
  check_sprintf ("0.0", "%.1RZf", x);
  check_sprintf ("0.1", "%.1RUf", x);
  check_sprintf ("0.1", "%.1RYf", x);
  check_sprintf ("0", "%.0RDf", x);
  check_sprintf ("0", "%.0RZf", x);
  check_sprintf ("1", "%.0RUf", x);
  check_sprintf ("1", "%.0RYf", x);

  /* powers of 10 with 'g' style */
  mpfr_set_str (x, "10", 10, MPFR_RNDN);
  check_sprintf ("10", "%Rg", x);
  check_sprintf ("1e+01", "%.0Rg", x);
  check_sprintf ("1e+01", "%.1Rg", x);
  check_sprintf ("10", "%.2Rg", x);

  mpfr_ui_div (x, 1, x, MPFR_RNDN);
  check_sprintf ("0.1", "%Rg", x);
  check_sprintf ("0.1", "%.0Rg", x);
  check_sprintf ("0.1", "%.1Rg", x);

  mpfr_set_str (x, "1000", 10, MPFR_RNDN);
  check_sprintf ("1000", "%Rg", x);
  check_sprintf ("1e+03", "%.0Rg", x);
  check_sprintf ("1e+03", "%.3Rg", x);
  check_sprintf ("1000", "%.4Rg", x);
  check_sprintf ("1e+03", "%.3Rg", x);
  check_sprintf ("1000", "%.4Rg", x);
  check_sprintf ("    1e+03", "%9.3Rg", x);
  check_sprintf ("     1000", "%9.4Rg", x);
  check_sprintf ("00001e+03", "%09.3Rg", x);
  check_sprintf ("000001000", "%09.4Rg", x);

  mpfr_ui_div (x, 1, x, MPFR_RNDN);
  check_sprintf ("0.001", "%Rg", x);
  check_sprintf ("0.001", "%.0Rg", x);
  check_sprintf ("0.001", "%.1Rg", x);

  mpfr_set_str (x, "100000", 10, MPFR_RNDN);
  check_sprintf ("100000", "%Rg", x);
  check_sprintf ("1e+05", "%.0Rg", x);
  check_sprintf ("1e+05", "%.5Rg", x);
  check_sprintf ("100000", "%.6Rg", x);
  check_sprintf ("            1e+05", "%17.5Rg", x);
  check_sprintf ("           100000", "%17.6Rg", x);
  check_sprintf ("0000000000001e+05", "%017.5Rg", x);
  check_sprintf ("00000000000100000", "%017.6Rg", x);

  mpfr_ui_div (x, 1, x, MPFR_RNDN);
  check_sprintf ("1e-05", "%Rg", x);
  check_sprintf ("1e-05", "%.0Rg", x);
  check_sprintf ("1e-05", "%.1Rg", x);

  /* check rounding mode */
  mpfr_set_str (x, "0.0076", 10, MPFR_RNDN);
  check_sprintf ("0.007", "%.3RDF", x);
  check_sprintf ("0.007", "%.3RZF", x);
  check_sprintf ("0.008", "%.3RF", x);
  check_sprintf ("0.008", "%.3RUF", x);
  check_sprintf ("0.008", "%.3RYF", x);
  check_vsprintf ("0.008", "%.3R*F", MPFR_RNDA, x);

  /* check limit between %f-style and %g-style */
  mpfr_set_str (x, "0.0000999", 10, MPFR_RNDN);
  check_sprintf ("0.0001",   "%.0Rg", x);
  check_sprintf ("9e-05",    "%.0RDg", x);
  check_sprintf ("0.0001",   "%.1Rg", x);
  check_sprintf ("0.0001",   "%.2Rg", x);
  check_sprintf ("9.99e-05", "%.3Rg", x);

  /* trailing zeros */
  mpfr_set_si_2exp (x, -1, -15, MPFR_RNDN); /* x=-2^-15 */
  check_sprintf ("-3.0517578125e-05", "%.30Rg", x);
  check_sprintf ("-3.051757812500000000000000000000e-05", "%.30Re", x);
  check_sprintf ("-3.05175781250000000000000000000e-05", "%#.30Rg", x);
  check_sprintf ("-0.000030517578125000000000000000", "%.30Rf", x);

  /* bug 20081023 */
  check_sprintf ("-3.0517578125e-05", "%.30Rg", x);
  mpfr_set_str (x, "1.9999", 10, MPFR_RNDN);
  check_sprintf ("1.999900  ", "%-#10.7RG", x);
  check_sprintf ("1.9999    ", "%-10.7RG", x);
  mpfr_set_ui (x, 1, MPFR_RNDN);
  check_sprintf ("1.", "%#.1Rg", x);
  check_sprintf ("1.   ", "%-#5.1Rg", x);
  check_sprintf ("  1.0", "%#5.2Rg", x);
  check_sprintf ("1.00000000000000000000000000000", "%#.30Rg", x);
  check_sprintf ("1", "%.30Rg", x);
  mpfr_set_ui (x, 0, MPFR_RNDN);
  check_sprintf ("0.", "%#.1Rg", x);
  check_sprintf ("0.   ", "%-#5.1Rg", x);
  check_sprintf ("  0.0", "%#5.2Rg", x);
  check_sprintf ("0.00000000000000000000000000000", "%#.30Rg", x);
  check_sprintf ("0", "%.30Rg", x);

  /* following tests with precision 53 bits */
  mpfr_set_prec (x, 53);

  /* Exponent zero has a plus sign */
  mpfr_set_str (x, "-9.95645044213728791504536275169812142849e-01", 10,
                MPFR_RNDN);
  check_sprintf ("-1.0e+00", "%- #0.1Re", x);

  /* Decimal point and no figure after it with '#' flag and 'G' style */
  mpfr_set_str (x, "-9.90597761233942053494e-01", 10, MPFR_RNDN);
  check_sprintf ("-1.", "%- #0.1RG", x);

  /* precision zero */
  mpfr_set_d (x, 9.5, MPFR_RNDN);
  check_sprintf ("9",    "%.0RDf", x);
  check_sprintf ("10",    "%.0RUf", x);

  mpfr_set_d (x, 19.5, MPFR_RNDN);
  check_sprintf ("19",    "%.0RDf", x);
  check_sprintf ("20",    "%.0RUf", x);

  mpfr_set_d (x, 99.5, MPFR_RNDN);
  check_sprintf ("99",    "%.0RDf", x);
  check_sprintf ("100",   "%.0RUf", x);

  mpfr_set_d (x, -9.5, MPFR_RNDN);
  check_sprintf ("-10",    "%.0RDf", x);
  check_sprintf ("-10",    "%.0RYf", x);
  check_sprintf ("-10",    "%.0Rf", x);
  check_sprintf ("-1e+01", "%.0Re", x);
  check_sprintf ("-1e+01", "%.0Rg", x);
  mpfr_set_ui_2exp (x, 1, -1, MPFR_RNDN);
  check_sprintf ("0",      "%.0Rf", x);
  check_sprintf ("5e-01",  "%.0Re", x);
  check_sprintf ("0.5",    "%.0Rg", x);
  mpfr_set_ui_2exp (x, 3, -1, MPFR_RNDN);
  check_sprintf ("2",      "%.0Rf", x);
  mpfr_set_ui_2exp (x, 5, -1, MPFR_RNDN);
  check_sprintf ("2",      "%.0Rf", x);
  mpfr_set_ui (x, 0x1f, MPFR_RNDN);
  check_sprintf ("0x1p+5", "%.0Ra", x);
  mpfr_set_ui (x, 3, MPFR_RNDN);
  check_sprintf ("1p+2",   "%.0Rb", x);

  /* round to next ten power with %f but not with %g */
  mpfr_set_str (x, "-6.64464380544039223686e-02", 10, MPFR_RNDN);
  check_sprintf ("-0.1",  "%.1Rf", x);
  check_sprintf ("-0.0",  "%.1RZf", x);
  check_sprintf ("-0.07", "%.1Rg", x);
  check_sprintf ("-0.06", "%.1RZg", x);

  /* round to next ten power and do not remove trailing zeros */
  mpfr_set_str (x, "9.98429393291486722006e-02", 10, MPFR_RNDN);
  check_sprintf ("0.1",   "%#.1Rg", x);
  check_sprintf ("0.10",  "%#.2Rg", x);
  check_sprintf ("0.099", "%#.2RZg", x);

  /* Halfway cases */
  mpfr_set_str (x, "1.5", 10, MPFR_RNDN);
  check_sprintf ("2e+00", "%.0Re", x);
  mpfr_set_str (x, "2.5", 10, MPFR_RNDN);
  check_sprintf ("2e+00", "%.0Re", x);
  mpfr_set_str (x, "9.5", 10, MPFR_RNDN);
  check_sprintf ("1e+01", "%.0Re", x);
  mpfr_set_str (x, "1.25", 10, MPFR_RNDN);
  check_sprintf ("1.2e+00", "%.1Re", x);
  mpfr_set_str (x, "1.75", 10, MPFR_RNDN);
  check_sprintf ("1.8e+00", "%.1Re", x);
  mpfr_set_str (x, "-0.5", 10, MPFR_RNDN);
  check_sprintf ("-0", "%.0Rf", x);
  mpfr_set_str (x, "1.25", 10, MPFR_RNDN);
  check_sprintf ("1.2", "%.1Rf", x);
  mpfr_set_str (x, "1.75", 10, MPFR_RNDN);
  check_sprintf ("1.8", "%.1Rf", x);
  mpfr_set_str (x, "1.5", 10, MPFR_RNDN);
  check_sprintf ("2", "%.1Rg", x);
  mpfr_set_str (x, "2.5", 10, MPFR_RNDN);
  check_sprintf ("2", "%.1Rg", x);
  mpfr_set_str (x, "9.25", 10, MPFR_RNDN);
  check_sprintf ("9.2", "%.2Rg", x);
  mpfr_set_str (x, "9.75", 10, MPFR_RNDN);
  check_sprintf ("9.8", "%.2Rg", x);

  /* assertion failure in r6320 */
  mpfr_set_str (x, "-9.996", 10, MPFR_RNDN);
  check_sprintf ("-10.0", "%.1Rf", x);

  /* regression in MPFR 3.1.0 (bug introduced in r7761, fixed in r7931) */
  check_sprintf ("-10", "%.2Rg", x);

  mpfr_clears (x, y, z, (mpfr_ptr) 0);
  return 0;
}

static int
hexadecimal (void)
{
  mpfr_t x, z;
  mpfr_inits2 (64, x, z, (mpfr_ptr) 0);

  /* special */
  mpfr_set_inf (x, 1);
  check_sprintf (pinf_str, "%Ra", x);
  check_sprintf (pinf_str, "%RUa", x);
  check_sprintf (pinf_str, "%RDa", x);
  check_sprintf (pinf_uc_str, "%RA", x);
  check_sprintf (pinf_uc_str, "%RYA", x);
  check_sprintf (pinf_uc_str, "%RZA", x);
  check_sprintf (pinf_uc_str, "%RNA", x);

  mpfr_set_inf (x, -1);
  check_sprintf (minf_str, "%Ra", x);
  check_sprintf (minf_str, "%RYa", x);
  check_sprintf (minf_str, "%RZa", x);
  check_sprintf (minf_str, "%RNa", x);
  check_sprintf (minf_uc_str, "%RA", x);
  check_sprintf (minf_uc_str, "%RUA", x);
  check_sprintf (minf_uc_str, "%RDA", x);

  mpfr_set_nan (x);
  check_sprintf (nan_str, "%Ra", x);
  check_sprintf (nan_uc_str, "%RA", x);

  /* regular numbers */
  mpfr_set_str (x, "FEDCBA9.87654321", 16, MPFR_RNDN);
  mpfr_set_ui (z, 0, MPFR_RNDZ);

  /* simplest case right justified */
  check_sprintf ("   0xf.edcba987654321p+24", "%25Ra", x);
  check_sprintf ("   0xf.edcba987654321p+24", "%25RUa", x);
  check_sprintf ("   0xf.edcba987654321p+24", "%25RDa", x);
  check_sprintf ("   0xf.edcba987654321p+24", "%25RYa", x);
  check_sprintf ("   0xf.edcba987654321p+24", "%25RZa", x);
  check_sprintf ("   0xf.edcba987654321p+24", "%25RNa", x);
  check_sprintf ("                  0x1p+28", "%25.0Ra", x);
  check_sprintf ("                   0x0p+0", "%25.0Ra", z);
  check_sprintf ("                   0x0p+0", "%25Ra", z);
  check_sprintf ("                  0x0.p+0", "%#25Ra", z);
  /* sign or space, pad with leading zeros */
  check_sprintf (" 0X00F.EDCBA987654321P+24", "% 025RA", x);
  check_sprintf (" 0X000000000000000001P+28", "% 025.0RA", x);
  check_sprintf (" 0X0000000000000000000P+0", "% 025.0RA", z);
  /* sign + or -, left justified */
  check_sprintf ("+0xf.edcba987654321p+24  ", "%+-25Ra", x);
  check_sprintf ("+0x1p+28                 ", "%+-25.0Ra", x);
  check_sprintf ("+0x0p+0                  ", "%+-25.0Ra", z);
  /* decimal point, left justified, precision and rounding parameter */
  check_vsprintf ("0XF.FP+24 ", "%#-10.*R*A", 1, MPFR_RNDN, x);
  check_vsprintf ("0X1.P+28  ", "%#-10.*R*A", 0, MPFR_RNDN, x);
  check_vsprintf ("0X0.P+0   ", "%#-10.*R*A", 0, MPFR_RNDN, z);
  /* sign or space */
  check_sprintf (" 0xf.eddp+24", "% .3RNa", x);
  check_sprintf (" 0x1p+28",     "% .0RNa", x);
  /* sign + or -, decimal point, pad with leading zeros */
  check_sprintf ("+0X0F.EP+24", "%0+#11.1RZA", x);
  check_sprintf ("+0X00F.P+24", "%0+#11.0RZA", x);
  check_sprintf ("+0X000.0P+0", "%0+#11.1RZA", z);
  /* pad with leading zero */
  check_sprintf ("0x0000f.edcba987654321p+24", "%026RDa", x);
  check_sprintf ("0x0000000000000000000fp+24", "%026.0RDa", x);
  /* sign or space, decimal point, left justified */
  check_sprintf (" 0XF.EP+24 ", "%- #11.1RDA", x);
  check_sprintf (" 0XF.P+24  ", "%- #11.0RDA", x);

  mpfr_mul_si (x, x, -1, MPFR_RNDD);
  mpfr_mul_si (z, z, -1, MPFR_RNDD);

  /* sign + or - */
  check_sprintf ("-0xf.ep+24", "%+10.1RUa", x);
  check_sprintf ("  -0xfp+24", "%+10.0RUa", x);
  check_sprintf ("   -0x0p+0", "%+10.0RUa", z);

  /* rounding bit is zero */
  mpfr_set_str (x, "0xF.7", 16, MPFR_RNDN);
  check_sprintf ("0XFP+0", "%.0RNA", x);
  /* tie case in round to nearest mode */
  mpfr_set_str (x, "0x0.8800000000000000p+3", 16, MPFR_RNDN);
  check_sprintf ("0x9.p-1", "%#.0RNa", x);
  mpfr_set_str (x, "-0x0.9800000000000000p+3", 16, MPFR_RNDN);
  check_sprintf ("-0xap-1", "%.0RNa", x);
  /* trailing zeros in fractional part */
  check_sprintf ("-0X4.C0000000000000000000P+0", "%.20RNA", x);
  /* rounding bit is one and the first non zero bit is far away */
  mpfr_set_prec (x, 1024);
  mpfr_set_ui_2exp (x, 29, -1, MPFR_RNDN);
  mpfr_nextabove (x);
  check_sprintf ("0XFP+0", "%.0RNA", x);

  /* with more than one limb */
  mpfr_set_prec (x, 300);
  mpfr_set_str (x, "0xf.ffffffffffffffffffffffffffffffffffffffffffffffffffff"
                "fffffffffffffffff", 16, MPFR_RNDN);
  check_sprintf ("0x1p+4 [300]", "%.0RNa [300]", x);
  check_sprintf ("0xfp+0 [300]", "%.0RZa [300]", x);
  check_sprintf ("0x1p+4 [300]", "%.0RYa [300]", x);
  check_sprintf ("0xfp+0 [300]", "%.0RDa [300]", x);
  check_sprintf ("0x1p+4 [300]", "%.0RUa [300]", x);
  check_sprintf ("0x1.0000000000000000000000000000000000000000p+4",
                 "%.40RNa", x);
  check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffffffp+0",
                 "%.40RZa", x);
  check_sprintf ("0x1.0000000000000000000000000000000000000000p+4",
                 "%.40RYa", x);
  check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffffffp+0",
                 "%.40RDa", x);
  check_sprintf ("0x1.0000000000000000000000000000000000000000p+4",
                 "%.40RUa", x);

  mpfr_set_str (x, "0xf.7fffffffffffffffffffffffffffffffffffffffffffffffffff"
                "ffffffffffffffffff", 16, MPFR_RNDN);
  check_sprintf ("0XFP+0", "%.0RNA", x);
  check_sprintf ("0XFP+0", "%.0RZA", x);
  check_sprintf ("0X1P+4", "%.0RYA", x);
  check_sprintf ("0XFP+0", "%.0RDA", x);
  check_sprintf ("0X1P+4", "%.0RUA", x);
  check_sprintf ("0XF.8P+0", "%.1RNA", x);
  check_sprintf ("0XF.7P+0", "%.1RZA", x);
  check_sprintf ("0XF.8P+0", "%.1RYA", x);
  check_sprintf ("0XF.7P+0", "%.1RDA", x);
  check_sprintf ("0XF.8P+0", "%.1RUA", x);

  /* do not round up to the next power of the base */
  mpfr_set_str (x, "0xf.fffffffffffffffffffffffffffffffffffffeffffffffffffff"
                "ffffffffffffffffff", 16, MPFR_RNDN);
  check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0",
                 "%.40RNa", x);
  check_sprintf ("0xf.fffffffffffffffffffffffffffffffffffffeffp+0",
                 "%.40RZa", x);
  check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0",
                 "%.40RYa", x);
  check_sprintf ("0xf.fffffffffffffffffffffffffffffffffffffeffp+0",
                 "%.40RDa", x);
  check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0",
                 "%.40RUa", x);

  mpfr_clears (x, z, (mpfr_ptr) 0);
  return 0;
}

static int
binary (void)
{
  mpfr_t x;
  mpfr_t z;
  mpfr_inits2 (64, x, z, (mpfr_ptr) 0);

  /* special */
  mpfr_set_inf (x, 1);
  check_sprintf (pinf_str, "%Rb", x);

  mpfr_set_inf (x, -1);
  check_sprintf (minf_str, "%Rb", x);

  mpfr_set_nan (x);
  check_sprintf (nan_str, "%Rb", x);

  /* regular numbers */
  mpfr_set_str (x, "1110010101.1001101", 2, MPFR_RNDN);
  mpfr_set_ui (z, 0, MPFR_RNDN);

  /* simplest case: right justified */
  check_sprintf ("    1.1100101011001101p+9", "%25Rb", x);
  check_sprintf ("                     0p+0", "%25Rb", z);
  check_sprintf ("                    0.p+0", "%#25Rb", z);
  /* sign or space, pad with leading zeros */
  check_sprintf (" 0001.1100101011001101p+9", "% 025Rb", x);
  check_sprintf (" 000000000000000000000p+0", "% 025Rb", z);
  /* sign + or -, left justified */
  check_sprintf ("+1.1100101011001101p+9   ", "%+-25Rb", x);
  check_sprintf ("+0p+0                    ", "%+-25Rb", z);
  /* sign or space */
  check_sprintf (" 1.110p+9",  "% .3RNb", x);
  check_sprintf (" 1.1101p+9", "% .4RNb", x);
  check_sprintf (" 0.0000p+0", "% .4RNb", z);
  /* sign + or -, decimal point, pad with leading zeros */
  check_sprintf ("+00001.1p+9", "%0+#11.1RZb", x);
  check_sprintf ("+0001.0p+10", "%0+#11.1RNb", x);
  check_sprintf ("+000000.p+0", "%0+#11.0RNb", z);
  /* pad with leading zero */
  check_sprintf ("00001.1100101011001101p+9", "%025RDb", x);
  /* sign or space, decimal point (unused), left justified */
  check_sprintf (" 1.1p+9    ", "%- #11.1RDb", x);
  check_sprintf (" 1.p+9     ", "%- #11.0RDb", x);
  check_sprintf (" 1.p+10    ", "%- #11.0RUb", x);
  check_sprintf (" 1.p+9     ", "%- #11.0RZb", x);
  check_sprintf (" 1.p+10    ", "%- #11.0RYb", x);
  check_sprintf (" 1.p+10    ", "%- #11.0RNb", x);

  mpfr_mul_si (x, x, -1, MPFR_RNDD);
  mpfr_mul_si (z, z, -1, MPFR_RNDD);

  /* sign + or - */
  check_sprintf ("   -1.1p+9", "%+10.1RUb", x);
  check_sprintf ("   -0.0p+0", "%+10.1RUb", z);

  /* precision 0 */
  check_sprintf ("-1p+10", "%.0RNb", x);
  check_sprintf ("-1p+10", "%.0RDb", x);
  check_sprintf ("-1p+9",  "%.0RUb", x);
  check_sprintf ("-1p+9",  "%.0RZb", x);
  check_sprintf ("-1p+10", "%.0RYb", x);
  /* round to next base power */
  check_sprintf ("-1.0p+10", "%.1RNb", x);
  check_sprintf ("-1.0p+10", "%.1RDb", x);
  check_sprintf ("-1.0p+10", "%.1RYb", x);
  /* do not round to next base power */
  check_sprintf ("-1.1p+9", "%.1RUb", x);
  check_sprintf ("-1.1p+9", "%.1RZb", x);
  /* rounding bit is zero */
  check_sprintf ("-1.11p+9", "%.2RNb", x);
  /* tie case in round to nearest mode */
  check_sprintf ("-1.1100101011001101p+9", "%.16RNb", x);
  /* trailing zeros in fractional part */
  check_sprintf ("-1.110010101100110100000000000000p+9", "%.30RNb", x);

  mpfr_clears (x, z, (mpfr_ptr) 0);
  return 0;
}

static int
mixed (void)
{
  int n1;
  int n2;
  int i = 121;
#ifdef PRINTF_L
  long double d = 1. / 31.;
#endif
  mpf_t mpf;
  mpq_t mpq;
  mpz_t mpz;
  mpfr_t x;
  mpfr_rnd_t rnd;
  int k;

  mpf_init (mpf);
  mpf_set_ui (mpf, 40);
  mpf_div_ui (mpf, mpf, 31); /* mpf = 40.0 / 31.0 */
  mpq_init (mpq);
  mpq_set_ui (mpq, 123456, 4567890);
  mpz_init (mpz);
  mpz_fib_ui (mpz, 64);
  mpfr_init (x);
  mpfr_set_str (x, "-12345678.875", 10, MPFR_RNDN);
  rnd = MPFR_RNDD;

  check_vsprintf ("121%", "%i%%", i);
  check_vsprintf ("121% -1.2345678875000000E+07", "%i%% %RNE", i, x);
  check_vsprintf ("121, -12345679", "%i, %.0Rf", i, x);
  check_vsprintf ("10610209857723, -1.2345678875000000e+07", "%Zi, %R*e", mpz, rnd,
                  x);
  check_vsprintf ("-12345678.9, 121", "%.1Rf, %i", x, i);
  check_vsprintf ("-12345678, 1e240/45b352", "%.0R*f, %Qx", MPFR_RNDZ, x, mpq);

  /* TODO: Systematically test with and without %n in check_vsprintf? */
  /* Do the test several times due to random parameters in check_vsprintf
     and the use of %n. In r11501, n2 is incorrect (seems random) when
     randsize <= 0, i.e. when the size argument of mpfr_vsnprintf is 0. */
  for (k = 0; k < 30; k++)
    {
      n2 = -17;
      /* If this value is obtained for n2 after the check_vsprintf call below,
         this probably means that n2 has not been written as expected. */
      n1 = check_vsprintf ("121, -12345678.875000000000, 1.290323",
                           "%i, %.*Rf, %Ff%n", i, 12, x, mpf, &n2);
      if (n1 != n2)
        {
          printf ("error in number of characters written by mpfr_vsprintf"
                  " for k = %d, randsize = %d\n", k, randsize);
          printf ("expected: %d\n", n2);
          printf ("     got: %d\n", n1);
          exit (1);
        }
    }

#ifdef PRINTF_L
  /* under MinGW, -D__USE_MINGW_ANSI_STDIO is required to support %Lf
     see https://gcc.gnu.org/legacy-ml/gcc/2013-03/msg00103.html */
  check_vsprintf ("00000010610209857723, -1.2345678875000000e+07, 0.032258",
                  "%.*Zi, %R*e, %Lf", 20, mpz, rnd, x, d);
#endif

  /* check invalid spec.spec */
  check_vsprintf ("%,", "%,");
  check_vsprintf ("%3*Rg", "%3*Rg");

  /* check empty format */
  check_vsprintf ("%", "%");

  mpf_clear (mpf);
  mpq_clear (mpq);
  mpz_clear (mpz);
  mpfr_clear (x);
  return 0;
}

#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && MPFR_LCONV_DPTS

/* Check with locale "da_DK". On most platforms, decimal point is ','
   and thousands separator is '.'; the test is not performed if this
   is not the case or if the locale doesn't exist. */
static void
locale_da_DK (void)
{
  mpfr_prec_t p = 128;
  mpfr_t x, y;

  if (setlocale (LC_ALL, "da_DK") == 0 ||
      localeconv()->decimal_point[0] != ',' ||
      localeconv()->thousands_sep[0] != '.')
    {
      setlocale (LC_ALL, "C");

      if (getenv ("MPFR_CHECK_LOCALES") == NULL)
        return;

      fprintf (stderr,
               "Cannot test the da_DK locale (not found or inconsistent).\n");
      exit (1);
    }

  mpfr_init2 (x, p);

  /* positive numbers */
  mpfr_set_str (x, "18993474.61279296875", 10, MPFR_RNDN);
  mpfr_init2 (y, 59);
  mpfr_set (y, x, MPFR_RNDN);

  /* simplest case right justified with thousands separator */
  check_sprintf ("1,899347461279296875000000000000000000000e+07", "%'30Re", x);
  check_sprintf ("      1,899347461279296875e+07", "%'30Re", y);
  check_sprintf ("                   1,89935e+07", "%'30Rg", x);
  check_sprintf ("        18.993.474,61279296875", "%'30.19Rg", x);
  check_sprintf ("             18.993.474,612793", "%'30Rf", x);

  /* sign or space, pad, thousands separator with leading zeros */
  check_sprintf (" 1,899347461279296875000000000000000000000E+07", "%' 030RE", x);
  check_sprintf (" 000001,899347461279296875E+07", "%' 030RE", y);
  check_sprintf (" 0000000000000000001,89935E+07", "%' 030RG", x);
  check_sprintf (" 000000018.993.474,61279296875", "%' 030.19RG", x);
  check_sprintf (" 00000000000018.993.474,612793", "%' 030RF", x);

#define T1 "000"
#define T2 ".000"
#define S1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1
#define S2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 ","

  mpfr_set_ui (x, 48, MPFR_RNDN);
  mpfr_exp10 (x, x, MPFR_RNDN);
  check_sprintf ("1" S1, "%.0Rf", x);
  check_sprintf ("1" S2, "%'#.0Rf", x);
  check_sprintf ("1" S2 "0000", "%'.4Rf", x);
  mpfr_mul_ui (x, x, 10, MPFR_RNDN);
  check_sprintf ("10" S1, "%.0Rf", x);
  check_sprintf ("10" S2, "%'#.0Rf", x);
  check_sprintf ("10" S2 "0000", "%'.4Rf", x);
  mpfr_mul_ui (x, x, 10, MPFR_RNDN);
  check_sprintf ("100" S1, "%.0Rf", x);
  check_sprintf ("100" S2, "%'#.0Rf", x);
  check_sprintf ("100" S2 "0000", "%'.4Rf", x);

  mpfr_clear (x);
  mpfr_clear (y);

  setlocale (LC_ALL, "C");
}

#endif  /* ... && MPFR_LCONV_DPTS */

/* check concordance between mpfr_asprintf result with a regular mpfr float
   and with a regular double float */
static int
random_double (void)
{
  mpfr_t x; /* random regular mpfr float */
  double y; /* regular double float (equal to x) */

  char flag[] =
    {
      '-',
      '+',
      ' ',
      '#',
      '0', /* no ambiguity: first zeros are flag zero */
      '\'' /* SUS extension */
    };
  /* no 'a': mpfr and glibc do not have the same semantic */
  char specifier[] =
    {
      'e',
      'f',
      'g',
      'E',
      'f', /* SUSv2 doesn't accept %F, but %F and %f are the same for
              regular numbers */
      'G',
    };
  int spec; /* random index in specifier[] */
  int prec; /* random value for precision field */

  /* in the format string for mpfr_t variable, the maximum length is
     reached by something like "%-+ #0'.*Rf", that is 12 characters. */
#define FMT_MPFR_SIZE 12
  char fmt_mpfr[FMT_MPFR_SIZE];
  char *ptr_mpfr;

  /* in the format string for double variable, the maximum length is
     reached by something like "%-+ #0'.*f", that is 11 characters. */
#define FMT_SIZE 11
  char fmt[FMT_SIZE];
  char *ptr;

  int xi;
  char *xs;
  int yi;
  char *ys;

  int i, j, jmax;

  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);

  for (i = 0; i < 1000; ++i)
    {
      /* 1. random double */
      do
        {
          y = DBL_RAND ();
        }
#ifdef HAVE_SUBNORM_DBL
      while (0);
#else
      while (ABS(y) < DBL_MIN);
#endif

      if (randlimb () % 2 == 0)
        y = -y;

      mpfr_set_d (x, y, MPFR_RNDN);
      if (y != mpfr_get_d (x, MPFR_RNDN))
        /* conversion error: skip this one */
        continue;

      /* 2. build random format strings fmt_mpfr and fmt */
      ptr_mpfr = fmt_mpfr;
      ptr = fmt;
      *ptr_mpfr++ = *ptr++ = '%';
      /* random specifier 'e', 'f', 'g', 'E', 'F', or 'G' */
      spec = (int) (randlimb() % 6);
      /* random flags, but no ' flag with %e or with non-glibc */
#if __MPFR_GLIBC(1,0)
      jmax = (spec == 0 || spec == 3) ? 5 : 6;
#else
      jmax = 5;
#endif
      for (j = 0; j < jmax; j++)
        {
          if (randlimb() % 3 == 0)
            *ptr_mpfr++ = *ptr++ = flag[j];
        }
      *ptr_mpfr++ = *ptr++ = '.';
      *ptr_mpfr++ = *ptr++ = '*';
      *ptr_mpfr++ = 'R';
      *ptr_mpfr++ = *ptr++ = specifier[spec];
      *ptr_mpfr = *ptr = '\0';
      MPFR_ASSERTN (ptr - fmt < FMT_SIZE);
      MPFR_ASSERTN (ptr_mpfr - fmt_mpfr < FMT_MPFR_SIZE);

      /* advantage small precision */
      if (randlimb() % 2 == 0)
        prec = (int) (randlimb() % 10);
      else
        prec = (int) (randlimb() % prec_max_printf);

      /* 3. calls and checks */
      /* the double float case is handled by the libc asprintf through
         gmp_asprintf */
      xi = mpfr_asprintf (&xs, fmt_mpfr, prec, x);
      yi = mpfr_asprintf (&ys, fmt, prec, y);

      /* test if XS and YS differ, beware that ISO C99 doesn't specify
         the sign of a zero exponent (the C99 rationale says: "The sign
         of a zero exponent in %e format is unspecified.  The committee
         knows of different implementations and choose not to require
         implementations to document their behavior in this case
         (by making this be implementation defined behaviour).  Most
         implementations use a "+" sign, e.g., 1.2e+00; but there is at
         least one implementation that uses the sign of the unlimited
         precision result, e.g., the 0.987 would be 9.87e-01, so could
         end up as 1e-00 after rounding to one digit of precision."),
         while mpfr always uses '+' */
      if (xi != yi
          || ((strcmp (xs, ys) != 0)
              && (spec == 1 || spec == 4
                  || ((strstr (xs, "e+00") == NULL
                       || strstr (ys, "e-00") == NULL)
                      && (strstr (xs, "E+00") == NULL
                          || strstr (ys, "E-00") == NULL)))))
        {
          mpfr_printf ("Error in mpfr_asprintf(\"%s\", %d, %Re)\n",
                       fmt_mpfr, prec, x);
          printf ("expected: %s\n", ys);
          printf ("     got: %s\n", xs);
          printf ("xi=%d yi=%d spec=%d\n", xi, yi, spec);

          exit (1);
        }

      mpfr_free_str (xs);
      mpfr_free_str (ys);
    }

  mpfr_clear (x);
  return 0;
}

static void
bug20080610 (void)
{
  /* bug on icc found on June 10, 2008 */
  /* this is not a bug but a different implementation choice: ISO C99 doesn't
     specify the sign of a zero exponent (see note in random_double above). */
  mpfr_t x;
  double y;
  int xi;
  char *xs;
  int yi;
  char *ys;

  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);

  y = -9.95645044213728791504536275169812142849e-01;
  mpfr_set_d (x, y, MPFR_RNDN);

  xi = mpfr_asprintf (&xs, "%- #0.*Re", 1, x);
  yi = mpfr_asprintf (&ys, "%- #0.*e", 1, y);

  if (xi != yi || strcmp (xs, ys) != 0)
    {
      printf ("Error in bug20080610\n");
      printf ("expected: %s\n", ys);
      printf ("     got: %s\n", xs);
      printf ("xi=%d yi=%d\n", xi, yi);

      exit (1);
    }

  mpfr_free_str (xs);
  mpfr_free_str (ys);
  mpfr_clear (x);
}

static void
bug20081214 (void)
{
 /* problem with glibc 2.3.6, December 14, 2008:
    the system asprintf outputs "-1.0" instead of "-1.". */
  mpfr_t x;
  double y;
  int xi;
  char *xs;
  int yi;
  char *ys;

  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);

  y = -9.90597761233942053494e-01;
  mpfr_set_d (x, y, MPFR_RNDN);

  xi = mpfr_asprintf (&xs, "%- #0.*RG", 1, x);
  yi = mpfr_asprintf (&ys, "%- #0.*G", 1, y);

  if (xi != yi || strcmp (xs, ys) != 0)
    {
      mpfr_printf ("Error in bug20081214\n"
                   "mpfr_asprintf(\"%- #0.*Re\", 1, %Re)\n", x);
      printf ("expected: %s\n", ys);
      printf ("     got: %s\n", xs);
      printf ("xi=%d yi=%d\n", xi, yi);

      exit (1);
    }

  mpfr_free_str (xs);
  mpfr_free_str (ys);
  mpfr_clear (x);
}

static void
bug20111102 (void)
{
  mpfr_t t;
  char s[100];

  mpfr_init2 (t, 84);
  mpfr_set_str (t, "999.99999999999999999999", 10, MPFR_RNDN);
  mpfr_sprintf (s, "%.20RNg", t);
  if (strcmp (s, "1000") != 0)
    {
      printf ("Error in bug20111102, expected 1000, got %s\n", s);
      exit (1);
    }
  mpfr_clear (t);
}

/* In particular, the following test makes sure that the rounding
 * for %Ra and %Rb is not done on the MPFR number itself (as it
 * would overflow). Note: it has been reported on comp.std.c that
 * some C libraries behave differently on %a, but this is a bug.
 */
static void
check_emax_aux (mpfr_exp_t e)
{
  mpfr_t x;
  char *s1, s2[256];
  int i;
  mpfr_exp_t emax;

  MPFR_ASSERTN (e <= LONG_MAX);
  emax = mpfr_get_emax ();
  set_emax (e);

  mpfr_init2 (x, 16);

  mpfr_set_inf (x, 1);
  mpfr_nextbelow (x);

  i = mpfr_asprintf (&s1, "%Ra %.2Ra", x, x);
  MPFR_ASSERTN (i > 0);

  mpfr_snprintf (s2, 256, "0x7.fff8p+%ld 0x8.00p+%ld", e-3, e-3);

  if (strcmp (s1, s2) != 0)
    {
      printf ("Error in check_emax_aux for emax = ");
      if (e > LONG_MAX)
        printf ("(>LONG_MAX)\n");
      else
        printf ("%ld\n", (long) e);
      printf ("Expected '%s'\n", s2);
      printf ("Got      '%s'\n", s1);
      exit (1);
    }

  mpfr_free_str (s1);

  i = mpfr_asprintf (&s1, "%Rb %.2Rb", x, x);
  MPFR_ASSERTN (i > 0);

  mpfr_snprintf (s2, 256, "1.111111111111111p+%ld 1.00p+%ld", e-1, e);

  if (strcmp (s1, s2) != 0)
    {
      printf ("Error in check_emax_aux for emax = ");
      if (e > LONG_MAX)
        printf ("(>LONG_MAX)\n");
      else
        printf ("%ld\n", (long) e);
      printf ("Expected %s\n", s2);
      printf ("Got      %s\n", s1);
      exit (1);
    }

  mpfr_free_str (s1);

  mpfr_clear (x);
  set_emax (emax);
}

static void
check_emax (void)
{
  check_emax_aux (15);
  check_emax_aux (MPFR_EMAX_MAX);
}

static void
check_emin_aux (mpfr_exp_t e)
{
  mpfr_t x;
  char *s1, s2[256];
  int i;
  mpfr_exp_t emin;
  mpz_t ee;

  MPFR_ASSERTN (e >= LONG_MIN);
  emin = mpfr_get_emin ();
  set_emin (e);

  mpfr_init2 (x, 16);
  mpz_init (ee);

  mpfr_setmin (x, e);
  mpz_set_si (ee, e);
  mpz_sub_ui (ee, ee, 1);

  i = mpfr_asprintf (&s1, "%Ra", x);
  MPFR_ASSERTN (i > 0);

  gmp_snprintf (s2, 256, "0x1p%Zd", ee);

  if (strcmp (s1, s2) != 0)
    {
      printf ("Error in check_emin_aux for emin = %ld\n", (long) e);
      printf ("Expected %s\n", s2);
      printf ("Got      %s\n", s1);
      exit (1);
    }

  mpfr_free_str (s1);

  i = mpfr_asprintf (&s1, "%Rb", x);
  MPFR_ASSERTN (i > 0);

  gmp_snprintf (s2, 256, "1p%Zd", ee);

  if (strcmp (s1, s2) != 0)
    {
      printf ("Error in check_emin_aux for emin = %ld\n", (long) e);
      printf ("Expected %s\n", s2);
      printf ("Got      %s\n", s1);
      exit (1);
    }

  mpfr_free_str (s1);

  mpfr_clear (x);
  mpz_clear (ee);
  set_emin (emin);
}

static void
check_emin (void)
{
  check_emin_aux (-15);
  check_emin_aux (mpfr_get_emin ());
  check_emin_aux (MPFR_EMIN_MIN);
}

static void
test20161214 (void)
{
  mpfr_t x;
  char buf[32];
  const char s[] = "0x0.fffffffffffff8p+1024";
  int r;

  mpfr_init2 (x, 64);
  mpfr_set_str (x, s, 16, MPFR_RNDN);
  r = mpfr_snprintf (buf, 32, "%.*RDf", -2, x);
  MPFR_ASSERTN(r == 316);
  r = mpfr_snprintf (buf, 32, "%.*RDf", INT_MIN + 1, x);
  MPFR_ASSERTN(r == 316);
  r = mpfr_snprintf (buf, 32, "%.*RDf", INT_MIN, x);
  MPFR_ASSERTN(r == 316);
  mpfr_clear (x);
}

/* http://gforge.inria.fr/tracker/index.php?func=detail&aid=21056 */
static void
bug21056 (void)
{
  mpfr_t x;
  const char s[] = "0x0.fffffffffffff8p+1024";
  int ndigits, r;

  mpfr_init2 (x, 64);

  mpfr_set_str (x, s, 16, MPFR_RNDN);

  ndigits = 1000;
  r = mpfr_snprintf (0, 0, "%.*RDf", ndigits, x);
  /* the return value should be ndigits + 310 */
  MPFR_ASSERTN(r == ndigits + 310);

  ndigits = INT_MAX - 310;
  r = mpfr_snprintf (0, 0, "%.*RDf", ndigits, x);
  MPFR_ASSERTN(r == INT_MAX);

  ndigits = INT_MAX - 10;
  r = mpfr_snprintf (0, 0, "%.*RDa", ndigits, x);
  MPFR_ASSERTN(r == INT_MAX);

  ndigits = INT_MAX - 7;
  r = mpfr_snprintf (0, 0, "%.*RDe", ndigits, x);
  MPFR_ASSERTN(r == INT_MAX);

  ndigits = 1000;
  r = mpfr_snprintf (0, 0, "%.*RDg", ndigits, x);
  /* since trailing zeros are removed with %g, we get less digits */
  MPFR_ASSERTN(r == 309);

  ndigits = INT_MAX;
  r = mpfr_snprintf (0, 0, "%.*RDg", ndigits, x);
  /* since trailing zeros are removed with %g, we get less digits */
  MPFR_ASSERTN(r == 309);

  ndigits = INT_MAX - 1;
  r = mpfr_snprintf (0, 0, "%#.*RDg", ndigits, x);
  MPFR_ASSERTN(r == ndigits + 1);

  mpfr_clear (x);
}

/* Fails for i = 5, i.e. t[i] = (size_t) UINT_MAX + 1,
   with r11427 on 64-bit machines (4-byte int, 8-byte size_t).
   On such machines, t[5] converted to int typically gives 0.
   Note: the assumed behavior corresponds to the snprintf behavior
   in ISO C, but this conflicts with POSIX:
     https://sourceware.org/bugzilla/show_bug.cgi?id=14771#c2
     https://austingroupbugs.net/view.php?id=761
     https://austingroupbugs.net/view.php?id=1219
     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87096
   Fixed in r11429.
*/
static void
snprintf_size (void)
{
  mpfr_t x;
  char buf[12];
  const char s[] = "17.00000000";
  size_t t[] = { 11, 12, 64, INT_MAX, (size_t) INT_MAX + 1,
                 (size_t) UINT_MAX + 1, (size_t) UINT_MAX + 2,
                 (size_t) -1 };
  int i, r;

  mpfr_init2 (x, 64);
  mpfr_set_ui (x, 17, MPFR_RNDN);

  for (i = 0; i < sizeof (t) / sizeof (*t); i++)
    {
      memset (buf, 0, sizeof (buf));
      /* r = snprintf (buf, t[i], "%.8f", 17.0); */
      r = mpfr_snprintf (buf, t[i], "%.8Rf", x);
      if (r != 11 || (t[i] > 11 && strcmp (buf, s) != 0))
        {
          printf ("Error in snprintf_size for i = %d:\n", i);
          printf ("expected r = 11, \"%s\"\n", s);
          printf ("got      r = %d, \"%s\"\n", r, buf);
          exit (1);
        }
    }

  mpfr_clear (x);
}

/* With r11516, n2 gets a random value for i = 0 only!
   valgrind detects a problem for "nchar = buf.curr - buf.start;"
   in the spec.spec == 'n' case. Indeed, there is no buffer when
   size is 0. */
static void
percent_n (void)
{
  int err = 0, i, j;

  for (i = 0; i < 24; i++)
    for (j = 0; j < 3; j++)
      {
        volatile int n1, n2;
        char buffer[64];

        memset (buffer, 0, 64);
        n2 = -17;
        n1 = mpfr_snprintf (buffer, i % 8, "%d%n", 123, &n2);
        if (n1 != 3 || n2 != 3)
          {
            printf ("Error 1 in percent_n: i = %d, n1 = %d, n2 = %d\n",
                    i, n1, n2);
            err = 1;
          }
      }

  if (err)
    exit (1);
}

struct clo
{
  const char *fmt;
  int width, r, e;
};

static void
check_length_overflow (void)
{
  mpfr_t x;
  int i, r, e;
  struct clo t[] = {
    { "%Rg", 0, 1, 0 },
    { "%*Rg", 1, 1, 0 },
    { "%*Rg", -1, 1, 0 },
    { "%5Rg", 0, 5, 0 },
    { "%*Rg", 5, 5, 0 },
    { "%*Rg", -5, 5, 0 },
#if INT_MAX == 2147483647
    { "%2147483647Rg", 0, 2147483647, 0 },
    { "%2147483647Rg ", 0, -1, 1 },
    { "%2147483648Rg", 0, -1, 1 },
    { "%18446744073709551616Rg", 0, -1, 1 },
    { "%*Rg", 2147483647, 2147483647, 0 },
    { "%*Rg", -2147483647, 2147483647, 0 },
# if INT_MIN < -INT_MAX
    { "%*Rg", INT_MIN, -1, 1 },
# endif
#endif
  };

  mpfr_init2 (x, MPFR_PREC_MIN);
  mpfr_set_ui (x, 0, MPFR_RNDN);

  for (i = 0; i < numberof (t); i++)
    {
      errno = 0;
      r = t[i].width == 0 ?
        mpfr_snprintf (NULL, 0, t[i].fmt, x) :
        mpfr_snprintf (NULL, 0, t[i].fmt, t[i].width, x);
      e = errno;
      if ((t[i].r < 0 ? r >= 0 : r != t[i].r)
#ifdef EOVERFLOW
          || (t[i].e && e != EOVERFLOW)
#endif
          )
        {
          printf ("Error in check_length_overflow for i=%d (%s %d)\n",
                  i, t[i].fmt, t[i].width);
          printf ("Expected r=%d, got r=%d\n", t[i].r, r);
#ifdef EOVERFLOW
          if (t[i].e && e != EOVERFLOW)
            printf ("Expected errno=EOVERFLOW=%d, got errno=%d\n",
                    EOVERFLOW, e);
#endif
          exit (1);
        }
    }

  mpfr_clear (x);
}

#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)

/* The following tests should be equivalent to those from test_locale()
   in tprintf.c (remove the \n at the end of the test strings). */

static void
test_locale (void)
{
  const char * const tab_locale[] = {
    "en_US",
    "en_US.iso88591",
    "en_US.iso885915",
    "en_US.utf8"
  };
  int i;
  mpfr_t x;
  char v[] = "99999999999999999999999.5";

  for (i = 0; i < numberof(tab_locale); i++)
    {
      char *s;

      s = setlocale (LC_ALL, tab_locale[i]);

      if (s != NULL && MPFR_THOUSANDS_SEPARATOR == ',')
        break;
    }

  if (i == numberof(tab_locale))
    {
      if (getenv ("MPFR_CHECK_LOCALES") == NULL)
        return;

      fprintf (stderr, "Cannot find a locale with ',' thousands separator.\n"
               "Please install one of the en_US based locales.\n");
      exit (1);
    }

  mpfr_init2 (x, 113);
  mpfr_set_ui (x, 10000, MPFR_RNDN);

  check_sprintf ("(1) 10000=10,000 ", "(1) 10000=%'Rg ", x);
  check_sprintf ("(2) 10000=10,000.000000 ", "(2) 10000=%'Rf ", x);

  mpfr_set_ui (x, 1000, MPFR_RNDN);
  check_sprintf ("(3) 1000=1,000.000000 ", "(3) 1000=%'Rf ", x);

  for (i = 1; i <= sizeof (v) - 3; i++)
    {
      char buf[64];
      int j;

      strcpy (buf, "(4) 10^i=1");
      for (j = i; j > 0; j--)
        strcat (buf, ",0" + (j % 3 != 0));
      strcat (buf, " ");
      mpfr_set_str (x, v + sizeof (v) - 3 - i, 10, MPFR_RNDN);
      check_sprintf (buf, "(4) 10^i=%'.0Rf ", x);
    }

#define N0 20

  for (i = 1; i <= N0; i++)
    {
      char s[N0+4], buf[64];
      int j;

      s[0] = '1';
      for (j = 1; j <= i; j++)
        s[j] = '0';
      s[i+1] = '\0';

      strcpy (buf, "(5) 10^i=1");
      for (j = i; j > 0; j--)
        strcat (buf, ",0" + (j % 3 != 0));
      strcat (buf, " ");

      mpfr_set_str (x, s, 10, MPFR_RNDN);

      check_sprintf (buf, "(5) 10^i=%'.0RNf ", x);
      check_sprintf (buf, "(5) 10^i=%'.0RZf ", x);
      check_sprintf (buf, "(5) 10^i=%'.0RUf ", x);
      check_sprintf (buf, "(5) 10^i=%'.0RDf ", x);
      check_sprintf (buf, "(5) 10^i=%'.0RYf ", x);

      strcat (s + (i + 1), ".5");
      check_sprintf (buf, "(5) 10^i=%'.0Rf ", x);
    }

  mpfr_set_str (x, "1000", 10, MPFR_RNDN);
  check_sprintf ("00000001e+03", "%'012.3Rg", x);
  check_sprintf ("00000001,000", "%'012.4Rg", x);
  check_sprintf ("000000001,000", "%'013.4Rg", x);

#ifdef PRINTF_GROUPFLAG
  check_vsprintf ("+01,234,567  :", "%0+ -'13.10Pd:", (mpfr_prec_t) 1234567);
#endif

  mpfr_clear (x);
}

#else

static void
test_locale (void)
{
  if (getenv ("MPFR_CHECK_LOCALES") != NULL)
    {
      fprintf (stderr, "Cannot test locales.\n");
      exit (1);
    }
}

#endif

int
main (int argc, char **argv)
{
  int k;

  tests_start_mpfr ();

#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE)
  /* currently, we just check with 'C' locale */
  setlocale (LC_ALL, "C");
#endif

  bug20111102 ();

  for (k = 0; k < 40; k++)
    {
      native_types ();
      hexadecimal ();
      binary ();
      decimal ();

#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && MPFR_LCONV_DPTS
      locale_da_DK ();
#else
      if (getenv ("MPFR_CHECK_LOCALES") != NULL)
        {
          fprintf (stderr, "Cannot test locales.\n");
          exit (1);
        }
#endif
    }

  check_emax ();
  check_emin ();
  test20161214 ();
  bug21056 ();
  snprintf_size ();
  percent_n ();
  mixed ();
  check_length_overflow ();
  test_locale ();

  if (getenv ("MPFR_CHECK_LIBC_PRINTF"))
    {
      /* check against libc */
      random_double ();
      bug20081214 ();
      bug20080610 ();
    }

  tests_end_mpfr ();
  return 0;
}

#else  /* HAVE_STDARG */

int
main (void)
{
  /* We have nothing to test. */
  return 77;
}

#endif  /* HAVE_STDARG */
