// xlocnum internal header

// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#pragma once
#ifndef _XLOCNUM_
#define _XLOCNUM_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iterator>
#include <streambuf>

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new

_EXTERN_C_UNLESS_PURE

extern _CRTIMP2_PURE float __CLRCALL_PURE_OR_CDECL _Stofx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, long, int*);
extern _CRTIMP2_PURE double __CLRCALL_PURE_OR_CDECL _Stodx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, long, int*);
extern _CRTIMP2_PURE long double __CLRCALL_PURE_OR_CDECL _Stoldx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, long, int*);
extern _CRTIMP2_PURE long __CLRCALL_PURE_OR_CDECL _Stolx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, int, int*);
extern _CRTIMP2_PURE unsigned long __CLRCALL_PURE_OR_CDECL _Stoulx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, int, int*);
extern _CRTIMP2_PURE long long __CLRCALL_PURE_OR_CDECL _Stollx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, int, int*);
extern _CRTIMP2_PURE unsigned long long __CLRCALL_PURE_OR_CDECL _Stoullx(
    const char*, _Out_opt_ _Deref_post_opt_valid_ char**, int, int*);

_END_EXTERN_C_UNLESS_PURE

_STD_BEGIN

inline double _Stodx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) { // convert string to double
    int& _Errno_ref = errno; // Nonzero cost, pay it once
    const int _Orig = _Errno_ref;

    _Errno_ref  = 0;
    double _Val = _CSTD strtod(_Str, _Endptr);
    *_Perr      = _Errno_ref;
    _Errno_ref  = _Orig;

    if (_Pten != 0) {
        _Val *= _CSTD pow(10.0, static_cast<double>(_Pten));
    }

    return _Val;
}

inline float _Stofx_v2(const char* _Str, char** _Endptr, int _Pten, int* _Perr) { // convert string to float
    int& _Errno_ref = errno; // Nonzero cost, pay it once
    const int _Orig = _Errno_ref;

    _Errno_ref = 0;
    float _Val = _CSTD strtof(_Str, _Endptr);
    *_Perr     = _Errno_ref;
    _Errno_ref = _Orig;

    if (_Pten != 0) {
        _Val *= _CSTD powf(10.0f, static_cast<float>(_Pten));
    }

    return _Val;
}

template <class _Elem, size_t _Base_size>
size_t _Find_elem(const _Elem (&_Base)[_Base_size],
    const _Elem _Ch) { // lookup _Ch in array storing NUL-terminated string _Base
                       // pre: _Base contains no nulls except for _Base[_Base_size - 1]
    return static_cast<size_t>(_Find_unchecked(_Base, _Base + (_Base_size - 1), _Ch) - _Base);
}

inline wchar_t* _Maklocwcs(const wchar_t* _Ptr) { // copy NTWCS to allocated storage
    const size_t _Count = _CSTD wcslen(_Ptr) + 1;

    wchar_t* _Ptrdest = static_cast<wchar_t*>(_calloc_dbg(_Count, sizeof(wchar_t), _CRT_BLOCK, __FILE__, __LINE__));

    if (!_Ptrdest) {
        _Xbad_alloc();
    }

    _CSTD wmemcpy(_Ptrdest, _Ptr, _Count);
    return _Ptrdest;
}

template <class _Elem>
class numpunct : public locale::facet { // facet for defining numeric punctuation text
private:
    friend _Tidy_guard<numpunct>;

public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t, unsigned short>,
        _FACET_SPECIALIZATION_MESSAGE);

    using string_type = basic_string<_Elem, char_traits<_Elem>, allocator<_Elem>>;
    using char_type   = _Elem;

    __PURE_APPDOMAIN_GLOBAL _CRTIMP2_PURE_IMPORT static locale::id id; // unique facet id

    _Elem decimal_point() const {
        return do_decimal_point();
    }

    _Elem thousands_sep() const {
        return do_thousands_sep();
    }

    string grouping() const {
        return do_grouping();
    }

    string_type falsename() const {
        return do_falsename();
    }

    string_type truename() const {
        return do_truename();
    }

    explicit numpunct(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        if (_Kseparator == 0) {
            _Kseparator = // NB: differs from "C" locale
                _Maklocchr(',', static_cast<_Elem*>(nullptr), _Lobj._Getcvt());
        }
        _END_LOCINFO()
    }

    numpunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) {
        _Init(_Lobj, _Isdef);
    }

    static size_t _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new numpunct<_Elem>(_Locinfo(_Ploc->c_str()), 0, true);
        }
        return _X_NUMERIC;
    }

protected:
    virtual __CLR_OR_THIS_CALL ~numpunct() noexcept {
        _Tidy();
    }

    numpunct(const char* _Locname, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) {
        _BEGIN_LOCINFO(_Lobj(_Locname))
        _Init(_Lobj, _Isdef);
        _END_LOCINFO()
    }

    template <class _Elem2>
    void _Getvals(_Elem2, const lconv* _Ptr, _Locinfo::_Cvtvec _Cvt) { // get values
        _Dp         = _Maklocchr(_Ptr->decimal_point[0], static_cast<_Elem2*>(nullptr), _Cvt);
        _Kseparator = _Maklocchr(_Ptr->thousands_sep[0], static_cast<_Elem2*>(nullptr), _Cvt);
    }

    void _Getvals(wchar_t, const lconv* _Ptr, _Locinfo::_Cvtvec) { // get values
        _Dp         = static_cast<_Elem>(_Ptr->_W_decimal_point[0]);
        _Kseparator = static_cast<_Elem>(_Ptr->_W_thousands_sep[0]);
    }

    void _Init(const _Locinfo& _Lobj, bool _Isdef = false) { // initialize from _Lobj
        const lconv* _Ptr      = _Lobj._Getlconv();
        _Locinfo::_Cvtvec _Cvt = _Lobj._Getcvt(); // conversion information

        _Grouping  = nullptr;
        _Falsename = nullptr;
        _Truename  = nullptr;

        _Tidy_guard<numpunct> _Guard{this};
        _Grouping      = _Maklocstr(_Isdef ? "" : _Ptr->grouping, static_cast<char*>(nullptr), _Lobj._Getcvt());
        _Falsename     = _Maklocstr(_Lobj._Getfalse(), static_cast<_Elem*>(nullptr), _Cvt);
        _Truename      = _Maklocstr(_Lobj._Gettrue(), static_cast<_Elem*>(nullptr), _Cvt);
        _Guard._Target = nullptr;

        if (_Isdef) { // apply defaults for required facets
            // _Grouping = _Maklocstr("", static_cast<char *>(nullptr), _Cvt);
            _Dp         = _Maklocchr('.', static_cast<_Elem*>(nullptr), _Cvt);
            _Kseparator = _Maklocchr(',', static_cast<_Elem*>(nullptr), _Cvt);
        } else {
            _Getvals(_Elem{}, _Ptr, _Cvt);
        }
    }

    virtual _Elem __CLR_OR_THIS_CALL do_decimal_point() const {
        return _Dp;
    }

    virtual _Elem __CLR_OR_THIS_CALL do_thousands_sep() const {
        return _Kseparator;
    }

    virtual string __CLR_OR_THIS_CALL do_grouping() const {
        return string(_Grouping);
    }

    virtual string_type __CLR_OR_THIS_CALL do_falsename() const {
        return string_type(_Falsename);
    }

    virtual string_type __CLR_OR_THIS_CALL do_truename() const {
        return string_type(_Truename);
    }

