1/* Test file for mpfr_fpif.
2
3Copyright 2012-2023 Free Software Foundation, Inc.
4Contributed by Olivier Demengeon.
5
6This file is part of the GNU MPFR Library.
7
8The GNU MPFR Library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as published by
10the Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13The GNU MPFR Library is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16License for more details.
17
18You should have received a copy of the GNU Lesser General Public License
19along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23#include <errno.h>
24
25#include "mpfr-test.h"
26
27#define FILE_NAME_RW "tfpif_rw.dat" /* temporary name (written then read) */
28#define FILE_NAME_R  "tfpif_r1.dat" /* fixed file name (read only) */
29#define FILE_NAME_R2 "tfpif_r2.dat" /* fixed file name (read only) with a
30                                       precision > MPFR_PREC_MAX */
31
32/* Note: The perror below must be called just after the failing function,
33   thus before fprintf (otherwise one could get an error associated with
34   fprintf). */
35
36static void
37doit (int argc, char *argv[], mpfr_prec_t p1, mpfr_prec_t p2)
38{
39  const char *filenameCompressed = FILE_NAME_RW;
40  const char *data = FILE_NAME_R;
41  int status;
42  FILE *fh;
43  mpfr_t x[9];
44  mpfr_t y;
45  int i, neg;
46  long pos;
47
48  mpfr_init2 (x[0], p1);
49  mpfr_init2 (x[8], p1);
50  mpfr_inits2 (p2, x[1], x[2], x[3], x[4], x[5], x[6], x[7], (mpfr_ptr) 0);
51  mpfr_set_str1 (x[0], "45.2564215000000018562786863185465335845947265625");
52  mpfr_set_str1 (x[1], "45.2564215000000018562786863185465335845947265625");
53  mpfr_set_str1 (x[2], "45.2564215000000018562786863185465335845947265625");
54  mpfr_set_exp (x[2], -48000);
55  mpfr_set_inf (x[3], 1);
56  mpfr_set_zero (x[4], 1);
57  mpfr_set_nan (x[5]);
58  mpfr_set_ui (x[6], 104348, MPFR_RNDN);
59  mpfr_set_ui (x[7], 33215, MPFR_RNDN);
60  mpfr_div (x[8], x[6], x[7], MPFR_RNDN);
61  mpfr_div (x[6], x[6], x[7], MPFR_RNDN);
62
63  /* we first write to file FILE_NAME_RW the numbers x[i] */
64  fh = fopen (filenameCompressed, "w");
65  if (fh == NULL)
66    {
67      perror ("doit");
68      fprintf (stderr, "Failed to open \"%s\" for writing\n",
69               filenameCompressed);
70      exit (1);
71    }
72
73  for (neg = 0; neg < 2; neg++)
74    for (i = 0; i < 9; i++)
75      {
76        if (neg)
77          MPFR_CHANGE_SIGN (x[i]);
78
79        status = mpfr_fpif_export (fh, x[i]);
80        if (status != 0)
81          {
82            fclose (fh);
83            printf ("Failed to export number %d, neg=%d\n", i, neg);
84            exit (1);
85          }
86
87        if (neg)
88          MPFR_CHANGE_SIGN (x[i]);
89      }
90
91  if (fclose (fh) != 0)
92    {
93      perror ("doit");
94      fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
95      exit (1);
96    }
97
98  /* we then read back FILE_NAME_RW and check we get the same numbers x[i] */
99  fh = fopen (filenameCompressed, "r");
100  if (fh == NULL)
101    {
102      perror ("doit");
103      fprintf (stderr, "Failed to open \"%s\" for reading\n",
104               filenameCompressed);
105      exit (1);
106    }
107
108  for (neg = 0; neg < 2; neg++)
109    for (i = 0; i < 9; i++)
110      {
111        mpfr_prec_t px, py;
112
113        if (neg)
114          MPFR_CHANGE_SIGN (x[i]);
115
116        mpfr_init2 (y, 2);
117        /* Set the sign bit of y to the opposite of the expected one.
118           Thus, if mpfr_fpif_import forgets to set the sign, this will
119           be detected. */
120        MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
121        mpfr_fpif_import (y, fh);
122        px = mpfr_get_prec (x[i]);
123        py = mpfr_get_prec (y);
124        if (px != py)
125          {
126            printf ("doit failed on written number %d, neg=%d:"
127                    " bad precision\n", i, neg);
128            printf ("expected %ld\n", (long) px);
129            printf ("got      %ld\n", (long) py);
130            exit (1);
131          }
132        if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
133          {
134            printf ("doit failed on written number %d, neg=%d:"
135                    " bad sign\n", i, neg);
136            printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
137            printf ("got      %d\n", (int) MPFR_SIGN (y));
138            exit (1);
139          }
140        if (! SAME_VAL (x[i], y))
141          {
142            printf ("doit failed on written number %d, neg=%d\n", i, neg);
143            printf ("expected "); mpfr_dump (x[i]);
144            printf ("got      "); mpfr_dump (y);
145            exit (1);
146          }
147        mpfr_clear (y);
148
149        if (neg)
150          MPFR_CHANGE_SIGN (x[i]);
151      }
152  fclose (fh);
153
154  /* we do the same for the fixed file FILE_NAME_R, this ensures
155     we get same results with different word size or endianness */
156  fh = src_fopen (data, "r");
157  if (fh == NULL)
158    {
159      perror ("doit");
160      fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
161      exit (1);
162    }
163
164  /* the fixed file FILE_NAME_R assumes p1=130 and p2=2048 */
165  for (i = 0; i < 9 && (p1 == 130 && p2 == 2048); i++)
166    {
167      mpfr_prec_t px, py;
168
169      mpfr_init2 (y, 2);
170      /* Set the sign bit of y to the opposite of the expected one.
171         Thus, if mpfr_fpif_import forgets to set the sign, this will
172         be detected. */
173      MPFR_SET_SIGN (y, - MPFR_SIGN (x[i]));
174      pos = ftell (fh);
175      mpfr_fpif_import (y, fh);
176      px = mpfr_get_prec (x[i]);
177      py = mpfr_get_prec (y);
178      if (px != py)
179        {
180          printf ("doit failed on data number %d, neg=%d:"
181                  " bad precision\n", i, neg);
182          printf ("expected %ld\n", (long) px);
183          printf ("got      %ld\n", (long) py);
184          exit (1);
185        }
186      if (MPFR_SIGN (x[i]) != MPFR_SIGN (y))
187        {
188          printf ("doit failed on data number %d, neg=%d:"
189                  " bad sign\n", i, neg);
190          printf ("expected %d\n", (int) MPFR_SIGN (x[i]));
191          printf ("got      %d\n", (int) MPFR_SIGN (y));
192          exit (1);
193        }
194      if (! SAME_VAL (x[i], y))
195        {
196          printf ("doit failed on data number %d, neg=%d, at offset 0x%lx\n",
197                  i, neg, (unsigned long) pos);
198          printf ("expected "); mpfr_dump (x[i]);
199          printf ("got      "); mpfr_dump (y);
200          exit (1);
201        }
202      mpfr_clear (y);
203    }
204  fclose (fh);
205
206  for (i = 0; i < 9; i++)
207    mpfr_clear (x[i]);
208
209  remove (filenameCompressed);
210}
211
212#define BAD 10
213
214static void
215check_bad (void)
216{
217  const char *filenameCompressed = FILE_NAME_RW;
218  int status;
219  FILE *fh;
220  mpfr_t x;
221  unsigned char badData[BAD][10] =
222    { { 7 }, { 16 }, { 23, 118 }, { 23, 95 }, { 23, 127 }, { 23, 47 },
223      { 7, 0, 0, 0, 0, 0, 0, 0, 128, 119 }, /* +0 in a huge precision */
224      /* precision 8-7=1, exponent on 98-94=4 bytes */
225      { 8, 98, 255, 255, 255, 127 },
226      /* precision 8-7=1, exponent on 102-94=8 bytes */
227      { 8, 102, 255, 255, 255, 255, 255, 255, 255, 127 },
228      { 8, 94 }
229      };
230  int badDataSize[BAD] = { 1, 1, 2, 2, 2, 2, 10, 6, 10, 2 };
231  int i;
232
233  mpfr_init2 (x, 2);
234  status = mpfr_fpif_export (NULL, x);
235  if (status == 0)
236    {
237      printf ("mpfr_fpif_export did not fail with a NULL file\n");
238      exit (1);
239    }
240  status = mpfr_fpif_import (x, NULL);
241  if (status == 0)
242    {
243      printf ("mpfr_fpif_import did not fail with a NULL file\n");
244      exit (1);
245    }
246
247  /* Since the file will be read after writing to it and a rewind, we need
248     to open it in mode "w+".
249     Note: mode "w" was used previously, and the issue remained undetected
250     until a test on AIX, where the fclose failed with the error:
251       check_bad: A file descriptor does not refer to an open file.
252     (the exit code of fclose has been checked since r13549 / 2019-08-09,
253     at the same time "w+" was changed to "w" by mistake).
254     What actually happened is that the fread in mpfr_fpif_import failed,
255     but this was not tested. So a test of errno has been added below;
256     with mode "w" (instead of "w+"), it yields:
257       check_bad: Bad file descriptor
258     as expected. */
259  fh = fopen (filenameCompressed, "w+");
260  if (fh == NULL)
261    {
262      perror ("check_bad");
263      fprintf (stderr, "Failed to open \"%s\" for writing\n",
264              filenameCompressed);
265      fclose (fh);
266      remove (filenameCompressed);
267      exit (1);
268    }
269  status = mpfr_fpif_import (x, fh);
270  if (status == 0)
271    {
272      printf ("mpfr_fpif_import did not fail on a empty file\n");
273      fclose (fh);
274      remove (filenameCompressed);
275      exit (1);
276    }
277
278  for (i = 0; i < BAD; i++)
279    {
280      mpfr_exp_t emax;
281      /* For i == 6, mpfr_prec_t needs at least a 65-bit precision
282         (64 value bits + 1 sign bit) to avoid a failure. */
283      if (i == 6 && MPFR_PREC_BITS > 64)
284        break;
285      /* For i=9, we use a reduced exponent range */
286      if (i == 9)
287        {
288          emax = mpfr_get_emax ();
289          set_emax (46);
290        }
291      rewind (fh);
292      status = fwrite (&badData[i][0], badDataSize[i], 1, fh);
293      if (status != 1)
294        {
295          printf ("Write error on the test file\n");
296          fclose (fh);
297          remove (filenameCompressed);
298          exit (1);
299        }
300      rewind (fh);
301      /* The check of errno below is needed to make sure that
302         mpfr_fpif_import fails due to bad data, not for some
303         arbitrary system error. */
304      errno = 0;
305      status = mpfr_fpif_import (x, fh);
306      if (errno != 0)
307        {
308          perror ("check_bad");
309          fprintf (stderr, "mpfr_fpif_import failed with unexpected"
310                   " errno = %d (and status = %d)\n", errno, status);
311          fclose (fh);
312          remove (filenameCompressed);
313          exit (1);
314        }
315      if (status == 0)
316        {
317          printf ("mpfr_fpif_import did not fail on a bad imported data\n");
318          switch (i)
319            {
320            case 0:
321              printf ("  not enough precision data\n");
322              break;
323            case 1:
324              printf ("  no exponent data\n");
325              break;
326            case 2:
327              printf ("  too big exponent\n");
328              break;
329            case 3:
330              printf ("  not enough exponent data\n");
331              break;
332            case 4:
333              printf ("  exponent data wrong\n");
334              break;
335            case 5:
336              printf ("  no limb data\n");
337              break;
338            case 6:
339              printf ("  too large precision\n");
340              break;
341            case 7:
342            case 8:
343            case 9:
344              printf ("  too large exponent\n");
345              break;
346            default:
347              printf ("Test fatal error, unknown case\n");
348              break;
349            }
350          fclose (fh);
351          remove (filenameCompressed);
352          exit (1);
353        }
354      if (i == 9)
355        set_emax (emax);
356    }
357
358  if (fclose (fh) != 0)
359    {
360      perror ("check_bad");
361      fprintf (stderr, "Failed to close \"%s\"\n", filenameCompressed);
362      exit (1);
363    }
364
365  mpfr_clear (x);
366
367  fh = fopen (filenameCompressed, "r");
368  if (fh == NULL)
369    {
370      perror ("check_bad");
371      fprintf (stderr, "Failed to open \"%s\" for reading\n",
372               filenameCompressed);
373      exit (1);
374    }
375
376  mpfr_init2 (x, 2);
377  status = mpfr_fpif_export (fh, x);
378  if (status == 0)
379    {
380      printf ("mpfr_fpif_export did not fail on a read only stream\n");
381      exit (1);
382    }
383  fclose (fh);
384  remove (filenameCompressed);
385  mpfr_clear (x);
386}
387
388/* exercise error when precision > MPFR_PREC_MAX */
389static void
390extra (void)
391{
392  const char *data = FILE_NAME_R2;
393  mpfr_t x;
394  FILE *fp;
395  int ret;
396
397  mpfr_init2 (x, 17);
398  mpfr_set_ui (x, 42, MPFR_RNDN);
399  fp = src_fopen (data, "r");
400  if (fp == NULL)
401    {
402      perror ("extra");
403      fprintf (stderr, "Failed to open \"%s\" in srcdir for reading\n", data);
404      exit (1);
405    }
406  ret = mpfr_fpif_import (x, fp);
407  MPFR_ASSERTN (ret != 0);  /* import failure */
408  MPFR_ASSERTN (mpfr_get_prec (x) == 17);  /* precision did not change */
409  MPFR_ASSERTN (mpfr_cmp_ui0 (x, 42) == 0);  /* value is still 42 */
410  fclose (fp);
411  mpfr_clear (x);
412}
413
414int
415main (int argc, char *argv[])
416{
417  if (argc != 1)
418    {
419      printf ("Usage: %s\n", argv[0]);
420      exit (1);
421    }
422
423  tests_start_mpfr ();
424
425  extra ();
426  doit (argc, argv, 130, 2048);
427  doit (argc, argv, 1, 53);
428  check_bad ();
429
430  tests_end_mpfr ();
431
432  return 0;
433}
434