/************************************************************************
 *
 * 27.istream.fmat.arith.cpp - test exercising basic_istream
 *                             arithmetic extractors
 *
 * $Id: 27.istream.fmat.arith.cpp 648752 2008-04-16 17:01:56Z faridz $
 *
 ************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 *
 **************************************************************************/

#include <cstddef>
#include <istream>

#include <rw_char.h>
#include <rw_ctype.h>
#include <rw_exception.h>
#include <rw_printf.h>
#include <rw_streambuf.h>
#include <driver.h>

/***********************************************************************/

struct LocaleData {
    const char *whitespace;
    int         decimal_point;
    int         thousands_sep;
    const char *grouping;
    const char *truename;
    const char *falsename;
};


template <class CharT>
const std::locale
make_locale (const CharT*, const LocaleData &data)
{
#if 255U == _RWSTD_UCHAR_MAX

    // work around a bogus gcc error: ISO C++ forbids variable-size
    // array (see https://issues.apache.org/jira/browse/STDCXX-351)
    static const std::size_t N = 256;

#else   // if UCHAR_MAX != 255

    // determine the number of classic characters
    static const std::size_t N = std::ctype<char>::table_size;

#endif

    // derive a class so we can call the protected classic_table()
    struct ClassicTable: std::ctype<char> {
        static const std::ctype_base::mask* get () {
            return classic_table ();
        }
    };

    // get the classic C table of masks
    const std::ctype_base::mask* const classic = ClassicTable::get ();

    static int chars [N + 1];
    static int masks [N + 1];

    // -1 denotes end of characters (can't use 0 since it's a valid value)
    chars [N] = -1;

    // copy the classic table clearing the space bit of each mask
    for (std::size_t i = 0; i != N; ++i) {
        chars [i] = int (i);
        masks [i] = classic [i] & ~std::ctype_base::space;
    }

    typedef unsigned char UChar;

    // set the space bit for all characters in whitespace
    for (const char *ws = data.whitespace; *ws; ++ws)
        masks [UChar (*ws)] |= std::ctype_base::space;

    // create a user-defined ctype-derived facet and initialize
    // it with the character set and the specified masks
    const std::ctype<CharT>* const ctp =
        new UserCtype<CharT>(chars, masks);

    struct NumPunct: std::numpunct<CharT> {

        // CharType typedef works around a Sun C++ bug stdcxx-345
        typedef CharT                                          CharType;
        typedef std::numpunct<CharType>                        Base;
        typedef std::char_traits<CharType>                     Traits;
        typedef std::allocator<CharType>                       Allocator;
        typedef std::basic_string<CharType, Traits, Allocator> String;

        int             dp_;
        int             ts_;
        const char     *grp_;
        const CharType *fn_;
        const CharType *tn_;

        NumPunct (int dp, int ts, const char *grp,
                  const CharType *fn, const CharType *tn)
            : Base (), dp_ (dp), ts_ (ts), grp_ (grp ? grp : ""),
              fn_ (fn), tn_ (tn) { /* empty */ }

        CharType do_decimal_point () const {
            return -1 == dp_ ? Base::do_decimal_point ()
                              : make_char (char (dp_), (CharType*)0);
        }

        CharType do_thousands_sep () const {
            return -1 == ts_ ? Base::do_thousands_sep ()
                             : make_char (char (ts_), (CharType*)0);
        }

        std::string do_grouping () const {
            return 0 == grp_ ? Base::do_grouping ()
                             : std::string (grp_);
        }

        String do_truename () const {
            return 0 == tn_ ? Base::do_truename () : String (tn_);
        }

        String do_falsename () const {
            return 0 == fn_ ? Base::do_falsename () : String (fn_);
        }
    };

    static CharT names [2][64];

    names [false][0] = CharT ();
    names [true][0]  = CharT ();

    if (data.falsename) {
        for (std::size_t i = 0; data.falsename [i]; ++i) {
            names [false][i]     = data.falsename [i];
            names [false][i + 1] = CharT ();
        }
    }

    if (data.truename) {
        for (std::size_t i = 0; data.truename [i]; ++i) {
            names [true][i]     = data.truename [i];
            names [true][i + 1] = CharT ();
        }
    }

    // create a user-defined numpunct-derived facet and initialize
    // its members with the specified parameters
    const std::numpunct<CharT>* const np =
        new NumPunct (data.decimal_point,
                      data.thousands_sep,
                      data.grouping,
                      data.falsename ? names [false] : 0,
                      data.truename ? names [true] : 0);

    // install the new nupunct facet in a temporary locale,
    // replacing the original (if CharT=char or CharT=wchar_t)
    const std::locale tmp (std::locale::classic (), np);

    // install the new ctype facet in a locale, replacing
    // the original (if CharT=char or CharT=wchar_t)
    return std::locale (tmp, ctp);
}

/***********************************************************************/

template <class CharT, class Traits>
const std::locale
make_locale (const CharT*, const Traits*,  const LocaleData &data)
{
    // construct a locale with the appropriate ctype and numpunct
    // facets installed
    const std::locale tmp (make_locale ((CharT*)0, data));

    typedef std::istreambuf_iterator<CharT, Traits> Iterator;
    typedef std::num_get<CharT, Iterator>           NumGet;

    // check to see if num_get for this charater type and traits
    // is installed and install it if it isn't (i.e., when Traits
    // is other than std::char_traits<CharT>)
    if (std::has_facet<NumGet>(tmp))
        return tmp;

    return std::locale (tmp, new NumGet);
}

/***********************************************************************/

/* extern */ int opt_no_gcount;