private:
    void _Tidy() noexcept { // free all storage
        _CSTD free(const_cast<char*>(_Grouping));
        _CSTD free(const_cast<_Elem*>(_Falsename));
        _CSTD free(const_cast<_Elem*>(_Truename));
    }

    const char* _Grouping; // grouping string, "" for "C" locale
    _Elem _Dp; // decimal point, '.' for "C" locale
    _Elem _Kseparator; // thousands separator, '\0' for "C" locale
    const _Elem* _Falsename; // name for false, "false" for "C" locale
    const _Elem* _Truename; // name for true, "true" for "C" locale
};

template <class _Elem>
class numpunct_byname : public numpunct<_Elem> { // numpunct for named locale
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t>, _FACET_SPECIALIZATION_MESSAGE);

    explicit numpunct_byname(const char* _Locname, size_t _Refs = 0)
        : numpunct<_Elem>(_Locname, _Refs) {} // construct for named locale

    explicit numpunct_byname(const string& _Str, size_t _Refs = 0)
        : numpunct<_Elem>(_Str.c_str(), _Refs) {} // construct for named locale

protected:
    virtual __CLR_OR_THIS_CALL ~numpunct_byname() noexcept {}
};

#if !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) // TRANSITION, VSO-578955
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // __clang__

template <class _Elem>
__PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__
#endif // !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE)

template <class _Elem, class _InIt = istreambuf_iterator<_Elem, char_traits<_Elem>>>
class num_get : public locale::facet { // facet for converting text to encoded numbers
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t, unsigned short>,
        _FACET_SPECIALIZATION_MESSAGE);

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new num_get<_Elem, _InIt>(_Locinfo(_Ploc->c_str()));
        }

        return _X_NUMERIC;
    }

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

protected:
    virtual __CLR_OR_THIS_CALL ~num_get() noexcept {}

    void _Init(const _Locinfo&) {} // initialize from _Locinfo object

public:
    explicit __CLR_OR_THIS_CALL num_get(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL num_get(const _Locinfo& _Lobj, size_t _Refs = 0) : locale::facet(_Refs) {
        _Init(_Lobj);
    }

    using char_type = _Elem;
    using iter_type = _InIt;

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        bool& _Val) const { // get bool from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned short& _Val) const { // get unsigned short from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned int& _Val) const { // get unsigned int from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long& _Val) const { // get long from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned long& _Val) const { // get unsigned long from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long long& _Val) const { // get long long from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned long long& _Val) const { // get unsigned long long from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        float& _Val) const { // get float from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        double& _Val) const { // get double from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long double& _Val) const { // get long double from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

    _InIt __CLR_OR_THIS_CALL get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        void*& _Val) const { // get void pointer from [_First, _Last) into _Val
        return do_get(_First, _Last, _Iosbase, _State, _Val);
    }

