1/* Test istream formatted input.
2
3Copyright 2001-2004 Free Software Foundation, Inc.
4
5This file is part of the GNU MP Library test suite.
6
7The GNU MP Library test suite is free software; you can redistribute it
8and/or modify it under the terms of the GNU General Public License as
9published by the Free Software Foundation; either version 3 of the License,
10or (at your option) any later version.
11
12The GNU MP Library test suite is distributed in the hope that it will be
13useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15Public License for more details.
16
17You should have received a copy of the GNU General Public License along with
18the GNU MP Library test suite.  If not, see https://www.gnu.org/licenses/.  */
19
20#include <iostream>
21#include <cstdlib>
22#include <cstring>
23
24#include "gmp-impl.h"
25#include "tests.h"
26
27using namespace std;
28
29
30// Under option_check_standard, the various test cases for mpz operator>>
31// are put through the standard operator>> for long, and likewise mpf
32// operator>> is put through double.
33//
34// In g++ 3.3 this results in some printouts about the final position
35// indicated for something like ".e123".  Our mpf code stops at the "e"
36// since there's no mantissa digits, but g++ reads the whole thing and only
37// then decides it's bad.
38
39bool option_check_standard = false;
40
41
42// On some versions of g++ 2.96 it's been observed that putback() may leave
43// tellg() unchanged.  We believe this is incorrect and presumably the
44// result of a bug, since for instance it's ok in g++ 2.95 and g++ 3.3.  We
45// detect the problem at runtime and disable affected checks.
46
47bool putback_tellg_works = true;
48
49void
50check_putback_tellg (void)
51{
52  istringstream input ("hello");
53  streampos  old_pos, new_pos;
54  char  c;
55
56  input.get(c);
57  old_pos = input.tellg();
58  input.putback(c);
59  new_pos = input.tellg();
60
61  if (old_pos == new_pos)
62    {
63      cout << "Warning, istringstream has a bug: putback() doesn't update tellg().\n";;
64      cout << "Tests on tellg() will be skipped.\n";
65      putback_tellg_works = false;
66    }
67}
68
69
70#define WRONG(str)                                              \
71  do {                                                          \
72    cout << str ", data[" << i << "]\n";                        \
73    cout << "  input: \"" << data[i].input << "\"\n";           \
74    cout << "  flags: " << hex << input.flags() << dec << "\n"; \
75  } while (0)
76
77void
78check_mpz (void)
79{
80  static const struct {
81    const char     *input;
82    int            want_pos;
83    const char     *want;
84    ios::fmtflags  flags;
85
86  } data[] = {
87
88    { "0",      -1, "0",    (ios::fmtflags) 0 },
89    { "123",    -1, "123",  (ios::fmtflags) 0 },
90    { "0123",   -1, "83",   (ios::fmtflags) 0 },
91    { "0x123",  -1, "291",  (ios::fmtflags) 0 },
92    { "-123",   -1, "-123", (ios::fmtflags) 0 },
93    { "-0123",  -1, "-83",  (ios::fmtflags) 0 },
94    { "-0x123", -1, "-291", (ios::fmtflags) 0 },
95    { "+123",   -1, "123", (ios::fmtflags) 0 },
96    { "+0123",  -1, "83",  (ios::fmtflags) 0 },
97    { "+0x123", -1, "291", (ios::fmtflags) 0 },
98
99    { "0",     -1, "0",    ios::dec },
100    { "1f",     1, "1",    ios::dec },
101    { "011f",   3, "11",   ios::dec },
102    { "123",   -1, "123",  ios::dec },
103    { "-1f",    2, "-1",   ios::dec },
104    { "-011f",  4, "-11",  ios::dec },
105    { "-123",  -1, "-123", ios::dec },
106    { "+1f",    2, "1",    ios::dec },
107    { "+011f",  4, "11",   ios::dec },
108    { "+123",  -1, "123",  ios::dec },
109
110    { "0",    -1, "0",   ios::oct },
111    { "123",  -1, "83",  ios::oct },
112    { "-123", -1, "-83", ios::oct },
113    { "+123", -1, "83",  ios::oct },
114
115    { "0",    -1, "0",    ios::hex },
116    { "123",  -1, "291",  ios::hex },
117    { "ff",   -1, "255",  ios::hex },
118    { "FF",   -1, "255",  ios::hex },
119    { "-123", -1, "-291", ios::hex },
120    { "-ff",  -1, "-255", ios::hex },
121    { "-FF",  -1, "-255", ios::hex },
122    { "+123", -1, "291",  ios::hex },
123    { "+ff",  -1, "255",  ios::hex },
124    { "+FF",  -1, "255",  ios::hex },
125    { "ab",   -1, "171",  ios::hex },
126    { "cd",   -1, "205",  ios::hex },
127    { "ef",   -1, "239",  ios::hex },
128
129    { " 123",  0, NULL,  (ios::fmtflags) 0 },   // not without skipws
130    { " 123", -1, "123", ios::skipws },
131  };
132
133  mpz_t      got, want;
134  bool       got_ok, want_ok;
135  bool       got_eof, want_eof;
136  long       got_si, want_si;
137  streampos  init_tellg, got_pos, want_pos;
138
139  mpz_init (got);
140  mpz_init (want);
141
142  for (size_t i = 0; i < numberof (data); i++)
143    {
144      size_t input_length = strlen (data[i].input);
145      want_pos = (data[i].want_pos == -1
146                  ? input_length : data[i].want_pos);
147      want_eof = (want_pos == streampos(input_length));
148
149      want_ok = (data[i].want != NULL);
150
151      if (data[i].want != NULL)
152        mpz_set_str_or_abort (want, data[i].want, 0);
153      else
154        mpz_set_ui (want, 0L);
155
156      if (option_check_standard && mpz_fits_slong_p (want))
157        {
158          istringstream  input (data[i].input);
159          input.flags (data[i].flags);
160          init_tellg = input.tellg();
161          want_si = mpz_get_si (want);
162
163          input >> got_si;
164          got_ok = !input.fail();
165          got_eof = input.eof();
166          input.clear();
167          got_pos = input.tellg() - init_tellg;
168
169          if (got_ok != want_ok)
170            {
171              WRONG ("stdc++ operator>> wrong status, check_mpz");
172              cout << "  want_ok: " << want_ok << "\n";
173              cout << "  got_ok:  " << got_ok << "\n";
174            }
175          if (want_ok && got_si != want_si)
176            {
177              WRONG ("stdc++ operator>> wrong result, check_mpz");
178              cout << "  got_si:  " << got_si << "\n";
179              cout << "  want_si: " << want_si << "\n";
180            }
181          if (want_ok && got_eof != want_eof)
182            {
183              WRONG ("stdc++ operator>> wrong EOF state, check_mpz");
184              cout << "  got_eof:  " << got_eof << "\n";
185              cout << "  want_eof: " << want_eof << "\n";
186            }
187          if (putback_tellg_works && got_pos != want_pos)
188            {
189              WRONG ("stdc++ operator>> wrong position, check_mpz");
190              cout << "  want_pos: " << want_pos << "\n";
191              cout << "  got_pos:  " << got_pos << "\n";
192            }
193        }
194
195      {
196        istringstream  input (data[i].input);
197        input.flags (data[i].flags);
198        init_tellg = input.tellg();
199
200        mpz_set_ui (got, 0xDEAD);
201        input >> got;
202        got_ok = !input.fail();
203	got_eof = input.eof();
204        input.clear();
205        got_pos = input.tellg() - init_tellg;
206
207        if (got_ok != want_ok)
208          {
209            WRONG ("mpz operator>> wrong status");
210            cout << "  want_ok: " << want_ok << "\n";
211            cout << "  got_ok:  " << got_ok << "\n";
212            abort ();
213          }
214        if (want_ok && mpz_cmp (got, want) != 0)
215          {
216            WRONG ("mpz operator>> wrong result");
217            mpz_trace ("  got ", got);
218            mpz_trace ("  want", want);
219            abort ();
220          }
221        if (want_ok && got_eof != want_eof)
222          {
223            WRONG ("mpz operator>> wrong EOF state");
224            cout << "  want_eof: " << want_eof << "\n";
225            cout << "  got_eof:  " << got_eof << "\n";
226            abort ();
227          }
228        if (putback_tellg_works && got_pos != want_pos)
229          {
230            WRONG ("mpz operator>> wrong position");
231            cout << "  want_pos: " << want_pos << "\n";
232            cout << "  got_pos:  " << got_pos << "\n";
233            abort ();
234          }
235      }
236    }
237
238  mpz_clear (got);
239  mpz_clear (want);
240}
241
242void
243check_mpq (void)
244{
245  static const struct {
246    const char     *input;
247    int            want_pos;
248    const char     *want;
249    ios::fmtflags  flags;
250
251  } data[] = {
252
253    { "0",   -1, "0", (ios::fmtflags) 0 },
254    { "00",  -1, "0", (ios::fmtflags) 0 },
255    { "0x0", -1, "0", (ios::fmtflags) 0 },
256
257    { "123/456",   -1, "123/456", ios::dec },
258    { "0123/456",  -1, "123/456", ios::dec },
259    { "123/0456",  -1, "123/456", ios::dec },
260    { "0123/0456", -1, "123/456", ios::dec },
261
262    { "123/456",   -1, "83/302", ios::oct },
263    { "0123/456",  -1, "83/302", ios::oct },
264    { "123/0456",  -1, "83/302", ios::oct },
265    { "0123/0456", -1, "83/302", ios::oct },
266
267    { "ab",   -1, "171",  ios::hex },
268    { "cd",   -1, "205",  ios::hex },
269    { "ef",   -1, "239",  ios::hex },
270
271    { "0/0",     -1, "0/0", (ios::fmtflags) 0 },
272    { "5/8",     -1, "5/8", (ios::fmtflags) 0 },
273    { "0x5/0x8", -1, "5/8", (ios::fmtflags) 0 },
274
275    { "123/456",   -1, "123/456",  (ios::fmtflags) 0 },
276    { "123/0456",  -1, "123/302",  (ios::fmtflags) 0 },
277    { "123/0x456", -1, "123/1110", (ios::fmtflags) 0 },
278    { "123/0X456", -1, "123/1110", (ios::fmtflags) 0 },
279
280    { "0123/123",   -1, "83/123", (ios::fmtflags) 0 },
281    { "0123/0123",  -1, "83/83",  (ios::fmtflags) 0 },
282    { "0123/0x123", -1, "83/291", (ios::fmtflags) 0 },
283    { "0123/0X123", -1, "83/291", (ios::fmtflags) 0 },
284
285    { "0x123/123",   -1, "291/123", (ios::fmtflags) 0 },
286    { "0X123/0123",  -1, "291/83",  (ios::fmtflags) 0 },
287    { "0x123/0x123", -1, "291/291", (ios::fmtflags) 0 },
288
289    { " 123",  0, NULL,  (ios::fmtflags) 0 },   // not without skipws
290    { " 123", -1, "123", ios::skipws },
291
292    { "123 /456",    3, "123",  (ios::fmtflags) 0 },
293    { "123/ 456",    4,  NULL,  (ios::fmtflags) 0 },
294    { "123/"    ,   -1,  NULL,  (ios::fmtflags) 0 },
295    { "123 /456",    3, "123",  ios::skipws },
296    { "123/ 456",    4,  NULL,  ios::skipws },
297  };
298
299  mpq_t      got, want;
300  bool       got_ok, want_ok;
301  bool       got_eof, want_eof;
302  long       got_si, want_si;
303  streampos  init_tellg, got_pos, want_pos;
304
305  mpq_init (got);
306  mpq_init (want);
307
308  for (size_t i = 0; i < numberof (data); i++)
309    {
310      size_t input_length = strlen (data[i].input);
311      want_pos = (data[i].want_pos == -1
312                  ? input_length : data[i].want_pos);
313      want_eof = (want_pos == streampos(input_length));
314
315      want_ok = (data[i].want != NULL);
316
317      if (data[i].want != NULL)
318        mpq_set_str_or_abort (want, data[i].want, 0);
319      else
320        mpq_set_ui (want, 0L, 1L);
321
322      if (option_check_standard
323          && mpz_fits_slong_p (mpq_numref(want))
324          && mpz_cmp_ui (mpq_denref(want), 1L) == 0
325          && strchr (data[i].input, '/') == NULL)
326        {
327          istringstream  input (data[i].input);
328          input.flags (data[i].flags);
329          init_tellg = input.tellg();
330          want_si = mpz_get_si (mpq_numref(want));
331
332          input >> got_si;
333          got_ok = !input.fail();
334          got_eof = input.eof();
335          input.clear();
336          got_pos = input.tellg() - init_tellg;
337
338          if (got_ok != want_ok)
339            {
340              WRONG ("stdc++ operator>> wrong status, check_mpq");
341              cout << "  want_ok: " << want_ok << "\n";
342              cout << "  got_ok:  " << got_ok << "\n";
343            }
344          if (want_ok && want_si != got_si)
345            {
346              WRONG ("stdc++ operator>> wrong result, check_mpq");
347              cout << "  got_si:  " << got_si << "\n";
348              cout << "  want_si: " << want_si << "\n";
349            }
350          if (want_ok && got_eof != want_eof)
351            {
352              WRONG ("stdc++ operator>> wrong EOF state, check_mpq");
353              cout << "  got_eof:  " << got_eof << "\n";
354              cout << "  want_eof: " << want_eof << "\n";
355            }
356          if (putback_tellg_works && got_pos != want_pos)
357            {
358              WRONG ("stdc++ operator>> wrong position, check_mpq");
359              cout << "  want_pos: " << want_pos << "\n";
360              cout << "  got_pos:  " << got_pos << "\n";
361            }
362        }
363
364      {
365        istringstream  input (data[i].input);
366        input.flags (data[i].flags);
367        init_tellg = input.tellg();
368        mpq_set_si (got, 0xDEAD, 0xBEEF);
369
370        input >> got;
371        got_ok = !input.fail();
372	got_eof = input.eof();
373        input.clear();
374        got_pos = input.tellg() - init_tellg;
375
376        if (got_ok != want_ok)
377          {
378            WRONG ("mpq operator>> wrong status");
379            cout << "  want_ok: " << want_ok << "\n";
380            cout << "  got_ok:  " << got_ok << "\n";
381            abort ();
382          }
383        // don't use mpq_equal, since we allow non-normalized values to be
384        // read, which can trigger ASSERTs in mpq_equal
385        if (want_ok && (mpz_cmp (mpq_numref (got), mpq_numref(want)) != 0
386                        || mpz_cmp (mpq_denref (got), mpq_denref(want)) != 0))
387          {
388            WRONG ("mpq operator>> wrong result");
389            mpq_trace ("  got ", got);
390            mpq_trace ("  want", want);
391            abort ();
392          }
393        if (want_ok && got_eof != want_eof)
394          {
395            WRONG ("mpq operator>> wrong EOF state");
396            cout << "  want_eof: " << want_eof << "\n";
397            cout << "  got_eof:  " << got_eof << "\n";
398            abort ();
399          }
400        if (putback_tellg_works && got_pos != want_pos)
401          {
402            WRONG ("mpq operator>> wrong position");
403            cout << "  want_pos: " << want_pos << "\n";
404            cout << "  got_pos:  " << got_pos << "\n";
405            abort ();
406          }
407      }
408    }
409
410  mpq_clear (got);
411  mpq_clear (want);
412}
413
414
415void
416check_mpf (void)
417{
418  static const struct {
419    const char     *input;
420    int            want_pos;
421    const char     *want;
422    ios::fmtflags  flags;
423
424  } data[] = {
425
426    { "0",      -1, "0", (ios::fmtflags) 0 },
427    { "+0",     -1, "0", (ios::fmtflags) 0 },
428    { "-0",     -1, "0", (ios::fmtflags) 0 },
429    { "0.0",    -1, "0", (ios::fmtflags) 0 },
430    { "0.",     -1, "0", (ios::fmtflags) 0 },
431    { ".0",     -1, "0", (ios::fmtflags) 0 },
432    { "+.0",    -1, "0", (ios::fmtflags) 0 },
433    { "-.0",    -1, "0", (ios::fmtflags) 0 },
434    { "+0.00",  -1, "0", (ios::fmtflags) 0 },
435    { "-0.000", -1, "0", (ios::fmtflags) 0 },
436    { "+0.00",  -1, "0", (ios::fmtflags) 0 },
437    { "-0.000", -1, "0", (ios::fmtflags) 0 },
438    { "0.0e0",  -1, "0", (ios::fmtflags) 0 },
439    { "0.e0",   -1, "0", (ios::fmtflags) 0 },
440    { ".0e0",   -1, "0", (ios::fmtflags) 0 },
441    { "0.0e-0", -1, "0", (ios::fmtflags) 0 },
442    { "0.e-0",  -1, "0", (ios::fmtflags) 0 },
443    { ".0e-0",  -1, "0", (ios::fmtflags) 0 },
444    { "0.0e+0", -1, "0", (ios::fmtflags) 0 },
445    { "0.e+0",  -1, "0", (ios::fmtflags) 0 },
446    { ".0e+0",  -1, "0", (ios::fmtflags) 0 },
447
448    { "1",  -1,  "1", (ios::fmtflags) 0 },
449    { "+1", -1,  "1", (ios::fmtflags) 0 },
450    { "-1", -1, "-1", (ios::fmtflags) 0 },
451
452    { " 0",  0,  NULL, (ios::fmtflags) 0 },  // not without skipws
453    { " 0",  -1, "0", ios::skipws },
454    { " +0", -1, "0", ios::skipws },
455    { " -0", -1, "0", ios::skipws },
456
457    { "+-123", 1, NULL, (ios::fmtflags) 0 },
458    { "-+123", 1, NULL, (ios::fmtflags) 0 },
459    { "1e+-123", 3, NULL, (ios::fmtflags) 0 },
460    { "1e-+123", 3, NULL, (ios::fmtflags) 0 },
461
462    { "e123",   0, NULL, (ios::fmtflags) 0 }, // at least one mantissa digit
463    { ".e123",  1, NULL, (ios::fmtflags) 0 },
464    { "+.e123", 2, NULL, (ios::fmtflags) 0 },
465    { "-.e123", 2, NULL, (ios::fmtflags) 0 },
466
467    { "123e",   4, NULL, (ios::fmtflags) 0 }, // at least one exponent digit
468    { "123e-",  5, NULL, (ios::fmtflags) 0 },
469    { "123e+",  5, NULL, (ios::fmtflags) 0 },
470  };
471
472  mpf_t      got, want;
473  bool       got_ok, want_ok;
474  bool       got_eof, want_eof;
475  double     got_d, want_d;
476  streampos  init_tellg, got_pos, want_pos;
477
478  mpf_init (got);
479  mpf_init (want);
480
481  for (size_t i = 0; i < numberof (data); i++)
482    {
483      size_t input_length = strlen (data[i].input);
484      want_pos = (data[i].want_pos == -1
485                  ? input_length : data[i].want_pos);
486      want_eof = (want_pos == streampos(input_length));
487
488      want_ok = (data[i].want != NULL);
489
490      if (data[i].want != NULL)
491        mpf_set_str_or_abort (want, data[i].want, 0);
492      else
493        mpf_set_ui (want, 0L);
494
495      want_d = mpf_get_d (want);
496      if (option_check_standard && mpf_cmp_d (want, want_d) == 0)
497        {
498          istringstream  input (data[i].input);
499          input.flags (data[i].flags);
500          init_tellg = input.tellg();
501
502          input >> got_d;
503          got_ok = !input.fail();
504          got_eof = input.eof();
505          input.clear();
506          got_pos = input.tellg() - init_tellg;
507
508          if (got_ok != want_ok)
509            {
510              WRONG ("stdc++ operator>> wrong status, check_mpf");
511              cout << "  want_ok: " << want_ok << "\n";
512              cout << "  got_ok:  " << got_ok << "\n";
513            }
514          if (want_ok && want_d != got_d)
515            {
516              WRONG ("stdc++ operator>> wrong result, check_mpf");
517              cout << "  got:   " << got_d << "\n";
518              cout << "  want:  " << want_d << "\n";
519            }
520          if (want_ok && got_eof != want_eof)
521            {
522              WRONG ("stdc++ operator>> wrong EOF state, check_mpf");
523              cout << "  got_eof:  " << got_eof << "\n";
524              cout << "  want_eof: " << want_eof << "\n";
525            }
526          if (putback_tellg_works && got_pos != want_pos)
527            {
528              WRONG ("stdc++ operator>> wrong position, check_mpf");
529              cout << "  want_pos: " << want_pos << "\n";
530              cout << "  got_pos:  " << got_pos << "\n";
531            }
532        }
533
534      {
535        istringstream  input (data[i].input);
536        input.flags (data[i].flags);
537        init_tellg = input.tellg();
538
539        mpf_set_ui (got, 0xDEAD);
540        input >> got;
541        got_ok = !input.fail();
542	got_eof = input.eof();
543        input.clear();
544        got_pos = input.tellg() - init_tellg;
545
546        if (got_ok != want_ok)
547          {
548            WRONG ("mpf operator>> wrong status");
549            cout << "  want_ok: " << want_ok << "\n";
550            cout << "  got_ok:  " << got_ok << "\n";
551            abort ();
552          }
553        if (want_ok && mpf_cmp (got, want) != 0)
554          {
555            WRONG ("mpf operator>> wrong result");
556            mpf_trace ("  got ", got);
557            mpf_trace ("  want", want);
558            abort ();
559          }
560        if (want_ok && got_eof != want_eof)
561          {
562            WRONG ("mpf operator>> wrong EOF state");
563            cout << "  want_eof: " << want_eof << "\n";
564            cout << "  got_eof:  " << got_eof << "\n";
565            abort ();
566          }
567        if (putback_tellg_works && got_pos != want_pos)
568          {
569            WRONG ("mpf operator>> wrong position");
570            cout << "  want_pos: " << want_pos << "\n";
571            cout << "  got_pos:  " << got_pos << "\n";
572            abort ();
573          }
574      }
575    }
576
577  mpf_clear (got);
578  mpf_clear (want);
579}
580
581
582
583int
584main (int argc, char *argv[])
585{
586  if (argc > 1 && strcmp (argv[1], "-s") == 0)
587    option_check_standard = true;
588
589  tests_start ();
590
591  check_putback_tellg ();
592  check_mpz ();
593  check_mpq ();
594  check_mpf ();
595
596  tests_end ();
597  return 0;
598}
599