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