protected:
    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        bool& _Val) const { // get bool from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        if (_Iosbase.flags() & ios_base::boolalpha) { // get false name or true name
            const auto& _Punct_fac = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
            basic_string<_Elem> _Str(static_cast<size_t>(1), _Elem{});
            _Str += _Punct_fac.falsename();
            _Str.push_back(_Elem{});
            _Str += _Punct_fac.truename(); // construct "\0false\0true"
            switch (_Getloctxt(_First, _Last, 2, _Str.c_str(), _Case_sensitive::_Yes)) {
            case 0:
                _Val = false;
                break;
            case 1:
                _Val = true;
                break;
            default:
                _Val   = false;
                _State = ios_base::failbit;
                break;
            }
        } else { // get long value
            char _Ac[_MAX_INT_DIG];
            const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc()); // gather field
            if (_Ac[0] == '\0') { // Handle N4727 [facet.num.get.virtuals]/3.6:
                                  //  "zero, if the conversion function does not convert the entire field."
                                  // We should still do numeric conversion with bad digit separators, instead of
                                  // setting 0, but we can't distinguish that from _Getifld's interface, and _Getifld's
                                  // interface can't be changed as it is an exported function.
                                  // TRANSITION, ABI: Fixing that when ABI allows is tracked by VSO-591516.
                _Val   = false;
                _State = ios_base::failbit;
            } else {
                char* _Ep;
                int _Errno;
                const long _Ans = _CSTD _Stolx(_Ac, &_Ep, _Base, &_Errno); // convert
                if (_Ep == _Ac || _Errno != 0) {
                    _Val   = true;
                    _State = ios_base::failbit;
                } else {
                    _Val = _Ans != 0;
                    if (_Ans != 0 && _Ans != 1) {
                        _State = ios_base::failbit;
                    }
                }
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned short& _Val) const { // get unsigned short from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc()); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0;
        } else {
            const bool _Minus   = _Ac[0] == '-';
            const char* _Digits = _Ac;
            // C11 7.22.1.4/5: the sequence of characters starting with the first digit
            // is interpreted as an integer constant according to the rules of 6.4.4.1
            if (_Minus) { // skip over minus to start with the first digit
                ++_Digits;
            }

            char* _Ep;
            int _Errno;
            const unsigned long _Tmp = _CSTD _Stoulx(_Digits, &_Ep, _Base, &_Errno); // convert
            _Val                     = static_cast<unsigned short>(_Tmp);
            if (_Ep == _Digits || _Errno != 0 || _Tmp > USHRT_MAX) {
                _State = ios_base::failbit;
                _Val   = USHRT_MAX;
            } else if (_Minus) { // C11 7.22.1.4/5:  If the subject sequence begins with a minus sign,
                                 // the value resulting from the conversion is negated (in the return type).
                _Val = static_cast<unsigned short>(0 - _Val);
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned int& _Val) const { // get unsigned int from [_First, _Last) into _Val
        static_assert(sizeof(unsigned int) == sizeof(unsigned long),
            "Bad overflow assumptions due to sizeof(unsigned int) != sizeof(unsigned long)");
        unsigned long _Tmp;
        _First = num_get::do_get(_First, _Last, _Iosbase, _State, _Tmp); // avoid virtual call for perf
        _Val   = _Tmp;
        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long& _Val) const { // get long from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc()); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0;
        } else {
            char* _Ep;
            int _Errno;
            _Val = _CSTD _Stolx(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned long& _Val) const { // get unsigned long from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc()); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0;
        } else {
            char* _Ep;
            int _Errno;
            _Val = _CSTD _Stoulx(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long long& _Val) const { // get long long from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc());
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0;
        } else {
            char* _Ep;
            int _Errno;
            _Val = _CSTD _Stollx(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        unsigned long long& _Val) const { // get unsigned long long from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, _Iosbase.flags(), _Iosbase.getloc());
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0;
        } else {
            int _Errno;
            char* _Ep;
            _Val = _CSTD _Stoullx(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

// TRANSITION, ABI: Sentinel value used by num_get::do_get()
// to enable correct "V2" behavior in _Getffld() and _Getffldx()
#define _ENABLE_V2_BEHAVIOR 1000000000

// Size of char buffer used by num_get::do_get() for float/double/long double
#define _FLOATING_BUFFER_SIZE (_MAX_EXP_DIG + _MAX_SIG_DIG_V2 + 16)

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        float& _Val) const { // get float from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_FLOATING_BUFFER_SIZE];
        int _Hexexp     = _ENABLE_V2_BEHAVIOR;
        const int _Base = _Getffld(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0.0f;
        } else {
            int _Errno;
            char* _Ep;
            _Val = _Stofx_v2(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
                _Val   = 0.0f;
            } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) {
                _Val = _CSTD ldexpf(_Val, 4 * _Hexexp);
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        double& _Val) const { // get double from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_FLOATING_BUFFER_SIZE];
        int _Hexexp     = _ENABLE_V2_BEHAVIOR;
        const int _Base = _Getffld(_Ac, _First, _Last, _Iosbase, &_Hexexp); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = 0.0;
        } else {
            int _Errno;
            char* _Ep;
            _Val = _Stodx_v2(_Ac, &_Ep, _Base, &_Errno); // convert
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
                _Val   = 0.0;
            } else if (_Hexexp != _ENABLE_V2_BEHAVIOR && _Hexexp != 0) {
                _Val = _CSTD ldexp(_Val, 4 * _Hexexp);
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }
#undef _FLOATING_BUFFER_SIZE

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        long double& _Val) const { // get long double from [_First, _Last) into _Val
        static_assert(sizeof(double) == sizeof(long double), "Bad assumption: sizeof(double) == sizeof(long double).");
        double _Result;
        _First = num_get::do_get(_First, _Last, _Iosbase, _State, _Result); // avoid virtual call for perf
        _Val   = _Result;
        return _First;
    }

    virtual _InIt __CLR_OR_THIS_CALL do_get(_InIt _First, _InIt _Last, ios_base& _Iosbase, ios_base::iostate& _State,
        void*& _Val) const { // get void pointer from [_First, _Last) into _Val
        _Adl_verify_range(_First, _Last);
        char _Ac[_MAX_INT_DIG];
        const int _Base = _Getifld(_Ac, _First, _Last, ios_base::hex, _Iosbase.getloc()); // gather field
        if (_Ac[0] == '\0') { // ditto "fails to convert the entire field" / VSO-591516
            _State = ios_base::failbit;
            _Val   = nullptr;
        } else {
            int _Errno;
            char* _Ep;
#ifdef _WIN64
            _Val = reinterpret_cast<void*>(_CSTD _Stoullx(_Ac, &_Ep, _Base, &_Errno));
#else // ^^^ _WIN64 ^^^ // vvv !_WIN64 vvv
            _Val = reinterpret_cast<void*>(_CSTD _Stoulx(_Ac, &_Ep, _Base, &_Errno));
#endif // _WIN64
            if (_Ep == _Ac || _Errno != 0) {
                _State = ios_base::failbit;
                _Val   = nullptr;
            }
        }

        if (_First == _Last) {
            _State |= ios_base::eofbit;
        }

        return _First;
    }

private:
    int __CLRCALL_OR_CDECL _Getifld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base::fmtflags _Basefield,
        const locale& _Loc) const { // get integer field from [_First, _Last) into _Ac
        const auto& _Punct_fac  = _STD use_facet<numpunct<_Elem>>(_Loc);
        const string _Grouping  = _Punct_fac.grouping();
        const _Elem _Kseparator = _Grouping.size() == 0 ? _Elem{} : _Punct_fac.thousands_sep();

        constexpr int _Numget_signoff = 22;
        constexpr int _Numget_xoff    = 24;
        static constexpr char _Src[]  = "0123456789ABCDEFabcdef-+Xx";
        _Elem _Atoms[sizeof(_Src)];
        const ctype<_Elem>& _Ctype_fac = _STD use_facet<ctype<_Elem>>(_Loc);
        _Ctype_fac.widen(_STD begin(_Src), _STD end(_Src), _Atoms);

        char* _Ptr = _Ac;

        if (_First != _Last) {
            if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign
                *_Ptr++ = '+';
                ++_First;
            } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign
                *_Ptr++ = '-';
                ++_First;
            }
        }

        _Basefield &= ios_base::basefield;

        int _Base;
        if (_Basefield == ios_base::oct) {
            _Base = 8;
        } else if (_Basefield == ios_base::hex) {
            _Base = 16;
        } else if (_Basefield == ios_base::_Fmtzero) {
            _Base = 0;
        } else {
            _Base = 10;
        }

        bool _Seendigit = false; // seen a digit in input
        bool _Nonzero   = false; // seen a nonzero digit in input

        if (_First != _Last && *_First == _Atoms[0]) { // leading zero, look for 0x, 0X
            _Seendigit = true;
            ++_First;
            if (_First != _Last && (*_First == _Atoms[_Numget_xoff + 1] || *_First == _Atoms[_Numget_xoff])
                && (_Base == 0 || _Base == 16)) {
                _Base      = 16;
                _Seendigit = false;
                ++_First;
            } else if (_Base == 0) {
                _Base = 8;
            }
        }

        const auto _Dlen = static_cast<size_t>(_Base == 0 || _Base == 10 ? 10 : _Base == 8 ? 8 : 16 + 6);
        string _Groups(1, static_cast<char>(_Seendigit));
        size_t _Group = 0;

        for (char* const _Pe = &_Ac[_MAX_INT_DIG - 1]; _First != _Last; ++_First) { // look for digits and separators
            size_t _Idx = _Find_elem(_Atoms, *_First);
            if (_Idx < _Dlen) { // got a digit, characterize it and add to group size
                *_Ptr = _Src[_Idx];
                if ((_Nonzero || *_Ptr != '0') && _Ptr < _Pe) {
                    ++_Ptr;
                    _Nonzero = true;
                }

                _Seendigit = true;
                if (_Groups[_Group] != CHAR_MAX) {
                    ++_Groups[_Group];
                }
            } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) {
                break; // not a group separator, done
            } else { // add a new group to _Groups string
                _Groups.push_back('\0');
                ++_Group;
            }
        }

        if (_Group != 0) {
            if ('\0' < _Groups[_Group]) {
                ++_Group; // add trailing group to group count
            } else {
                _Seendigit = false; // trailing separator, fail
            }
        }

        for (const char* _Pg = &_Grouping[0]; _Seendigit && 0 < _Group;) {
            if (*_Pg == CHAR_MAX) {
                break; // end of grouping constraints to check
            } else if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) {
                _Seendigit = false; // bad group size, fail
            } else if ('\0' < _Pg[1]) {
                ++_Pg; // group size okay, advance to next test
            }
        }

        if (_Seendigit && !_Nonzero) {
            *_Ptr++ = '0'; // zero field, replace stripped zero(s)
        } else if (!_Seendigit) {
            _Ptr = _Ac; // roll back pointer to indicate failure
        }

        *_Ptr = '\0';
        return _Base;
    }

    int __CLRCALL_OR_CDECL _Getffld(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase,
        int* _Phexexp) const { // get floating-point field from [_First, _Last) into _Ac
        if ((_Iosbase.flags() & ios_base::floatfield) == ios_base::hexfloat) {
            return _Getffldx(_Ac, _First, _Last, _Iosbase, _Phexexp); // hex format
        }

        const auto& _Punct_fac = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
        const string _Grouping = _Punct_fac.grouping();
        char* _Ptr             = _Ac;
        bool _Bad              = false;
        bool _Sticky           = false;

        constexpr int _Numget_signoff = 10;
        constexpr int _Numget_eoff    = 12;
        static constexpr char _Src[]  = "0123456789-+Ee";
        _Elem _Atoms[sizeof(_Src)];
        const ctype<_Elem>& _Ctype_fac = _STD use_facet<ctype<_Elem>>(_Iosbase.getloc());
        _Ctype_fac.widen(_STD begin(_Src), _STD end(_Src), _Atoms);

        if (_First != _Last) {
            if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign
                *_Ptr++ = '+';
                ++_First;
            } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign
                *_Ptr++ = '-';
                ++_First;
            }
        }

        char* _Leading = _Ptr; // remember backstop
        *_Ptr++        = '0'; // backstop carries from sticky bit

        bool _Seendigit  = false; // seen a digit in input
        int _Significant = 0; // number of significant digits
        int _Pten        = 0; // power of 10 multiplier
        size_t _Idx;

        const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1);

        const char* _Pg = &_Grouping[0];
        if (*_Pg == CHAR_MAX || *_Pg <= '\0') {
            for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) {
                if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky
                    ++_Pten;
                    if (0 < _Idx) {
                        _Sticky = true;
                    }
                } else if (_Idx != 0 || _Significant != 0) { // save a significant digit
                    *_Ptr++ = _Src[_Idx];
                    ++_Significant;
                }
            }
        } else { // grouping specified, gather digits and group sizes
            const _Elem _Kseparator = _Grouping.size() == 0 ? _Elem{} : _Punct_fac.thousands_sep();
            string _Groups(1, '\0');
            size_t _Group = 0;

            for (; _First != _Last; ++_First) {
                if ((_Idx = _Find_elem(_Atoms, *_First)) < 10) { // got a digit, add to group size
                    _Seendigit = true;
                    if (_Max_sig_dig <= _Significant) { // enough digits, scale by 10 and update _Sticky
                        ++_Pten;
                        if (0 < _Idx) {
                            _Sticky = true;
                        }
                    } else if (_Idx != 0 || _Significant != 0) { // save a significant digit
                        *_Ptr++ = _Src[_Idx];
                        ++_Significant;
                    }

                    if (_Groups[_Group] != CHAR_MAX) {
                        ++_Groups[_Group];
                    }
                } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) {
                    break; // not a group separator, done
                } else { // add a new group to _Groups string
                    _Groups.push_back('\0');
                    ++_Group;
                }
            }

            if (_Group != 0) {
                if ('\0' < _Groups[_Group]) {
                    ++_Group; // add trailing group to group count
                } else {
                    _Bad = true; // trailing separator, fail
                }
            }

            while (!_Bad && 0 < _Group) {
                if (*_Pg == CHAR_MAX) {
                    break; // end of grouping constraints to check
                }

                if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) {
                    _Bad = true; // bad group size, fail
                } else if ('\0' < _Pg[1]) {
                    ++_Pg; // group size okay, advance to next test
                }
            }
        }

        if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add .
            *_Ptr++ = localeconv()->decimal_point[0];
            ++_First;
        }

        if (*_Phexexp != _ENABLE_V2_BEHAVIOR && _Significant == 0) { // 0000. so far
            for (; _First != _Last && *_First == _Atoms[0]; _Seendigit = true, (void) ++_First) {
                --_Pten; // just count leading fraction zeros
            }

            if (_Pten < 0) { // put one back
                *_Ptr++ = '0';
                ++_Pten;
            }
        }

        for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) {
            if (_Significant < _Max_sig_dig) { // save a significant fraction digit
                *_Ptr++ = _Src[_Idx];
                ++_Significant;
            } else if (0 < _Idx) {
                _Sticky = true; // just update _Sticky
            }
        }

        if (_Sticky) { // increment ls digit in memory of those lost
            char* _Px = _Ptr;
            while (--_Px != _Leading) { // add in carry
                if (*_Px != localeconv()->decimal_point[0]) { // not decimal point
                    if (*_Px != '9') { // carry stops here
                        ++*_Px;
                        break;
                    }

                    *_Px = '0'; // propagate carry
                }
            }

            if (_Px == _Leading) { // change "999..." to "1000..." and scale _Pten
                *_Px = '1';
                ++_Pten;
            }
        }

        if (_Seendigit && _First != _Last
            && (*_First == _Atoms[_Numget_eoff + 1]
                || *_First == _Atoms[_Numget_eoff])) { // 'e' or 'E', collect exponent
            *_Ptr++ = 'e';
            ++_First;
            _Seendigit   = false;
            _Significant = 0;

            if (_First != _Last) {
                if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign
                    *_Ptr++ = '+';
                    ++_First;
                } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign
                    *_Ptr++ = '-';
                    ++_First;
                }
            }

            for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros
                _Seendigit = true;
            }

            if (_Seendigit) {
                *_Ptr++ = '0'; // put one back
            }

            for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < 10; _Seendigit = true, (void) ++_First) {
                if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit
                    *_Ptr++ = _Src[_Idx];
                    ++_Significant;
                }
            }
        }

        if (_Bad || !_Seendigit) {
            _Ptr = _Ac; // roll back pointer to indicate failure
        }

        *_Ptr = '\0';
        return _Pten;
    }

    int __CLRCALL_OR_CDECL _Getffldx(char* _Ac, _InIt& _First, _InIt& _Last, ios_base& _Iosbase,
        int* _Phexexp) const { // get hex floating-point field from [_First, _Last) into _Ac
        const auto& _Punct_fac = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
        const string _Grouping = _Punct_fac.grouping();

        constexpr int _Numget_signoff = 22;
        constexpr int _Numget_xoff    = 24;
        constexpr int _Numget_poff    = 26;
        static constexpr char _Src[]  = "0123456789ABCDEFabcdef-+XxPp";
        _Elem _Atoms[sizeof(_Src)];
        const ctype<_Elem>& _Ctype_fac = _STD use_facet<ctype<_Elem>>(_Iosbase.getloc());
        _Ctype_fac.widen(_STD begin(_Src), _STD end(_Src), _Atoms);

        char* _Ptr = _Ac;
        bool _Bad  = false;
        size_t _Idx;

        if (_First != _Last) {
            if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign
                *_Ptr++ = '+';
                ++_First;
            } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign
                *_Ptr++ = '-';
                ++_First;
            }
        }

        *_Ptr++ = '0';
        *_Ptr++ = 'x';

        bool _Seendigit  = false; // seen a digit in input
        int _Significant = 0; // number of significant digits
        int _Phex        = 0; // power of 10 multiplier

        if (_First != _Last && *_First == _Atoms[0]) {
            if (++_First != _Last && (*_First == _Atoms[_Numget_xoff + 1] || *_First == _Atoms[_Numget_xoff])) {
                ++_First; // discard any 0x or 0X
            } else {
                _Seendigit = true; // '0' not followed by 'x' or 'X'
            }
        }

        const int _Max_sig_dig = (*_Phexexp == _ENABLE_V2_BEHAVIOR ? _MAX_SIG_DIG_V2 : _MAX_SIG_DIG_V1);

        const char* _Pg = &_Grouping[0];
        if (*_Pg == CHAR_MAX || *_Pg <= '\0') {
            for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff;
                 _Seendigit = true, (void) ++_First) {
                if (_Max_sig_dig <= _Significant) {
                    ++_Phex; // just scale by 10
                } else if (_Idx != 0 || _Significant != 0) { // save a significant digit
                    *_Ptr++ = _Src[_Idx];
                    ++_Significant;
                }
            }
        } else { // grouping specified, gather digits and group sizes
            const _Elem _Kseparator = _Grouping.size() == 0 ? _Elem{} : _Punct_fac.thousands_sep();
            string _Groups(1, '\0');
            size_t _Group = 0;

            for (; _First != _Last; ++_First) {
                if ((_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff) { // got a digit, add to group size
                    _Seendigit = true;
                    if (_Max_sig_dig <= _Significant) {
                        ++_Phex; // just scale by 10
                    } else if (_Idx != 0 || _Significant != 0) { // save a significant digit
                        *_Ptr++ = _Src[_Idx];
                        ++_Significant;
                    }

                    if (_Groups[_Group] != CHAR_MAX) {
                        ++_Groups[_Group];
                    }
                } else if (_Groups[_Group] == '\0' || _Kseparator == _Elem{} || *_First != _Kseparator) {
                    break; // not a group separator, done
                } else { // add a new group to _Groups string
                    _Groups.push_back('\0');
                    ++_Group;
                }
            }

            if (_Group != 0) {
                if ('\0' < _Groups[_Group]) {
                    ++_Group; // add trailing group to group count
                } else {
                    _Bad = true; // trailing separator, fail
                }
            }

            while (!_Bad && 0 < _Group) {
                if (*_Pg == CHAR_MAX) {
                    break; // end of grouping constraints to check
                }

                if ((0 < --_Group && *_Pg != _Groups[_Group]) || (0 == _Group && *_Pg < _Groups[_Group])) {
                    _Bad = true; // bad group size, fail
                } else if ('\0' < _Pg[1]) {
                    ++_Pg; // group size okay, advance to next test
                }
            }
        }

        if (_Seendigit && _Significant == 0) {
            *_Ptr++ = '0'; // save at least one leading digit
        }

        if (_First != _Last && *_First == _Punct_fac.decimal_point()) { // add .
            *_Ptr++ = localeconv()->decimal_point[0];
            ++_First;
        }

        if (_Significant == 0) { // 0000. so far
            for (; _First != _Last && *_First == _Atoms[0]; _Seendigit = true, (void) ++_First) {
                --_Phex; // just count leading fraction zeros
            }

            if (_Phex < 0) { // put one back
                *_Ptr++ = '0';
                ++_Phex;
            }
        }

        for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff;
             _Seendigit = true, (void) ++_First) {
            if (_Significant < _Max_sig_dig) { // save a significant fraction digit
                *_Ptr++ = _Src[_Idx];
                ++_Significant;
            }
        }

        if (_Seendigit && _First != _Last
            && (*_First == _Atoms[_Numget_poff + 1]
                || *_First == _Atoms[_Numget_poff])) { // 'p' or 'P', collect exponent
            *_Ptr++ = 'p';
            ++_First;
            _Seendigit   = false;
            _Significant = 0;

            if (_First != _Last) {
                if (*_First == _Atoms[_Numget_signoff + 1]) { // gather plus sign
                    *_Ptr++ = '+';
                    ++_First;
                } else if (*_First == _Atoms[_Numget_signoff]) { // gather minus sign
                    *_Ptr++ = '-';
                    ++_First;
                }
            }

            for (; _First != _Last && *_First == _Atoms[0]; ++_First) { // strip leading zeros
                _Seendigit = true;
            }

            if (_Seendigit) {
                *_Ptr++ = '0'; // put one back
            }

            for (; _First != _Last && (_Idx = _Find_elem(_Atoms, *_First)) < _Numget_signoff;
                 _Seendigit = true, (void) ++_First) {
                if (_Significant < _MAX_EXP_DIG) { // save a significant exponent digit
                    *_Ptr++ = _Src[_Idx];
                    ++_Significant;
                }
            }
        }

        if (_Bad || !_Seendigit) {
            _Ptr = _Ac; // roll back pointer to indicate failure
        }

        *_Ptr     = '\0';
        *_Phexexp = _Phex; // power of 16 multiplier
        return 0; // power of 10 multiplier
    }