template <class CharT, class Traits, class ArithmeticType>
void
test_extractor (CharT*, Traits*, ArithmeticType*,
                const char     *cname,
                const char     *tname,
                const char     *aname,
                int             line,
                // printf formatting directive for ArithmeticType
                const char *valfmt,
                // character buffer (input sequence)
                const char *cbuf,
                // number of characters in buffer:
                std::size_t cbuf_size,
                // ctype and numpunct data
                const LocaleData &locale_data,
                // stream flags():
                int flags,
                // initial stream rdstate():
                int init_state,
                // unmasked exceptions:
                int exceptions,
                // expected exception:
                int expect_exception,
                // expected stream state after extraction:
                int expect_state,
                // expected number of extracted characters:
                int expect_extract,
                // have streambuf fail (or throw) after so many calls
                // to underflow() (each call extracts a single chracter):
                int fail_when,
                // initial value of the argument to extractor:
                ArithmeticType init_value,
                // expected value of the argument after extraction:
                ArithmeticType expect_value)
{
    _RWSTD_UNUSED (cname);
    _RWSTD_UNUSED (tname);

    typedef std::basic_istream<CharT, Traits> Istream;
    typedef MyStreambuf<CharT, Traits>        Streambuf;

    const char *fail_desc = 0;

    int fail_how = 0;

    if (fail_when < 0) {
        // have the stream buffer object's underflow() fail (by throwing
        // an exception if possible) after `fail_when' characters have
        // been extracted (this object calls underflow() for every char)
        fail_how  = Underflow | Throw;
        fail_when = -fail_when;
        fail_desc = "threw";
    }
    else if (0 < fail_when) {
        // have the stream buffer object's underflow() fail by returning
        // eof after `fail_when' characters have been extracted (this
        // object calls underflow() for every char)
        fail_how  = Underflow;
        fail_desc = "returned EOF";
    }

    // construct a stream buffer object and initialize its read sequence
    // with the character buffer
    Streambuf sb (cbuf, cbuf_size, fail_how, fail_when);

    // construct an istream object and initialize it with the user
    // defined streambuf object
    Istream is (&sb);

    if (-1 == flags) {
        // get the initial stream object's format control flags
        flags = is.flags ();
    }
    else {
        // set the stream object's format control flags
        is.flags (std::ios::fmtflags (flags));
    }

    if (-1 == exceptions) {
        // get the initial stream object's exceptions
        exceptions = is.exceptions ();
    }
    else {
        // unmask the stream objects exceptions (must be done
        // before calling setstate() to prevent the latter from
        // throwing ios::failure)
        is.exceptions (std::ios::iostate (exceptions));
    }

    if (-1 == init_state) {
        // get the initial stream object's state
        init_state = is.rdstate ();
    }
    else {
        // set the stream object's initial state

#ifndef _RWSTD_NO_EXCEPTIONS
        try {
            is.setstate (std::ios::iostate (init_state));
        }
        catch (...) {
            // ignore exceptions
        }
#else   // if defined ( _RWSTD_NO_EXCEPTIONS)
        is.setstate (std::ios::iostate (init_state));
#endif   //  _RWSTD_NO_EXCEPTIONS
    }

    // construct a locale object that treats only the specified `white'
    // characters as whitespace (all others are treated normally)
    const std::locale loc =
        is.imbue (make_locale ((CharT*)0, (Traits*)0, locale_data));

    // imbue the previous locale into the stream buffer to verify that
    // the ws manipulator uses the locale imbued in the stream object
    // and not the one in the stream buffer
    sb.pubimbue (loc);

    // initialize the variable to the initial value to detect
    // the extractor setting it when it's not supposed to
    ArithmeticType value = init_value;

    // format the FUNCALL environment variable w/o writing out any output
    rw_fprintf (0,
                "%{$FUNCALL!:@}",
                "%{$CLASS}(%{*Ac}).operator>>(%s& = %{@}): "
                "initial flags() = %{If}, rdstate() = %{Is}, "
                "exceptions() = %{Is}, whitespace = %{#s}, numpunct = { "
                ".dp=%{#c}, .ts=%{#c}, .grp=%{#s}, .fn=%{#s}, .tn=%{#s} }",
                int (sizeof *cbuf), cbuf, aname, valfmt, init_value,
                flags, init_state,
                exceptions, locale_data.whitespace,
                locale_data.decimal_point, locale_data.thousands_sep,
                locale_data.grouping, locale_data.falsename,
                locale_data.truename);

#ifndef _RWSTD_NO_EXCEPTIONS

    int caught = 0;

    try {
        is >> value;
    }
    catch (Exception&) {
        caught = 1;
    }
    catch (std::ios_base::failure &ex) {
        caught = 2;
        rw_assert (caught == expect_exception, 0, line,
                   "line %d. %{$FUNCALL}: unexpectedly threw "
                   "ios_base::failure(%{#s})", __LINE__, ex.what ());
    }
    catch (...) {
        caught = -1;
        rw_assert (false, 0, line,
                   "line %d. %{$FUNCALL}: unexpectely threw an exception "
                   "of unknown type", __LINE__);
    }

    //////////////////////////////////////////////////////////////////
    // verify that the function propagates exceptions thrown from the
    // streambuf object only when badbit is set in the stream object's
    // exceptions()

    rw_assert (caught == expect_exception, 0, line,
               "line %d. %{$FUNCALL}: "
               "%{?}failed to throw"
               "%{:}unexpectedly propagated"
               "%{;} exception",
               __LINE__, expect_exception);

#else   // if defined (_RWSTD_NO_EXCEPTIONS)

    is >> value;

#endif   // _RWSTD_NO_EXCEPTIONS

    // clear the text describing the type of failure when streambuf
    // didn't actually fail (or throw)
    if (sb.failed_ == None && sb.threw_ == None)
        fail_desc = 0;

    //////////////////////////////////////////////////////////////////
    // verify that the expected number of characters have been
    // extracted from the stream

    const int extracted = int (sb.pubgptr () - sb.pubeback ());

    rw_assert (expect_extract == extracted, 0, line,
               "%d. %{$FUNCALL}: expected to extract %d characters, "
               "got %d%{?} (underflow() %s at extraction %u)%{;}",
               __LINE__, expect_extract,
               extracted, 0 != fail_desc, fail_desc, fail_when);

    //////////////////////////////////////////////////////////////////
    // verify that gcount() is not affected

    if (0 == opt_no_gcount)
        rw_assert (0 == is.gcount (), 0, line,
                   "%d. %{$FUNCALL}: gcount() == 0, got %d "
                   "%{?} (underflow() %s at extraction %u)%{;}",
                   __LINE__, is.gcount (),
                   0 != fail_desc, fail_desc, fail_when);

    //////////////////////////////////////////////////////////////////
    // verify the state of the stream object after the function call

    rw_assert (is.rdstate () == expect_state, 0, line,
               "line %d. %{$FUNCALL}: rdstate() == %{Is}, got %{Is}"
               "%{?} (underflow() %s at extraction %u)%{;}",
               __LINE__, expect_state, is.rdstate(),
               0 != fail_desc, fail_desc, fail_when);

    //////////////////////////////////////////////////////////////////
    // verify the extracted value matches the expected value

    rw_assert (expect_value == value, 0, line,
               "line %d. %{$FUNCALL}: expected value %{@}, got %{@}",
               __LINE__, valfmt, expect_value, valfmt, value);
}

/***********************************************************************/

// state abbreviations for brevity
const int ___ =                0;
const int __f =                0 |                0 | std::ios::failbit;
const int _e_ =                0 | std::ios::eofbit |                 0;
const int _ef =                0 | std::ios::eofbit | std::ios::failbit;
const int b__ = std::ios::badbit |                0 |                 0;
const int b_f = std::ios::badbit |                0 | std::ios::failbit;
const int be_ = std::ios::badbit | std::ios::eofbit |                 0;
const int bef = std::ios::badbit | std::ios::eofbit | std::ios::failbit;

const int DEC = std::ios::dec;
const int OCT = std::ios::oct;
const int HEX = std::ios::hex;