#undef _ENABLE_V2_BEHAVIOR
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // __clang__

template <class _Elem, class _InIt>
__PURE_APPDOMAIN_GLOBAL locale::id num_get<_Elem, _InIt>::id;

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__

template <class _Ty>
struct _Hex_float_precision;

template <>
struct _Hex_float_precision<double> {
    // the number of hexits needed to represent (DBL_MANT_DIG - 1) bits after the radix point exactly
    static constexpr int value = ((DBL_MANT_DIG - 1) + 3) / 4;
};

template <>
struct _Hex_float_precision<long double> {
    // the number of hexits needed to represent (LDBL_MANT_DIG - 1) bits after the radix point exactly
    static constexpr int value = ((LDBL_MANT_DIG - 1) + 3) / 4;
};

template <class _Ty>
int _Float_put_desired_precision(const streamsize _Precision, const ios_base::fmtflags _Float_flags) {
    const bool _Is_hex = _Float_flags == (ios_base::fixed | ios_base::scientific);
    if (_Is_hex) {
        return _Hex_float_precision<_Ty>::value;
    }

    if (_Precision > 0) {
        return static_cast<int>(_Precision);
    } else if (_Precision == 0) {
        const bool _Is_default_float = _Float_flags == 0;
        if (_Is_default_float) {
            return 1;
        } else {
            return 0;
        }
    } else {
        constexpr int _Default_precision = 6;
        return _Default_precision;
    }
}

template <class _Elem, class _OutIt = ostreambuf_iterator<_Elem, char_traits<_Elem>>>
class num_put : public locale::facet { // facet for converting encoded numbers to text
public:
    static_assert(!_ENFORCE_FACET_SPECIALIZATIONS || _Is_any_of_v<_Elem, char, wchar_t, unsigned short>,
        _FACET_SPECIALIZATION_MESSAGE);

    static size_t __CLRCALL_OR_CDECL _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) {
        // return locale category mask and construct standard facet
        if (_Ppf && !*_Ppf) {
            *_Ppf = new num_put<_Elem, _OutIt>(_Locinfo(_Ploc->c_str()));
        }

        return _X_NUMERIC;
    }

    __PURE_APPDOMAIN_GLOBAL static locale::id id; // unique facet id

protected:
    virtual __CLR_OR_THIS_CALL ~num_put() noexcept {}

    void __CLR_OR_THIS_CALL _Init(const _Locinfo&) {} // initialize from _Locinfo object

public:
    explicit __CLR_OR_THIS_CALL num_put(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale
        _BEGIN_LOCINFO(_Lobj)
        _Init(_Lobj);
        _END_LOCINFO()
    }

    __CLR_OR_THIS_CALL num_put(const _Locinfo& _Lobj, size_t _Refs = 0) : locale::facet(_Refs) {
        _Init(_Lobj);
    }

    using char_type = _Elem;
    using iter_type = _OutIt;

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, bool _Val) const { // put formatted bool to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long _Val) const { // put formatted long to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill,
        unsigned long _Val) const { // put formatted unsigned long to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long long _Val) const { // put formatted long long to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill,
        unsigned long long _Val) const { // put formatted unsigned long long to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, double _Val) const { // put formatted double to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long double _Val) const { // put formatted long double to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

    _OutIt __CLR_OR_THIS_CALL put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const void* _Val) const { // put formatted void pointer to _Dest
        return do_put(_Dest, _Iosbase, _Fill, _Val);
    }