template <class CharT, class Traits, class ArithmeticType>
void
test_extractor (CharT*, Traits*, ArithmeticType*,
                const char *cname,
                const char *tname,
                const char *aname,
                const char *fmt)
{
    typedef ArithmeticType T;

    LocaleData locale_data = {
        0, -1, -1, 0, 0, 0
    };

    static const volatile T zero = T ();
    static const volatile T negative_one = T (-1);

    const bool is_signed   = negative_one < zero;
    const bool is_floating = zero > negative_one / 2;

    // avoid test cases designed to exercise the extraction of negative
    // values when ArithmeticType is an unsigned type
#undef TEST
#define TEST(str, fl, is, ex, ee, es, ne, fw, iv, ev)                        \
    if (is_signed || 0 <= ev)                                                \
        test_extractor ((CharT*)0, (Traits*)0, (T*)0, cname, tname, aname,   \
                        __LINE__, fmt, str, sizeof str - 1, locale_data,     \
                        fl, is, ex, ee, es, ne, fw, (T)(iv), (T)ev)

    locale_data.whitespace = "";

    //    +-- subject sequence
    //    |     +-- initial flags (-1 for default)
    //    |     |    +-- initial state (-1 for default)
    //    |     |    |   +-- exceptions (-1 for default)
    //    |     |    |   |  +-- expected exception
    //    |     |    |   |  |   +-- expected rdstate()
    //    |     |    |   |  |   |   +-- extracted characters
    //    |     |    |   |  |   |   |   +-- fail when? (0 to not)
    //    |     |    |   |  |   |   |   |  +-- initial value
    //    |     |    |   |  |   |   |   |  |  +-- expected value
    //    |     |    |   |  |   |   |   |  |  |
    //    V     V    V   V  V   V   V   V  V  V
    TEST ("",  -1,  -1, -1, 0, _ef, 0,  0, 1, 1);
    TEST (" ", -1,  -1, -1, 0, __f, 0,  0, 2, 2);
    TEST (" ", -1,  -1, -1, 0, _ef, 0,  1, 3, 3);   // fail
    TEST (" ", -1,  -1, -1, 0, b__, 0, -1, 4, 4);   // throw

    // exercise behavior when the initial state is other than good
    TEST (" ",   -1, __f, -1, 0, __f, 0, -1,  5,  5);
    TEST (" ",   -1, _e_, -1, 0, _ef, 0, -1,  6,  6);
    TEST (" ",   -1, _ef, -1, 0, _ef, 0, -1,  7,  7);
    TEST (" ",   -1, b__, -1, 0, b_f, 0, -1,  8,  8);
    TEST (" ",   -1, b_f, -1, 0, b_f, 0, -1,  9,  9);
    TEST (" ",   -1, be_, -1, 0, bef, 0, -1, 10, 10);
    TEST (" ",   -1, bef, -1, 0, bef, 0, -1, 11, 11);

    // verify that when the initial state isn't good the extractors
    // do not attempt to obtain the requested input but call setstate()
    // to set failbit (which causes failure to be thrown when failbit
    // or any other bit is set in exceptions) (see LWG issue 419)
    TEST (" ",   -1, __f, __f, 2, __f, 0, -1, 9, 9);
    TEST (" ",   -1, _e_, __f, 2, _ef, 0, -1, 9, 9);
    TEST (" ",   -1, _e_, _e_, 2, _ef, 0, -1, 9, 9);
    TEST (" ",   -1, _ef, _e_, 2, _ef, 0, -1, 9, 9);
    TEST (" ",   -1, b__, __f, 2, b_f, 0, -1, 9, 9);
    TEST (" ",   -1, b_f, __f, 2, b_f, 0, -1, 9, 9);
    TEST (" ",   -1, b_f, b__, 2, b_f, 0, -1, 9, 9);

    locale_data.whitespace = " ";
    TEST (" ",   -1,  -1, -1, 0, _ef, 1,  0, 9, 9);
    TEST (" ",   -1,  -1, -1, 0, _ef, 0,  1, 9, 9);   // fail
    TEST (" ",   -1,  -1, -1, 0, b__, 0, -1, 9, 9);   // throw
    TEST (" ",   -1,  -1, -1, 0, _ef, 1,  2, 9, 9);   // fail
    TEST (" ",   -1,  -1, -1, 0, b__, 1, -2, 9, 9);   // throw
    TEST ("_",   -1,  -1, -1, 0, __f, 0,  0, 9, 9);

    TEST (" ",    0,  -1, -1, 0, __f, 0,  0, 9, 9);

    locale_data.whitespace = "_";
    TEST (" ",   -1, -1, -1, 0, __f, 0, 0, 9, 9);
    TEST ("_",   -1, -1, -1, 0, _ef, 1, 0, 9, 9);

    locale_data.whitespace = "";
    TEST ("0",   -1, -1, -1, 0, _e_, 1, 0, 9,  0);
    TEST ("1",   -1, -1, -1, 0, _e_, 1, 0, 9,  1);
    TEST ("+0",  -1, -1, -1, 0, _e_, 2, 0, 9,  0);
    TEST ("-0",  -1, -1, -1, 0, _e_, 2, 0, 9,  0);
    TEST ("+1",  -1, -1, -1, 0, _e_, 2, 0, 9,  1);
    TEST ("-1",  -1, -1, -1, 0, _e_, 2, 0, 1, -1);
    TEST ("+2 ", -1, -1, -1, 0, ___, 2, 0, 9,  2);
    TEST ("-2 ", -1, -1, -1, 0, ___, 2, 0, 9, -2);

    if (is_floating) {
        TEST ("3.",  -1, -1, -1, 0, _e_, 2, 0, 9,  3);
        TEST ("+3.", -1, -1, -1, 0, _e_, 3, 0, 9,  3);
        TEST ("-3.", -1, -1, -1, 0, _e_, 3, 0, 9, -3);
    }
    else { 
        TEST ("3.",  -1, -1, -1, 0, ___, 1, 0, 9,  3);
        TEST ("+3.", -1, -1, -1, 0, ___, 2, 0, 9,  3);
        TEST ("-3.", -1, -1, -1, 0, ___, 2, 0, 9, -3);
   }

    // set '#' as the only whitespace character
    locale_data.whitespace = "#";
    TEST ("#+3",  -1, -1, -1, 0, _e_, 3, 0,  9,  3);
    TEST ("#-4",  -1, -1, -1, 0, _e_, 3, 0,  9, -4);
    TEST ("#+5",  -1, -1, -1, 0, _e_, 3, 0,  9,  5);
    TEST ("#-6",  -1, -1, -1, 0, _e_, 3, 0,  9, -6);
    TEST ("#+7#", -1, -1, -1, 0, ___, 3, 0,  9,  7);
    TEST ("#-8#", -1, -1, -1, 0, ___, 3, 0,  9, -8);
    TEST (" -9",  -1, -1, -1, 0, __f, 0, 0, -9, -9);

    // set 'X', 'Y', and 'Z' as the only whitespace characters
    locale_data.whitespace = "XYZ";
    TEST ("X10",   -1, -1, -1, 0, _e_, 3, 0, 9, 10);
    TEST ("XY11",  -1, -1, -1, 0, _e_, 4, 0, 9, 11);
    TEST ("XYZ12", -1, -1, -1, 0, _e_, 5, 0, 9, 12);

    // set '9' as the whitespace character
    locale_data.whitespace = "9";
    TEST ("9",    -1, -1, -1, 0, _ef, 1, 0, 8, 8);
    TEST ("99",   -1, -1, -1, 0, _ef, 2, 0, 8, 8);
    TEST ("999",  -1, -1, -1, 0, _ef, 3, 0, 8, 8);
    TEST ("9991", -1, -1, -1, 0, _e_, 4, 0, 8, 1);

    // exercise grouping
    locale_data.whitespace    = "_";
    locale_data.thousands_sep = ';';
    locale_data.grouping      = "\1";

    TEST ("0;0",       -1, -1, -1, 0, _e_, 3, 0, 9,  0);
    TEST ("+0;0",      -1, -1, -1, 0, _e_, 4, 0, 9,  0);
    TEST ("-0;0",      -1, -1, -1, 0, _e_, 4, 0, 9,  0);
    TEST ("+0;1",      -1, -1, -1, 0, _e_, 4, 0, 9,  1);
    TEST ("-0;1",      -1, -1, -1, 0, _e_, 4, 0, 9, -1);
    TEST ("0;1",       -1, -1, -1, 0, _e_, 3, 0, 9,  1);
    TEST ("1;0",       -1, -1, -1, 0, _e_, 3, 0, 9,  10);
    TEST ("1;2",       -1, -1, -1, 0, _e_, 3, 0, 9,  12);
    TEST ("_1;2",      -1, -1, -1, 0, _e_, 4, 0, 9,  12);
    TEST ("_1;2_",     -1, -1, -1, 0, ___, 4, 0, 9,  12);
    TEST ("_1_;2",     -1, -1, -1, 0, ___, 2, 0, 9,  1);
    TEST ("12345",     -1, -1, -1, 0, _e_, 5, 0, 9,  12345);
    TEST ("1;2;3;4;5", -1, -1, -1, 0, _e_, 9, 0, 9,  12345);
    TEST ("12;3;4;5",  -1, -1, -1, 0, _ef, 8, 0, 9,  12345);

    // exercise thousands_sep that's a space
    locale_data.whitespace    = " ";
    locale_data.thousands_sep = ' ';
    locale_data.grouping      = "\2";
    TEST (" 0",        -1, -1, -1, 0, _e_, 2, 0, 9,  0);
    TEST ("  0",       -1, -1, -1, 0, _e_, 3, 0, 9,  0);
    TEST ("   0",      -1, -1, -1, 0, _e_, 4, 0, 9,  0);
    TEST (" 0 00",     -1, -1, -1, 0, _e_, 5, 0, 9,  0);
    TEST ("+0 00",     -1, -1, -1, 0, _e_, 5, 0, 9,  0);
    TEST ("-0 00",     -1, -1, -1, 0, _e_, 5, 0, 9,  0);
    TEST (" +0 00",    -1, -1, -1, 0, _e_, 6, 0, 9,  0);
    TEST (" -0 00",    -1, -1, -1, 0, _e_, 6, 0, 9,  0);
    TEST ("  +0 00",   -1, -1, -1, 0, _e_, 7, 0, 9,  0);
    TEST ("  -0 00",   -1, -1, -1, 0, _e_, 7, 0, 9,  0);
    TEST (" +00 00",   -1, -1, -1, 0, _e_, 7, 0, 9,  0);
    TEST (" -00 00",   -1, -1, -1, 0, _e_, 7, 0, 9,  0);
    TEST ("  00 01",   -1, -1, -1, 0, _e_, 7, 0, 9,  1);
    TEST (" +00 01",   -1, -1, -1, 0, _e_, 7, 0, 9,  1);
    TEST (" -00 01",   -1, -1, -1, 0, _e_, 7, 0, 1, -1);
    TEST ("0 01 02",   -1, -1, -1, 0, _e_, 7, 0, 9,  102);
    TEST ("1 02 03",   -1, -1, -1, 0, _e_, 7, 0, 9,  10203);
    TEST ("2 03 04",   -1, -1, -1, 0, _e_, 7, 0, 9,  20304);
    TEST ("304 05",    -1, -1, -1, 0, _ef, 6, 0, 9,  30405);
    TEST ("4 0 5 0",   -1, -1, -1, 0, _ef, 7, 0, 9,  4050);

    // according to the grammar in locale.numpunct, a valid number
    // cannot start or end with a thousands separator (and all pairs
    // of thousands separator must be separated by at least one
    // digit)
    locale_data.whitespace    = "_";
    locale_data.thousands_sep = ';';
    locale_data.grouping      = "\1";
    TEST (";",         -1, -1, -1, 0, __f, 0, 0, 9, 9);
    TEST (";;",        -1, -1, -1, 0, __f, 0, 0, 9, 9);
    TEST ("_;",        -1, -1, -1, 0, __f, 1, 0, 9, 9);
    TEST ("+;",        -1, -1, -1, 0, __f, 1, 0, 9, 9);
    TEST ("-;",        -1, -1, -1, 0, __f, 1, 0, 9, 9);
    TEST ("0;",        -1, -1, -1, 0, _ef, 2, 0, 9, 9);
    TEST ("+0;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("-0;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("+1;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("-1;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("1;",        -1, -1, -1, 0, _ef, 2, 0, 9, 9);
    TEST ("0;_",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST ("1;_",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST ("_0;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("_1;",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST ("0;;",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST ("1;;",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST (";0",        -1, -1, -1, 0, __f, 0, 0, 9, 9);
    TEST (";1",        -1, -1, -1, 0, __f, 0, 0, 9, 9);
    TEST ("_;0",       -1, -1, -1, 0, __f, 1, 0, 9, 9);
    TEST ("_;1",       -1, -1, -1, 0, __f, 1, 0, 9, 9);

    // exercise errors when thousands_sep that's a space
    locale_data.whitespace    = " ";
    locale_data.thousands_sep = ' ';
    locale_data.grouping      = "\2";
    TEST (" ",         -1, -1, -1, 0, _ef, 1, 0, 9, 9);
    TEST (" +",        -1, -1, -1, 0, _ef, 2, 0, 9, 9);
    TEST (" -",        -1, -1, -1, 0, _ef, 2, 0, 9, 9);
    TEST (" + ",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST (" - ",       -1, -1, -1, 0, __f, 2, 0, 9, 9);
    TEST (" 0 ",       -1, -1, -1, 0, _ef, 3, 0, 9, 9);
    TEST (" +0 ",      -1, -1, -1, 0, _ef, 4, 0, 9, 9);
    TEST (" -0 ",      -1, -1, -1, 0, _ef, 4, 0, 9, 9);
    TEST (" 123 ",     -1, -1, -1, 0, _ef, 5, 0, 9, 9);
    TEST (" +123 ",    -1, -1, -1, 0, _ef, 6, 0, 9, 9);
    TEST (" -123 ",    -1, -1, -1, 0, _ef, 6, 0, 9, 9);
}

/***********************************************************************/

void info (const char *cname, const char *tname, const char *type)
{
    // format the CLASS environment variable w/o writing out any output
    rw_fprintf (0,
                "%{$CLASS!:@}",
                "%{?}istream%{:}%{?}wistream"
                "%{:}basic_istream<%s, %s>%{;}%{;}",
                'c' == *cname && 's' == *tname, 
                'w' == *cname && 's' == *tname,
                cname, tname);

    rw_info (0, 0, __LINE__, "std::%{$CLASS}::operator>>(%s&)", type);
}

/***********************************************************************/

#undef TEST
#define TEST(T, fmt, str, fl, is, ex, ee, es, ne, fw, iv, ev)              \
        test_extractor ((CharT*)0, (Traits*)0, (T*)0, cname, tname, #T,    \
                        __LINE__, fmt, str, sizeof str - 1, locale_data,   \
                        fl, is, ex, ee, es, ne, fw, (T)(iv), (T)ev)

/***********************************************************************/

template <class CharT, class Traits>
void
test_bool (CharT*, Traits*,
           const char *cname,
           const char *tname)
{
    info (cname, tname, "bool");

#define TEST_BOOL(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (bool, "%b", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    const int ba    = std::ios::boolalpha;
    const int ba_ws = ba | std::ios::skipws;

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "_";
    locale_data.falsename = "NO";
    locale_data.truename  = "YES";

    TEST_BOOL ("",      -1, -1, -1, 0, _ef, 0, 0, false, false);
    TEST_BOOL ("",      -1, -1, -1, 0, _ef, 0, 0, true,  true);
    TEST_BOOL ("0",     -1, -1, -1, 0, _e_, 1, 0, true,  false);
    TEST_BOOL ("1",     -1, -1, -1, 0, _e_, 1, 0, false, true);
    TEST_BOOL ("+0",    -1, -1, -1, 0, _e_, 2, 0, true,  false);
    TEST_BOOL ("-0",    -1, -1, -1, 0, _e_, 2, 0, true,  false);
    TEST_BOOL ("+1",    -1, -1, -1, 0, _e_, 2, 0, false, true);
    TEST_BOOL ("00",    -1, -1, -1, 0, _e_, 2, 0, true,  false);
    TEST_BOOL ("01",    -1, -1, -1, 0, _e_, 2, 0, false, true);
    TEST_BOOL ("_000",  -1, -1, -1, 0, _e_, 4, 0, true,  false);
    TEST_BOOL ("_001",  -1, -1, -1, 0, _e_, 4, 0, false, true);

    TEST_BOOL ("NO",    -1, -1, -1, 0, __f, 0, 0, true,  true);
    TEST_BOOL ("YES",   -1, -1, -1, 0, __f, 0, 0, false, false);
    TEST_BOOL ("_NO",   -1, -1, -1, 0, __f, 1, 0, true,  true);
    TEST_BOOL ("_YES",  -1, -1, -1, 0, __f, 1, 0, false, false);

    TEST_BOOL ("NO",    ba,    -1, -1, 0, ___, 2, 0, false, false);
    TEST_BOOL ("YES",   ba,    -1, -1, 0, ___, 3, 0, true,  true);
    TEST_BOOL ("_NO",   ba_ws, -1, -1, 0, ___, 3, 0, false, false);
    TEST_BOOL ("_YES",  ba_ws, -1, -1, 0, ___, 4, 0, true,  true);
    TEST_BOOL ("_NO_",  ba_ws, -1, -1, 0, ___, 3, 0, false, false);
    TEST_BOOL ("_YES_", ba_ws, -1, -1, 0, ___, 4, 0, true,  true);

    locale_data.falsename = "bool:0";
    locale_data.truename  = "bool:1";

    TEST_BOOL ("bool:0", ba, -1, -1, 0, ___, 6, 0, false, false);
    TEST_BOOL ("bool:1", ba, -1, -1, 0, ___, 6, 0, true,  true);
    TEST_BOOL ("bool:2", ba, -1, -1, 0, __f, 5, 0, true,  true);
}

/***********************************************************************/

template <class CharT, class Traits>
void
test_shrt (CharT*, Traits*,
           const char *cname,
           const char *tname)
{
    info (cname, tname, "short");

    test_extractor ((CharT*)0, (Traits*)0, (short*)0,
                    cname, tname, "short", "%hi");
    
#define TEST_SHRT(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (short, "%hi", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_SHRT_SIZE
    const short smax = +32767;
    const short smin = -smax - 1;

    TEST_SHRT ("-32768", -1, -1, -1, 0, _e_, 6, 0, -1, smin);
    TEST_SHRT ("-32769", -1, -1, -1, 0, _ef, 6, 0, -1, smin);
    TEST_SHRT ("-99999", -1, -1, -1, 0, _ef, 6, 0, -1, smin);

    TEST_SHRT ("+32767", -1, -1, -1, 0, _e_, 6, 0, -1, smax);
    TEST_SHRT ("+32768", -1, -1, -1, 0, _ef, 6, 0, -1, smax);
    TEST_SHRT ("+99999", -1, -1, -1, 0, _ef, 6, 0, -1, smax);
    TEST_SHRT ("999999", -1, -1, -1, 0, _ef, 6, 0, -1, smax);

#elif 4 == _RWSTD_SHRT_SIZE
    const short smax = +2147483647;
    const short smin = -smax - 1;

    TEST_SHRT ("-2147483648", -1, -1, -1, 0, _e_, 11, 0, -1, smin);
    TEST_SHRT ("-2147483649", -1, -1, -1, 0, _ef, 11, 0, -1, smin);
    TEST_SHRT ("-9999999999", -1, -1, -1, 0, _ef, 11, 0, -1, smin);

    TEST_SHRT ("+2147483647", -1, -1, -1, 0, _e_, 11, 0, -1, smax);
    TEST_SHRT ("+2147483648", -1, -1, -1, 0, _ef, 11, 0, -1, smax);
    TEST_SHRT ("+9999999999", -1, -1, -1, 0, _ef, 11, 0, -1, smax);
    TEST_SHRT ("99999999999", -1, -1, -1, 0, _ef, 11, 0, -1, smax);
#endif   // _RWSTD_SHRT_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_ushrt (CharT*, Traits*,
            const char *cname,
            const char *tname)
{
    info (cname, tname, "unsigned short");

    test_extractor ((CharT*)0, (Traits*)0, (unsigned short*)0,
                    cname, tname, "unsigned short", "%hu");

#define TEST_USHRT(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (unsigned short, "%hu", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_USHRT_SIZE
    const unsigned short usmax = 65535U;

    TEST_SHRT ("65535",  -1, -1, -1, 0, _e_, 5, 0,  1, usmax);
    TEST_SHRT ("65536",  -1, -1, -1, 0, _ef, 5, 0,  1, usmax);
    TEST_SHRT ("99999",  -1, -1, -1, 0, _ef, 5, 0,  1, usmax);
#elif 4 == _RWSTD_USHRT_SIZE
    const unsigned short usmax = 4294967295U;
    
    TEST_SHRT ("4294967295", -1, -1, -1, 0, _e_, 10, 0, -1, usmax);
    TEST_SHRT ("4294967296", -1, -1, -1, 0, _ef, 10, 0, -1, usmax);
    TEST_SHRT ("9999999999", -1, -1, -1, 0, _ef, 10, 0, -1, usmax);
#endif   // _RWSTD_USHRT_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_int (CharT*, Traits*,
          const char *cname,
          const char *tname)
{
    info (cname, tname, "int");

    test_extractor ((CharT*)0, (Traits*)0, (int*)0,
                    cname, tname, "int", "%i");

#define TEST_INT(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (int, "%i", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_INT_SIZE
    const int imax = +32767;
    const int imin = -imax - 1;

    TEST_INT ("-32768",  -1, -1, -1, 0, _e_, 6, 0,  1, imin);
    TEST_INT ("-32769",  -1, -1, -1, 0, _ef, 6, 0,  1, imin);
    TEST_INT ("-99999",  -1, -1, -1, 0, _ef, 6, 0,  1, imin);

    TEST_INT ("+32767",  -1, -1, -1, 0, _e_, 6, 0,  1, imax);
    TEST_INT ("+32768",  -1, -1, -1, 0, _ef, 6, 0,  1, imax);
    TEST_INT ("+99999",  -1, -1, -1, 0, _ef, 6, 0,  1, imax);
#elif 4 == _RWSTD_INT_SIZE
    const int imax = +2147483647;
    const int imin = -imax - 1;

    TEST_INT ("-2147483648", -1, -1, -1, 0, _e_, 11, 0, -1, imin);
    TEST_INT ("-2147483649", -1, -1, -1, 0, _ef, 11, 0, -1, imin);
    TEST_INT ("-9999999999", -1, -1, -1, 0, _ef, 11, 0, -1, imin);

    TEST_INT ("+2147483647", -1, -1, -1, 0, _e_, 11, 0, -1, imax);
    TEST_INT ("+2147483648", -1, -1, -1, 0, _ef, 11, 0, -1, imax);
    TEST_INT ("99999999999", -1, -1, -1, 0, _ef, 11, 0, -1, imax);
#elif 8 == _RWSTD_INT_SIZE
    const int imax = +9223372036854775807;
    const int imin = -imax - 1;

    TEST_INT ("-9223372036854775808", -1, -1, -1, 0, _e_, 20, 0, -1, imin);
    TEST_INT ("-9223372036854775809", -1, -1, -1, 0, _ef, 20, 0, -1, imin);
    TEST_INT ("-9999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, imin);

    TEST_INT ("+9223372036854775807", -1, -1, -1, 0, _e_, 20, 0, -1, imax);
    TEST_INT ("+9223372036854775808", -1, -1, -1, 0, _ef, 20, 0, -1, imax);
    TEST_INT ("99999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, imax);
#endif   // _RWSTD_INT_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_uint (CharT*, Traits*,
           const char *cname,
           const char *tname)
{
    info (cname, tname, "unsigned int");

    test_extractor ((CharT*)0, (Traits*)0, (unsigned int*)0,
                     cname, tname, "unsigned int", "%u");

#define TEST_UINT(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (int, "%u", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_UINT_SIZE
    const unsigned uimax = 65535U;

    TEST_INT ("65535",  -1, -1, -1, 0, _e_, 5, 0,  1, uimax);
    TEST_INT ("65536",  -1, -1, -1, 0, _ef, 5, 0,  1, uimax);
    TEST_INT ("99999",  -1, -1, -1, 0, _ef, 5, 0,  1, uimax);
#elif 4 == _RWSTD_UINT_SIZE
    const unsigned uimax = 4294967295U;

    TEST_INT ("4294967295", -1, -1, -1, 0, _e_, 10, 0, -1, uimax);
    TEST_INT ("4294967296", -1, -1, -1, 0, _ef, 10, 0, -1, uimax);
    TEST_INT ("9999999999", -1, -1, -1, 0, _ef, 10, 0, -1, uimax);
#elif 8 == _RWSTD_UINT_SIZE
    const unsigned uimax = 9223372036854775807U;

    TEST_INT ("9223372036854775807", -1, -1, -1, 0, _e_, 10, 0, -1, uimax);
    TEST_INT ("9223372036854775808", -1, -1, -1, 0, _ef, 10, 0, -1, uimax);
    TEST_INT ("9999999999999999999", -1, -1, -1, 0, _ef, 10, 0, -1, uimax);
#endif   // _RWSTD_UINT_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_long (CharT*, Traits*,
           const char *cname,
           const char *tname)
{
    info (cname, tname, "long");

    test_extractor ((CharT*)0, (Traits*)0, (long*)0,
                    cname, tname, "long", "%li");

#define TEST_LONG(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (long, "%li", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_LONG_SIZE
    const long lmax = +32767L;
    const long lmin = -lmax - 1;

    TEST_LONG ("-32768",  -1, -1, -1, 0, _e_, 6, 0,  1, lmin);
    TEST_LONG ("-32769",  -1, -1, -1, 0, _ef, 6, 0,  1, lmin);
    TEST_LONG ("-99999",  -1, -1, -1, 0, _ef, 6, 0,  1, lmin);

    TEST_LONG ("+32767",  -1, -1, -1, 0, _e_, 6, 0,  1, lmax);
    TEST_LONG ("+32768",  -1, -1, -1, 0, _ef, 6, 0,  1, lmax);
    TEST_LONG ("+99999",  -1, -1, -1, 0, _ef, 6, 0,  1, lmax);
#elif 4 == _RWSTD_LONG_SIZE
    const long lmax = +2147483647L;
    const long lmin = -lmax - 1;

    TEST_LONG ("-2147483648", -1, -1, -1, 0, _e_, 11, 0, -1, lmin);
    TEST_LONG ("-2147483649", -1, -1, -1, 0, _ef, 11, 0, -1, lmin);
    TEST_LONG ("-9999999999", -1, -1, -1, 0, _ef, 11, 0, -1, lmin);

    TEST_LONG ("+2147483647", -1, -1, -1, 0, _e_, 11, 0, -1, lmax);
    TEST_LONG ("+2147483648", -1, -1, -1, 0, _ef, 11, 0, -1, lmax);
    TEST_LONG ("99999999999", -1, -1, -1, 0, _ef, 11, 0, -1, lmax);
#elif 8 == _RWSTD_LONG_SIZE
    const long lmax = +9223372036854775807L;
    const long lmin = -lmax - 1;

    TEST_LONG ("-9223372036854775808", -1, -1, -1, 0, _e_, 20, 0, -1, lmin);
    TEST_LONG ("-9223372036854775809", -1, -1, -1, 0, _ef, 20, 0, -1, lmin);
    TEST_LONG ("-9999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, lmin);

    TEST_LONG ("+9223372036854775807", -1, -1, -1, 0, _e_, 20, 0, -1, lmax);
    TEST_LONG ("+9223372036854775808", -1, -1, -1, 0, _ef, 20, 0, -1, lmax);
    TEST_LONG ("99999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, lmax);
#endif   // _RWSTD_LONG_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_ulong (CharT*, Traits*,
            const char *cname,
            const char *tname)
{
    info (cname, tname, "unsigned long");

    test_extractor ((CharT*)0, (Traits*)0, (unsigned long*)0,
                     cname, tname, "unsigned long", "%lu");

#define TEST_ULONG(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (unsigned long, "%lu", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_ULONG_SIZE
    const unsigned ulmax = 65535UL;

    TEST_ULONG ("65535",  -1, -1, -1, 0, _e_, 5, 0,  1, ulmax);
    TEST_ULONG ("65536",  -1, -1, -1, 0, _ef, 5, 0,  1, ulmax);
    TEST_ULONG ("99999",  -1, -1, -1, 0, _ef, 5, 0,  1, ulmax);
#elif 4 == _RWSTD_ULONG_SIZE
    const unsigned ulmax = 4294967295UL;

    TEST_ULONG ("4294967295", -1, -1, -1, 0, _e_, 10, 0, -1, ulmax);
    TEST_ULONG ("4294967296", -1, -1, -1, 0, _ef, 10, 0, -1, ulmax);
    TEST_ULONG ("9999999999", -1, -1, -1, 0, _ef, 10, 0, -1, ulmax);
#elif 8 == _RWSTD_ULONG_SIZE
    const unsigned ulmax = 9223372036854775807UL;

    TEST_ULONG ("9223372036854775807", -1, -1, -1, 0, _e_, 10, 0, -1, ulmax);
    TEST_ULONG ("9223372036854775808", -1, -1, -1, 0, _ef, 10, 0, -1, ulmax);
    TEST_ULONG ("9999999999999999999", -1, -1, -1, 0, _ef, 10, 0, -1, ulmax);
#endif   // _RWSTD_ULONG_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_llong (CharT*, Traits*,
            const char *cname,
            const char *tname)
{
    info (cname, tname, "long long");

    test_extractor ((CharT*)0, (Traits*)0, (long long*)0,
                    cname, tname, "long long", "%lli");

#define TEST_LLONG(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (long long, "%lli", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_LLONG_SIZE
    const long long llmax = +32767LL;
    const long long llmin = -llmax - 1;

    TEST_LLONG ("-32768",  -1, -1, -1, 0, _e_, 6, 0,  1, llmin);
    TEST_LLONG ("-32769",  -1, -1, -1, 0, _ef, 6, 0,  1, llmin);
    TEST_LLONG ("-99999",  -1, -1, -1, 0, _ef, 6, 0,  1, llmin);

    TEST_LLONG ("+32767",  -1, -1, -1, 0, _e_, 6, 0,  1, llmax);
    TEST_LLONG ("+32768",  -1, -1, -1, 0, _ef, 6, 0,  1, llmax);
    TEST_LLONG ("+99999",  -1, -1, -1, 0, _ef, 6, 0,  1, llmax);
#elif 4 == _RWSTD_LLONG_SIZE
    const long long llmax = +2147483647LL;
    const long long llmin = -llmax - 1;

    TEST_LLONG ("-2147483648", -1, -1, -1, 0, _e_, 11, 0, -1, llmin);
    TEST_LLONG ("-2147483649", -1, -1, -1, 0, _ef, 11, 0, -1, llmin);
    TEST_LLONG ("-9999999999", -1, -1, -1, 0, _ef, 11, 0, -1, llmin);

    TEST_LLONG ("+2147483647", -1, -1, -1, 0, _e_, 11, 0, -1, llmax);
    TEST_LLONG ("+2147483648", -1, -1, -1, 0, _ef, 11, 0, -1, llmax);
    TEST_LLONG ("99999999999", -1, -1, -1, 0, _ef, 11, 0, -1, llmax);
#elif 8 == _RWSTD_LLONG_SIZE
    const long long llmax = +9223372036854775807LL;
    const long long llmin = -llmax - 1;

    TEST_LLONG ("-9223372036854775808", -1, -1, -1, 0, _e_, 20, 0, -1, llmin);
    TEST_LLONG ("-9223372036854775809", -1, -1, -1, 0, _ef, 20, 0, -1, llmin);
    TEST_LLONG ("-9999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, llmin);

    TEST_LLONG ("+9223372036854775807", -1, -1, -1, 0, _e_, 20, 0, -1, llmax);
    TEST_LLONG ("+9223372036854775808", -1, -1, -1, 0, _ef, 20, 0, -1, llmax);
    TEST_LLONG ("99999999999999999999", -1, -1, -1, 0, _ef, 20, 0, -1, llmax);
#endif   // _RWSTD_LLONG_SIZE

}

/***********************************************************************/

template <class CharT, class Traits>
void
test_ullong (CharT*, Traits*,
             const char *cname,
             const char *tname)
{
    info (cname, tname, "unsigned long long");

    test_extractor ((CharT*)0, (Traits*)0, (unsigned long long*)0,
                    cname, tname, "unsigned long long", "%llu");

#define TEST_ULLONG(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (unsigned long long, "%llu", ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

#if 2 == _RWSTD_ULLONG_SIZE
    const unsigned long long ullmax = 65535ULL;

    TEST_ULLONG ("65535",  -1, -1, -1, 0, _e_, 5, 0,  1, ullmax);
    TEST_ULLONG ("65536",  -1, -1, -1, 0, _ef, 5, 0,  1, ullmax);
    TEST_ULLONG ("99999",  -1, -1, -1, 0, _ef, 5, 0,  1, ullmax);
#elif 4 == _RWSTD_ULLONG_SIZE
    const unsigned long long ullmax = 4294967295ULL;

    TEST_ULLONG ("4294967295", -1, -1, -1, 0, _e_, 10, 0, -1, ullmax);
    TEST_ULLONG ("4294967296", -1, -1, -1, 0, _ef, 10, 0, -1, ullmax);
    TEST_ULLONG ("9999999999", -1, -1, -1, 0, _ef, 10, 0, -1, ullmax);
#elif 8 == _RWSTD_ULLONG_SIZE
    const unsigned long long ullmax = 9223372036854775807ULL;

    TEST_ULLONG ("9223372036854775807", -1, -1, -1, 0, _e_, 10, 0, -1, ullmax);
    TEST_ULLONG ("9223372036854775808", -1, -1, -1, 0, _ef, 10, 0, -1, ullmax);
    TEST_ULLONG ("9999999999999999999", -1, -1, -1, 0, _ef, 10, 0, -1, ullmax);
#endif   // _RWSTD_ULLONG_SIZE

}

/***********************************************************************/

template <class CharT, class Traits, class FloatingType>
void
test_floating (CharT*, Traits*, FloatingType*,
               const char *cname,
               const char *tname,
               const char *fname,
               const char *fmt)
{
    typedef FloatingType T;

    test_extractor ((CharT*)0, (Traits*)0, (FloatingType*)0,
                    cname, tname, fname, fmt);

#define TEST_FLOATING(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (T, fmt, ss, fl, is, ex, ee, es, ne, fw, T (iv), T (ev))

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

    TEST_FLOATING ("", -1, -1, -1, 0, _ef, 0, 0, 1.0,  1.0);
}

/***********************************************************************/

template <class CharT, class Traits>
void
test_flt (CharT*, Traits*,
          const char *cname,
          const char *tname)
{
    const char fmt[]   = "%g";
    const char fname[] = "float";

    info (cname, tname, fname);

    test_floating ((CharT*)0, (Traits*)0, (float*)0,
                   cname, tname, fname, fmt);

#define TEST_FLT(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (float, fmt, ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

    TEST_FLT ("", -1, -1, -1, 0, _ef, 0, 0, 1.0f,  1.0f);

    rw_warn (0, 0, __LINE__,
             "%{$CLASS}::operator>>(%s&) insufficiently exercised", fname);
}

/***********************************************************************/

template <class CharT, class Traits>
void
test_dbl (CharT*, Traits*,
          const char *cname,
          const char *tname)
{
    const char fmt[]   = "%g";
    const char fname[] = "double";

    info (cname, tname, fname);

    test_floating ((CharT*)0, (Traits*)0, (double*)0,
                   cname, tname, fname, fmt);

#define TEST_DBL(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (double, fmt, ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

    TEST_DBL ("", -1, -1, -1, 0, _ef, 0, 0, 1.0,  1.0);

    rw_warn (0, 0, __LINE__,
             "%{$CLASS}::operator>>(%s&) insufficiently exercised", fname);
}

/***********************************************************************/

template <class CharT, class Traits>
void
test_ldbl (CharT*, Traits*,
           const char *cname,
           const char *tname)
{
    const char fmt[]   = "%Lg";
    const char fname[] = "long double";

    info (cname, tname, fname);

    test_floating ((CharT*)0, (Traits*)0, (long double*)0,
                   cname, tname, fname, fmt);

#define TEST_LDBL(ss, fl, is, ex, ee, es, ne, fw, iv, ev)   \
    TEST (long double, fmt, ss, fl, is, ex, ee, es, ne, fw, iv, ev)

    LocaleData locale_data = { 0, -1, -1, 0, 0, 0 };
    locale_data.whitespace = "";

    TEST_LDBL ("", -1, -1, -1, 0, _ef, 0, 0, 1.0L,  1.0L);

    rw_warn (0, 0, __LINE__,
             "%{$CLASS}::operator>>(%s&) insufficiently exercised", fname);
}

/***********************************************************************/

int opt_bool;
int opt_shrt;
int opt_ushrt;
int opt_int;
int opt_uint;
int opt_long;
int opt_ulong;
int opt_llong;
int opt_ullong;
int opt_flt;
int opt_dbl;
int opt_ldbl;


template <class CharT, class Traits>
void
test_extractor (CharT*, Traits*,
                const char *cname,
                const char *tname)
{
    // silence unused variable warnings
    (void)(___+__f+_e_+_ef+b__+b_f+be_+bef+DEC+OCT+HEX);

#undef TEST
#define TEST(T)                                                          \
    if (rw_note (0 == opt_ ## T, 0, __LINE__, "%s tests disabled", #T))   \
        test_ ## T ((CharT*)0, (Traits*)0, cname, tname)

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(bool&)
    TEST (bool);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(short&)
    TEST (shrt);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(unsigned short&)
    TEST (ushrt);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(int&)
    TEST (int);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(unsigned int&)
    TEST (uint);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(long&)
    TEST (long);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(unsigned long&)
    TEST (ulong);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(long long&)
    TEST (llong);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(unsigned long long&)
    TEST (ullong);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(float&)
    TEST (flt);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(double&)
    TEST (dbl);

    //////////////////////////////////////////////////////////////////
    // basic_istream::operator>>(long double&)
    TEST (ldbl);
}

/***********************************************************************/

static int opt_char;
static int opt_wchar;
static int opt_char_traits;
static int opt_user_traits;


static int
run_test (int, char**)
{
#undef TEST
#define TEST(CharT, Traits) \
    test_extractor ((CharT*)0, (Traits*)0, #CharT, #Traits)

    if (rw_note (0 <= opt_char && 0 <= opt_char_traits, 0, __LINE__,
                 "basic_istream<char, char_traits<char>>::operator>>() "
                 "tests disabled"))
        TEST (char, std::char_traits<char>);

    if (rw_note (0 <= opt_char && 0 <= opt_user_traits, 0, __LINE__,
                 "basic_istream<char, UserTraits<char>>::operator>>() "
                 "tests disabled"))
        TEST (char, UserTraits<char>);

#ifndef _RWSTD_NO_WCHAR_T

    if (rw_note (0 <= opt_wchar && 0 <= opt_char_traits, 0, __LINE__,
                 "basic_istream<wchar_t, char_traits<wchar_t>>::operator>>() "
                 "tests disabled"))
        TEST (wchar_t, std::char_traits<wchar_t>);

    if (rw_note (0 <= opt_wchar && 0 <= opt_user_traits, 0, __LINE__,
                 "basic_istream<wchar_t, UserTraits<wchar_t>>::operator>>() "
                 "tests disabled"))
        TEST (wchar_t, UserTraits<wchar_t>);

#endif   // _RWSTD_NO_WCHAR_T

    return 0;
}

/***********************************************************************/

int main (int argc, char *argv[])
{
    return rw_test (argc, argv, __FILE__,
                    "istream.formatted.arithmetic",
                    0 /* no comment */,
                    run_test,
                    "|-char~ "
                    "|-wchar_t~ "
                    "|-char_traits~ "
                    "|-UserTraits~ "
                    "|-bool~ "
                    "|-short~ "
                    "|-unsigned-short~ "
                    "|-int~ "
                    "|-unsigned-int~ "
                    "|-long~ "
                    "|-unsigned-long~ "
                    "|-long-long~ "
                    "|-unsigned-long-long~ "
                    "|-float~ "
                    "|-double~ "
                    "|-long-double~ "
                    "|-no-gcount# ",
                    &opt_char,
                    &opt_wchar,
                    &opt_char_traits,
                    &opt_user_traits,
                    &opt_bool,
                    &opt_shrt,
                    &opt_ushrt,
                    &opt_int,
                    &opt_uint,
                    &opt_long,
                    &opt_ulong,
                    &opt_llong,
                    &opt_ullong,
                    &opt_flt,
                    &opt_dbl,
                    &opt_ldbl,
                    &opt_no_gcount,
                    (void*)0   /* sentinel */);
}