protected:
    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, bool _Val) const { // put formatted bool to _Dest
        if (!(_Iosbase.flags() & ios_base::boolalpha)) {
            return do_put(_Dest, _Iosbase, _Fill, static_cast<long>(_Val));
        } else { // put "false" or "true"
            const auto& _Punct_fac = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
            basic_string<_Elem> _Str;
            if (_Val) {
                _Str.assign(_Punct_fac.truename());
            } else {
                _Str.assign(_Punct_fac.falsename());
            }

            size_t _Fillcount;
            if (_Iosbase.width() <= 0 || static_cast<size_t>(_Iosbase.width()) <= _Str.size()) {
                _Fillcount = 0;
            } else {
                _Fillcount = static_cast<size_t>(_Iosbase.width()) - _Str.size();
            }

            if ((_Iosbase.flags() & ios_base::adjustfield) != ios_base::left) { // put leading fill
                _Dest      = _Rep(_Dest, _Fill, _Fillcount);
                _Fillcount = 0;
            }
            _Dest = _Put(_Dest, _Str.c_str(), _Str.size()); // put field
            _Iosbase.width(0);
            return _Rep(_Dest, _Fill, _Fillcount); // put trailing fill
        }
    }

#pragma warning(push)
#pragma warning(disable : 4774) // format string expected in argument N is not a string literal (/Wall)
    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long _Val) const { // put formatted long to _Dest
        char _Buf[2 * _MAX_INT_DIG];
        char _Fmt[6];

        return _Iput(_Dest, _Iosbase, _Fill, _Buf,
            static_cast<size_t>(_CSTD sprintf_s(_Buf, sizeof(_Buf), _Ifmt(_Fmt, "ld", _Iosbase.flags()), _Val)));
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill,
        unsigned long _Val) const { // put formatted unsigned long to _Dest
        char _Buf[2 * _MAX_INT_DIG];
        char _Fmt[6];

        return _Iput(_Dest, _Iosbase, _Fill, _Buf,
            static_cast<size_t>(_CSTD sprintf_s(_Buf, sizeof(_Buf), _Ifmt(_Fmt, "lu", _Iosbase.flags()), _Val)));
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long long _Val) const { // put formatted long long to _Dest
        char _Buf[2 * _MAX_INT_DIG];
        char _Fmt[8];

        return _Iput(_Dest, _Iosbase, _Fill, _Buf,
            static_cast<size_t>(_CSTD sprintf_s(_Buf, sizeof(_Buf), _Ifmt(_Fmt, "Ld", _Iosbase.flags()), _Val)));
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill,
        unsigned long long _Val) const { // put formatted unsigned long long to _Dest
        char _Buf[2 * _MAX_INT_DIG];
        char _Fmt[8];

        return _Iput(_Dest, _Iosbase, _Fill, _Buf,
            static_cast<size_t>(_CSTD sprintf_s(_Buf, sizeof(_Buf), _Ifmt(_Fmt, "Lu", _Iosbase.flags()), _Val)));
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, double _Val) const { // put formatted double to _Dest
        string _Buf;
        char _Fmt[8];
        const auto _Float_flags     = _Iosbase.flags() & ios_base::floatfield;
        const bool _Is_fixed        = _Float_flags == ios_base::fixed;
        const bool _Is_hex          = _Float_flags == (ios_base::fixed | ios_base::scientific);
        const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting
        const int _Desired_precision =
            _Float_put_desired_precision<double>(_Precision, _Float_flags); // desired precision
        size_t _Bufsize = static_cast<size_t>(_Desired_precision);
        if (_Is_fixed && 1e10 < _CSTD fabs(_Val)) { // f or F format
            int _Ptwo;
            (void) _CSTD frexp(_Val, &_Ptwo);
            _Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L;
        }

        _Buf.resize(_Bufsize + 50); // add fudge factor
        const auto _Ngen = static_cast<size_t>(_CSTD sprintf_s(
            &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 0, _Iosbase.flags()), static_cast<int>(_Precision), _Val));

        return _Fput(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen);
    }

    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, long double _Val) const { // put formatted long double to _Dest
        string _Buf;
        char _Fmt[8];
        const auto _Float_flags     = _Iosbase.flags() & ios_base::floatfield;
        const bool _Is_fixed        = _Float_flags == ios_base::fixed;
        const bool _Is_hex          = _Float_flags == (ios_base::fixed | ios_base::scientific);
        const streamsize _Precision = _Is_hex ? -1 : _Iosbase.precision(); // precision setting
        const int _Desired_precision =
            _Float_put_desired_precision<long double>(_Precision, _Float_flags); // desired precision
        size_t _Bufsize = static_cast<size_t>(_Desired_precision);
        if (_Is_fixed && 1e10 < _CSTD fabsl(_Val)) { // f or F format
            int _Ptwo;
            (void) _CSTD frexpl(_Val, &_Ptwo);
            _Bufsize += _CSTD abs(_Ptwo) * 30103L / 100000L;
        }

        _Buf.resize(_Bufsize + 50); // add fudge factor
        const auto _Ngen = static_cast<size_t>(_CSTD sprintf_s(
            &_Buf[0], _Buf.size(), _Ffmt(_Fmt, 'L', _Iosbase.flags()), static_cast<int>(_Precision), _Val));

        return _Fput(_Dest, _Iosbase, _Fill, _Buf.c_str(), _Ngen);
    }
#pragma warning(pop)

    virtual _OutIt __CLR_OR_THIS_CALL do_put(
        _OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const void* _Val) const { // put formatted void pointer to _Dest
        char _Buf[2 * _MAX_INT_DIG];

        return _Iput(
            _Dest, _Iosbase, _Fill, _Buf, static_cast<size_t>(_CSTD sprintf_s(_Buf, sizeof(_Buf), "%p", _Val)));
    }

private:
    char* __CLRCALL_OR_CDECL _Ffmt(
        char* _Fmt, char _Spec, ios_base::fmtflags _Flags) const { // generate sprintf format for floating-point
        char* _Ptr = _Fmt;
        *_Ptr++    = '%';

        if (_Flags & ios_base::showpos) {
            *_Ptr++ = '+';
        }

        if (_Flags & ios_base::showpoint) {
            *_Ptr++ = '#';
        }

        *_Ptr++ = '.';
        *_Ptr++ = '*'; // for precision argument
        if (_Spec != '\0') {
            *_Ptr++ = _Spec; // 'L' qualifier for long double only
        }

        char _Ch; // specifier
        ios_base::fmtflags _Ffl = _Flags & ios_base::floatfield;
        if (_Flags & ios_base::uppercase) {
            if (_Ffl == ios_base::fixed) {
                _Ch = 'f';
            } else if (_Ffl == ios_base::hexfloat) {
                _Ch = 'A'; // added with TR1
            } else if (_Ffl == ios_base::scientific) {
                _Ch = 'E';
            } else {
                _Ch = 'G';
            }
        } else {
            if (_Ffl == ios_base::fixed) {
                _Ch = 'f';
            } else if (_Ffl == ios_base::hexfloat) {
                _Ch = 'a'; // added with TR1
            } else if (_Ffl == ios_base::scientific) {
                _Ch = 'e';
            } else {
                _Ch = 'g';
            }
        }
        *_Ptr++ = _Ch;

        *_Ptr = '\0';
        return _Fmt;
    }

    _OutIt __CLRCALL_OR_CDECL _Fput(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, const char* _Buf,
        size_t _Count) const { // put formatted floating-point to _Dest
        auto _Prefix = static_cast<size_t>(0 < _Count && (*_Buf == '+' || *_Buf == '-'));
        const char* _Exps;
        if ((_Iosbase.flags() & ios_base::floatfield) != ios_base::hexfloat) {
            _Exps = "eE";
        } else { // correct for hexadecimal floating-point
            _Exps = "pP";
            if (_Prefix + 2 <= _Count && _Buf[_Prefix] == '0'
                && (_Buf[_Prefix + 1] == 'x' || _Buf[_Prefix + 1] == 'X')) {
                _Prefix += 2;
            }
        }
        const size_t _Eoff = _CSTD strcspn(&_Buf[0], _Exps); // find exponent
        char _Dp[2]        = {"."};
        _Dp[0]             = _CSTD localeconv()->decimal_point[0];
        const size_t _Poff = _CSTD strcspn(&_Buf[0], &_Dp[0]); // find decimal point

        const ctype<_Elem>& _Ctype_fac = _STD use_facet<ctype<_Elem>>(_Iosbase.getloc());
        basic_string<_Elem> _Groupstring(_Count, _Elem(0)); // reserve space
        _Ctype_fac.widen(_Buf, _Buf + _Count, &_Groupstring[0]);

        const auto& _Punct_fac  = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
        const string _Grouping  = _Punct_fac.grouping();
        const _Elem _Kseparator = _Punct_fac.thousands_sep();

        if (_Poff != _Count) {
            _Groupstring[_Poff] = _Punct_fac.decimal_point();
        }

        size_t _Off     = _Poff == _Count ? _Eoff : _Poff;
        const char* _Pg = &_Grouping[0];
        while (*_Pg != CHAR_MAX && '\0' < *_Pg && static_cast<size_t>(*_Pg) < _Off - _Prefix) {
            // add thousands separator
            _Groupstring.insert(_Off -= *_Pg, 1, _Kseparator);
            if ('\0' < _Pg[1]) {
                ++_Pg; // not last group, advance
            }
        }

        _Count = _Groupstring.size();

        size_t _Fillcount;
        if (_Iosbase.width() <= 0 || static_cast<size_t>(_Iosbase.width()) <= _Count) {
            _Fillcount = 0;
        } else {
            _Fillcount = static_cast<size_t>(_Iosbase.width()) - _Count;
        }

        ios_base::fmtflags _Adjustfield = _Iosbase.flags() & ios_base::adjustfield;
        if (_Adjustfield != ios_base::left && _Adjustfield != ios_base::internal) { // put leading fill
            _Dest      = _Rep(_Dest, _Fill, _Fillcount);
            _Fillcount = 0;
            _Dest      = _Put(_Dest, &_Groupstring[0], _Prefix);
        } else if (_Adjustfield == ios_base::internal) { // put internal fill
            _Dest      = _Put(_Dest, &_Groupstring[0], _Prefix);
            _Dest      = _Rep(_Dest, _Fill, _Fillcount);
            _Fillcount = 0;
        } else {
            _Dest = _Put(_Dest, &_Groupstring[0], _Prefix);
        }

        _Dest = _Put(_Dest, &_Groupstring[_Prefix], _Count - _Prefix);
        _Iosbase.width(0);
        return _Rep(_Dest, _Fill, _Fillcount); // put trailing fill
    }

    char* __CLRCALL_OR_CDECL _Ifmt(
        char* _Fmt, const char* _Spec, ios_base::fmtflags _Flags) const { // generate sprintf format for integer
        char* _Ptr = _Fmt;
        *_Ptr++    = '%';

        if (_Flags & ios_base::showpos) {
            *_Ptr++ = '+';
        }

        if (_Flags & ios_base::showbase) {
            *_Ptr++ = '#';
        }

        if (_Spec[0] != 'L') {
            *_Ptr++ = _Spec[0]; // qualifier
        } else { // change L to I64
            *_Ptr++ = 'I';
            *_Ptr++ = '6';
            *_Ptr++ = '4';
        }

        ios_base::fmtflags _Basefield = _Flags & ios_base::basefield;
        *_Ptr++                       = _Basefield == ios_base::oct  ? 'o'
                                      : _Basefield != ios_base::hex  ? _Spec[1] // 'd' or 'u'
                                      : _Flags & ios_base::uppercase ? 'X'
                                                                     : 'x';
        *_Ptr                         = '\0';
        return _Fmt;
    }

    _OutIt __CLRCALL_OR_CDECL _Iput(_OutIt _Dest, ios_base& _Iosbase, _Elem _Fill, char* _Buf,
        size_t _Count) const { // put formatted integer to _Dest
        auto _Prefix = static_cast<size_t>(0 < _Count && (*_Buf == '+' || *_Buf == '-'));
        if ((_Iosbase.flags() & ios_base::basefield) == ios_base::hex && _Prefix + 2 <= _Count && _Buf[_Prefix] == '0'
            && (_Buf[_Prefix + 1] == 'x' || _Buf[_Prefix + 1] == 'X')) {
            _Prefix += 2;
        }

        const ctype<_Elem>& _Ctype_fac = _STD use_facet<ctype<_Elem>>(_Iosbase.getloc());
        basic_string<_Elem> _Groupstring(_Count, _Elem(0)); // reserve space
        _Ctype_fac.widen(_Buf, _Buf + _Count, &_Groupstring[0]);

        const auto& _Punct_fac = _STD use_facet<numpunct<_Elem>>(_Iosbase.getloc());
        const string _Grouping = _Punct_fac.grouping();
        const char* _Pg        = &_Grouping[0];
        if (*_Pg != CHAR_MAX && '\0' < *_Pg) { // grouping specified, add thousands separators
            const _Elem _Kseparator = _Punct_fac.thousands_sep();
            while (*_Pg != CHAR_MAX && '\0' < *_Pg && static_cast<size_t>(*_Pg) < _Count - _Prefix) {
                // insert thousands separator
                _Count -= *_Pg;
                _Groupstring.insert(_Count, 1, _Kseparator);
                if ('\0' < _Pg[1]) {
                    ++_Pg; // not last group, advance
                }
            }
        }

        _Count = _Groupstring.size();

        size_t _Fillcount;
        if (_Iosbase.width() <= 0 || static_cast<size_t>(_Iosbase.width()) <= _Count) {
            _Fillcount = 0;
        } else {
            _Fillcount = static_cast<size_t>(_Iosbase.width()) - _Count;
        }

        ios_base::fmtflags _Adjustfield = _Iosbase.flags() & ios_base::adjustfield;
        if (_Adjustfield != ios_base::left && _Adjustfield != ios_base::internal) { // put leading fill
            _Dest      = _Rep(_Dest, _Fill, _Fillcount);
            _Fillcount = 0;
            _Dest      = _Put(_Dest, &_Groupstring[0], _Prefix);
        } else if (_Adjustfield == ios_base::internal) { // put internal fill
            _Dest      = _Put(_Dest, &_Groupstring[0], _Prefix);
            _Dest      = _Rep(_Dest, _Fill, _Fillcount);
            _Fillcount = 0;
        } else {
            _Dest = _Put(_Dest, &_Groupstring[0], _Prefix);
        }

        _Dest = _Put(_Dest, &_Groupstring[_Prefix], _Count - _Prefix);
        _Iosbase.width(0);
        return _Rep(_Dest, _Fill, _Fillcount); // put trailing fill
    }

    _OutIt __CLRCALL_OR_CDECL _Put(
        _OutIt _Dest, const _Elem* _Ptr, size_t _Count) const { // put [_Ptr, _Ptr + _Count) to _Dest
        for (; 0 < _Count; --_Count, (void) ++_Dest, ++_Ptr) {
            *_Dest = *_Ptr;
        }

        return _Dest;
    }

    _OutIt __CLRCALL_OR_CDECL _Rep(_OutIt _Dest, _Elem _Ch, size_t _Count) const { // put _Count * _Ch to _Dest
        for (; 0 < _Count; --_Count, (void) ++_Dest) {
            *_Dest = _Ch;
        }

        return _Dest;
    }
};

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdllimport-static-field-def"
#endif // __clang__

template <class _Elem, class _OutIt>
__PURE_APPDOMAIN_GLOBAL locale::id num_put<_Elem, _OutIt>::id;

#if defined(_DLL_CPPLIB)

#if !defined(_CRTBLD) || defined(__FORCE_INSTANCE)
template __PURE_APPDOMAIN_GLOBAL locale::id numpunct<char>::id;
template class _CRTIMP2_PURE_IMPORT num_get<char, istreambuf_iterator<char, char_traits<char>>>;
template class _CRTIMP2_PURE_IMPORT num_put<char, ostreambuf_iterator<char, char_traits<char>>>;

template __PURE_APPDOMAIN_GLOBAL locale::id numpunct<wchar_t>::id;
template class _CRTIMP2_PURE_IMPORT num_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t>>>;
template class _CRTIMP2_PURE_IMPORT num_put<wchar_t, ostreambuf_iterator<wchar_t, char_traits<wchar_t>>>;
#endif // !defined(_CRTBLD) || defined(__FORCE_INSTANCE)

#ifdef __FORCE_INSTANCE
template __PURE_APPDOMAIN_GLOBAL locale::id numpunct<unsigned short>::id;
template class _CRTIMP2_PURE_IMPORT
    num_get<unsigned short, istreambuf_iterator<unsigned short, char_traits<unsigned short>>>;
template class _CRTIMP2_PURE_IMPORT
    num_put<unsigned short, ostreambuf_iterator<unsigned short, char_traits<unsigned short>>>;
#endif // __FORCE_INSTANCE
#endif // defined(_DLL_CPPLIB)

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XLOCNUM_
