Changeset - r27416:8a08da0bf98e
[Not reviewed]
master
0 5 4
Rubidium - 16 months ago 2023-05-20 09:50:13
rubidium@openttd.org
Codechange: update to fmt 10.0.0 and add formatting support for chrono and std types
9 files changed with 10258 insertions and 6212 deletions:
0 comments (0 inline, 0 general)
src/3rdparty/fmt/CMakeLists.txt
Show inline comments
 
add_files(
 
    chrono.h
 
    core.h
 
    format.h
 
    format-inl.h
 
    ostream.h
 
    ranges.h
 
    std.h
 
)
src/3rdparty/fmt/LICENSE.rst
Show inline comments
 
Copyright (c) 2012 - present, Victor Zverovich
 
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
 

	
 
Permission is hereby granted, free of charge, to any person obtaining
 
a copy of this software and associated documentation files (the
src/3rdparty/fmt/chrono.h
Show inline comments
 
new file 100644
 
// Formatting library for C++ - chrono support
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
//
 
// For the license information refer to format.h.
 

	
 
#ifndef FMT_CHRONO_H_
 
#define FMT_CHRONO_H_
 

	
 
#include <algorithm>
 
#include <chrono>
 
#include <cmath>    // std::isfinite
 
#include <cstring>  // std::memcpy
 
#include <ctime>
 
#include <iterator>
 
#include <locale>
 
#include <ostream>
 
#include <type_traits>
 

	
 
#include "format.h"
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
// Check if std::chrono::local_t is available.
 
#ifndef FMT_USE_LOCAL_TIME
 
#  ifdef __cpp_lib_chrono
 
#    define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
 
#  else
 
#    define FMT_USE_LOCAL_TIME 0
 
#  endif
 
#endif
 

	
 
// Check if std::chrono::utc_timestamp is available.
 
#ifndef FMT_USE_UTC_TIME
 
#  ifdef __cpp_lib_chrono
 
#    define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
 
#  else
 
#    define FMT_USE_UTC_TIME 0
 
#  endif
 
#endif
 

	
 
// Enable tzset.
 
#ifndef FMT_USE_TZSET
 
// UWP doesn't provide _tzset.
 
#  if FMT_HAS_INCLUDE("winapifamily.h")
 
#    include <winapifamily.h>
 
#  endif
 
#  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
 
                          (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
 
#    define FMT_USE_TZSET 1
 
#  else
 
#    define FMT_USE_TZSET 0
 
#  endif
 
#endif
 

	
 
// Enable safe chrono durations, unless explicitly disabled.
 
#ifndef FMT_SAFE_DURATION_CAST
 
#  define FMT_SAFE_DURATION_CAST 1
 
#endif
 
#if FMT_SAFE_DURATION_CAST
 

	
 
// For conversion between std::chrono::durations without undefined
 
// behaviour or erroneous results.
 
// This is a stripped down version of duration_cast, for inclusion in fmt.
 
// See https://github.com/pauldreik/safe_duration_cast
 
//
 
// Copyright Paul Dreik 2019
 
namespace safe_duration_cast {
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 
                        std::numeric_limits<From>::is_signed ==
 
                            std::numeric_limits<To>::is_signed)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using F = std::numeric_limits<From>;
 
  using T = std::numeric_limits<To>;
 
  static_assert(F::is_integer, "From must be integral");
 
  static_assert(T::is_integer, "To must be integral");
 

	
 
  // A and B are both signed, or both unsigned.
 
  if (detail::const_check(F::digits <= T::digits)) {
 
    // From fits in To without any problem.
 
  } else {
 
    // From does not always fit in To, resort to a dynamic check.
 
    if (from < (T::min)() || from > (T::max)()) {
 
      // outside range.
 
      ec = 1;
 
      return {};
 
    }
 
  }
 
  return static_cast<To>(from);
 
}
 

	
 
/**
 
 * converts From to To, without loss. If the dynamic value of from
 
 * can't be converted to To without loss, ec is set.
 
 */
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 
                        std::numeric_limits<From>::is_signed !=
 
                            std::numeric_limits<To>::is_signed)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using F = std::numeric_limits<From>;
 
  using T = std::numeric_limits<To>;
 
  static_assert(F::is_integer, "From must be integral");
 
  static_assert(T::is_integer, "To must be integral");
 

	
 
  if (detail::const_check(F::is_signed && !T::is_signed)) {
 
    // From may be negative, not allowed!
 
    if (fmt::detail::is_negative(from)) {
 
      ec = 1;
 
      return {};
 
    }
 
    // From is positive. Can it always fit in To?
 
    if (detail::const_check(F::digits > T::digits) &&
 
        from > static_cast<From>(detail::max_value<To>())) {
 
      ec = 1;
 
      return {};
 
    }
 
  }
 

	
 
  if (detail::const_check(!F::is_signed && T::is_signed &&
 
                          F::digits >= T::digits) &&
 
      from > static_cast<From>(detail::max_value<To>())) {
 
    ec = 1;
 
    return {};
 
  }
 
  return static_cast<To>(from);  // Lossless conversion.
 
}
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  return from;
 
}  // function
 

	
 
// clang-format off
 
/**
 
 * converts From to To if possible, otherwise ec is set.
 
 *
 
 * input                            |    output
 
 * ---------------------------------|---------------
 
 * NaN                              | NaN
 
 * Inf                              | Inf
 
 * normal, fits in output           | converted (possibly lossy)
 
 * normal, does not fit in output   | ec is set
 
 * subnormal                        | best effort
 
 * -Inf                             | -Inf
 
 */
 
// clang-format on
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using T = std::numeric_limits<To>;
 
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 
  static_assert(std::is_floating_point<To>::value, "To must be floating");
 

	
 
  // catch the only happy case
 
  if (std::isfinite(from)) {
 
    if (from >= T::lowest() && from <= (T::max)()) {
 
      return static_cast<To>(from);
 
    }
 
    // not within range.
 
    ec = 1;
 
    return {};
 
  }
 

	
 
  // nan and inf will be preserved
 
  return static_cast<To>(from);
 
}  // function
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 
  ec = 0;
 
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 
  return from;
 
}
 

	
 
/**
 
 * safe duration cast between integral durations
 
 */
 
template <typename To, typename FromRep, typename FromPeriod,
 
          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
 
          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
 
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 
                      int& ec) {
 
  using From = std::chrono::duration<FromRep, FromPeriod>;
 
  ec = 0;
 
  // the basic idea is that we need to convert from count() in the from type
 
  // to count() in the To type, by multiplying it with this:
 
  struct Factor
 
      : std::ratio_divide<typename From::period, typename To::period> {};
 

	
 
  static_assert(Factor::num > 0, "num must be positive");
 
  static_assert(Factor::den > 0, "den must be positive");
 

	
 
  // the conversion is like this: multiply from.count() with Factor::num
 
  // /Factor::den and convert it to To::rep, all this without
 
  // overflow/underflow. let's start by finding a suitable type that can hold
 
  // both To, From and Factor::num
 
  using IntermediateRep =
 
      typename std::common_type<typename From::rep, typename To::rep,
 
                                decltype(Factor::num)>::type;
 

	
 
  // safe conversion to IntermediateRep
 
  IntermediateRep count =
 
      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
 
  if (ec) return {};
 
  // multiply with Factor::num without overflow or underflow
 
  if (detail::const_check(Factor::num != 1)) {
 
    const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
 
    if (count > max1) {
 
      ec = 1;
 
      return {};
 
    }
 
    const auto min1 =
 
        (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
 
    if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
 
        count < min1) {
 
      ec = 1;
 
      return {};
 
    }
 
    count *= Factor::num;
 
  }
 

	
 
  if (detail::const_check(Factor::den != 1)) count /= Factor::den;
 
  auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
 
  return ec ? To() : To(tocount);
 
}
 

	
 
/**
 
 * safe duration_cast between floating point durations
 
 */
 
template <typename To, typename FromRep, typename FromPeriod,
 
          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
 
          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
 
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 
                      int& ec) {
 
  using From = std::chrono::duration<FromRep, FromPeriod>;
 
  ec = 0;
 
  if (std::isnan(from.count())) {
 
    // nan in, gives nan out. easy.
 
    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
 
  }
 
  // maybe we should also check if from is denormal, and decide what to do about
 
  // it.
 

	
 
  // +-inf should be preserved.
 
  if (std::isinf(from.count())) {
 
    return To{from.count()};
 
  }
 

	
 
  // the basic idea is that we need to convert from count() in the from type
 
  // to count() in the To type, by multiplying it with this:
 
  struct Factor
 
      : std::ratio_divide<typename From::period, typename To::period> {};
 

	
 
  static_assert(Factor::num > 0, "num must be positive");
 
  static_assert(Factor::den > 0, "den must be positive");
 

	
 
  // the conversion is like this: multiply from.count() with Factor::num
 
  // /Factor::den and convert it to To::rep, all this without
 
  // overflow/underflow. let's start by finding a suitable type that can hold
 
  // both To, From and Factor::num
 
  using IntermediateRep =
 
      typename std::common_type<typename From::rep, typename To::rep,
 
                                decltype(Factor::num)>::type;
 

	
 
  // force conversion of From::rep -> IntermediateRep to be safe,
 
  // even if it will never happen be narrowing in this context.
 
  IntermediateRep count =
 
      safe_float_conversion<IntermediateRep>(from.count(), ec);
 
  if (ec) {
 
    return {};
 
  }
 

	
 
  // multiply with Factor::num without overflow or underflow
 
  if (detail::const_check(Factor::num != 1)) {
 
    constexpr auto max1 = detail::max_value<IntermediateRep>() /
 
                          static_cast<IntermediateRep>(Factor::num);
 
    if (count > max1) {
 
      ec = 1;
 
      return {};
 
    }
 
    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
 
                          static_cast<IntermediateRep>(Factor::num);
 
    if (count < min1) {
 
      ec = 1;
 
      return {};
 
    }
 
    count *= static_cast<IntermediateRep>(Factor::num);
 
  }
 

	
 
  // this can't go wrong, right? den>0 is checked earlier.
 
  if (detail::const_check(Factor::den != 1)) {
 
    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
 
    count /= static_cast<common_t>(Factor::den);
 
  }
 

	
 
  // convert to the to type, safely
 
  using ToRep = typename To::rep;
 

	
 
  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
 
  if (ec) {
 
    return {};
 
  }
 
  return To{tocount};
 
}
 
}  // namespace safe_duration_cast
 
#endif
 

	
 
// Prevents expansion of a preceding token as a function-style macro.
 
// Usage: f FMT_NOMACRO()
 
#define FMT_NOMACRO
 

	
 
namespace detail {
 
template <typename T = void> struct null {};
 
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
 
inline null<> localtime_s(...) { return null<>(); }
 
inline null<> gmtime_r(...) { return null<>(); }
 
inline null<> gmtime_s(...) { return null<>(); }
 

	
 
inline const std::locale& get_classic_locale() {
 
  static const auto& locale = std::locale::classic();
 
  return locale;
 
}
 

	
 
template <typename CodeUnit> struct codecvt_result {
 
  static constexpr const size_t max_size = 32;
 
  CodeUnit buf[max_size];
 
  CodeUnit* end;
 
};
 
template <typename CodeUnit>
 
constexpr const size_t codecvt_result<CodeUnit>::max_size;
 

	
 
template <typename CodeUnit>
 
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
 
                   const std::locale& loc) {
 
#if FMT_CLANG_VERSION
 
#  pragma clang diagnostic push
 
#  pragma clang diagnostic ignored "-Wdeprecated"
 
  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
 
#  pragma clang diagnostic pop
 
#else
 
  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
 
#endif
 
  auto mb = std::mbstate_t();
 
  const char* from_next = nullptr;
 
  auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
 
                     std::begin(out.buf), std::end(out.buf), out.end);
 
  if (result != std::codecvt_base::ok)
 
    FMT_THROW(format_error("failed to format time"));
 
}
 

	
 
template <typename OutputIt>
 
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
 
    -> OutputIt {
 
  if (detail::is_utf8() && loc != get_classic_locale()) {
 
    // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
 
    // gcc-4.
 
#if FMT_MSC_VERSION != 0 || \
 
    (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
 
    // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
 
    // and newer.
 
    using code_unit = wchar_t;
 
#else
 
    using code_unit = char32_t;
 
#endif
 

	
 
    using unit_t = codecvt_result<code_unit>;
 
    unit_t unit;
 
    write_codecvt(unit, in, loc);
 
    // In UTF-8 is used one to four one-byte code units.
 
    unicode_to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>
 
        u;
 
    if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
 
      FMT_THROW(format_error("failed to format time"));
 
    return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
 
  }
 
  return copy_str<char>(in.data(), in.data() + in.size(), out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
 
    -> OutputIt {
 
  codecvt_result<Char> unit;
 
  write_codecvt(unit, sv, loc);
 
  return copy_str<Char>(unit.buf, unit.end, out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
 
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
 
    -> OutputIt {
 
  return write_encoded_tm_str(out, sv, loc);
 
}
 

	
 
template <typename Char>
 
inline void do_write(buffer<Char>& buf, const std::tm& time,
 
                     const std::locale& loc, char format, char modifier) {
 
  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
 
  auto&& os = std::basic_ostream<Char>(&format_buf);
 
  os.imbue(loc);
 
  using iterator = std::ostreambuf_iterator<Char>;
 
  const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
 
  auto end = facet.put(os, os, Char(' '), &time, format, modifier);
 
  if (end.failed()) FMT_THROW(format_error("failed to format time"));
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
 
           char format, char modifier = 0) -> OutputIt {
 
  auto&& buf = get_buffer<Char>(out);
 
  do_write<Char>(buf, time, loc, format, modifier);
 
  return get_iterator(buf, out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
 
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
 
           char format, char modifier = 0) -> OutputIt {
 
  auto&& buf = basic_memory_buffer<Char>();
 
  do_write<char>(buf, time, loc, format, modifier);
 
  return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
 
}
 

	
 
}  // namespace detail
 

	
 
FMT_BEGIN_EXPORT
 

	
 
/**
 
  Converts given time since epoch as ``std::time_t`` value into calendar time,
 
  expressed in local time. Unlike ``std::localtime``, this function is
 
  thread-safe on most platforms.
 
 */
 
inline std::tm localtime(std::time_t time) {
 
  struct dispatcher {
 
    std::time_t time_;
 
    std::tm tm_;
 

	
 
    dispatcher(std::time_t t) : time_(t) {}
 

	
 
    bool run() {
 
      using namespace fmt::detail;
 
      return handle(localtime_r(&time_, &tm_));
 
    }
 

	
 
    bool handle(std::tm* tm) { return tm != nullptr; }
 

	
 
    bool handle(detail::null<>) {
 
      using namespace fmt::detail;
 
      return fallback(localtime_s(&tm_, &time_));
 
    }
 

	
 
    bool fallback(int res) { return res == 0; }
 

	
 
#if !FMT_MSC_VERSION
 
    bool fallback(detail::null<>) {
 
      using namespace fmt::detail;
 
      std::tm* tm = std::localtime(&time_);
 
      if (tm) tm_ = *tm;
 
      return tm != nullptr;
 
    }
 
#endif
 
  };
 
  dispatcher lt(time);
 
  // Too big time values may be unsupported.
 
  if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
 
  return lt.tm_;
 
}
 

	
 
#if FMT_USE_LOCAL_TIME
 
template <typename Duration>
 
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
 
  return localtime(std::chrono::system_clock::to_time_t(
 
      std::chrono::current_zone()->to_sys(time)));
 
}
 
#endif
 

	
 
/**
 
  Converts given time since epoch as ``std::time_t`` value into calendar time,
 
  expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
 
  function is thread-safe on most platforms.
 
 */
 
inline std::tm gmtime(std::time_t time) {
 
  struct dispatcher {
 
    std::time_t time_;
 
    std::tm tm_;
 

	
 
    dispatcher(std::time_t t) : time_(t) {}
 

	
 
    bool run() {
 
      using namespace fmt::detail;
 
      return handle(gmtime_r(&time_, &tm_));
 
    }
 

	
 
    bool handle(std::tm* tm) { return tm != nullptr; }
 

	
 
    bool handle(detail::null<>) {
 
      using namespace fmt::detail;
 
      return fallback(gmtime_s(&tm_, &time_));
 
    }
 

	
 
    bool fallback(int res) { return res == 0; }
 

	
 
#if !FMT_MSC_VERSION
 
    bool fallback(detail::null<>) {
 
      std::tm* tm = std::gmtime(&time_);
 
      if (tm) tm_ = *tm;
 
      return tm != nullptr;
 
    }
 
#endif
 
  };
 
  dispatcher gt(time);
 
  // Too big time values may be unsupported.
 
  if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
 
  return gt.tm_;
 
}
 

	
 
inline std::tm gmtime(
 
    std::chrono::time_point<std::chrono::system_clock> time_point) {
 
  return gmtime(std::chrono::system_clock::to_time_t(time_point));
 
}
 

	
 
FMT_BEGIN_DETAIL_NAMESPACE
 

	
 
// DEPRECATED!
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
 
                               format_specs<Char>& specs) -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  auto align = align::none;
 
  auto p = begin + code_point_length(begin);
 
  if (end - p <= 0) p = begin;
 
  for (;;) {
 
    switch (to_ascii(*p)) {
 
    case '<':
 
      align = align::left;
 
      break;
 
    case '>':
 
      align = align::right;
 
      break;
 
    case '^':
 
      align = align::center;
 
      break;
 
    }
 
    if (align != align::none) {
 
      if (p != begin) {
 
        auto c = *begin;
 
        if (c == '}') return begin;
 
        if (c == '{') {
 
          throw_format_error("invalid fill character '{'");
 
          return begin;
 
        }
 
        specs.fill = {begin, to_unsigned(p - begin)};
 
        begin = p + 1;
 
      } else {
 
        ++begin;
 
      }
 
      break;
 
    } else if (p == begin) {
 
      break;
 
    }
 
    p = begin;
 
  }
 
  specs.align = align;
 
  return begin;
 
}
 

	
 
// Writes two-digit numbers a, b and c separated by sep to buf.
 
// The method by Pavel Novikov based on
 
// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
 
inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
 
                                   unsigned c, char sep) {
 
  unsigned long long digits =
 
      a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
 
  // Convert each value to BCD.
 
  // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
 
  // The difference is
 
  //   y - x = a * 6
 
  // a can be found from x:
 
  //   a = floor(x / 10)
 
  // then
 
  //   y = x + a * 6 = x + floor(x / 10) * 6
 
  // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
 
  digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
 
  // Put low nibbles to high bytes and high nibbles to low bytes.
 
  digits = ((digits & 0x00f00000f00000f0) >> 4) |
 
           ((digits & 0x000f00000f00000f) << 8);
 
  auto usep = static_cast<unsigned long long>(sep);
 
  // Add ASCII '0' to each digit byte and insert separators.
 
  digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
 

	
 
  constexpr const size_t len = 8;
 
  if (const_check(is_big_endian())) {
 
    char tmp[len];
 
    std::memcpy(tmp, &digits, len);
 
    std::reverse_copy(tmp, tmp + len, buf);
 
  } else {
 
    std::memcpy(buf, &digits, len);
 
  }
 
}
 

	
 
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
 
  if (std::is_same<Period, std::atto>::value) return "as";
 
  if (std::is_same<Period, std::femto>::value) return "fs";
 
  if (std::is_same<Period, std::pico>::value) return "ps";
 
  if (std::is_same<Period, std::nano>::value) return "ns";
 
  if (std::is_same<Period, std::micro>::value) return "µs";
 
  if (std::is_same<Period, std::milli>::value) return "ms";
 
  if (std::is_same<Period, std::centi>::value) return "cs";
 
  if (std::is_same<Period, std::deci>::value) return "ds";
 
  if (std::is_same<Period, std::ratio<1>>::value) return "s";
 
  if (std::is_same<Period, std::deca>::value) return "das";
 
  if (std::is_same<Period, std::hecto>::value) return "hs";
 
  if (std::is_same<Period, std::kilo>::value) return "ks";
 
  if (std::is_same<Period, std::mega>::value) return "Ms";
 
  if (std::is_same<Period, std::giga>::value) return "Gs";
 
  if (std::is_same<Period, std::tera>::value) return "Ts";
 
  if (std::is_same<Period, std::peta>::value) return "Ps";
 
  if (std::is_same<Period, std::exa>::value) return "Es";
 
  if (std::is_same<Period, std::ratio<60>>::value) return "m";
 
  if (std::is_same<Period, std::ratio<3600>>::value) return "h";
 
  return nullptr;
 
}
 

	
 
enum class numeric_system {
 
  standard,
 
  // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
 
  alternative
 
};
 

	
 
// Glibc extensions for formatting numeric values.
 
enum class pad_type {
 
  unspecified,
 
  // Do not pad a numeric result string.
 
  none,
 
  // Pad a numeric result string with zeros even if the conversion specifier
 
  // character uses space-padding by default.
 
  zero,
 
  // Pad a numeric result string with spaces.
 
  space,
 
};
 

	
 
template <typename OutputIt>
 
auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
 
  if (pad == pad_type::none) return out;
 
  return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
 
}
 

	
 
template <typename OutputIt>
 
auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
 
  if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0';
 
  return out;
 
}
 

	
 
// Parses a put_time-like format string and invokes handler actions.
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
 
                                              const Char* end,
 
                                              Handler&& handler) {
 
  if (begin == end || *begin == '}') return begin;
 
  if (*begin != '%') FMT_THROW(format_error("invalid format"));
 
  auto ptr = begin;
 
  pad_type pad = pad_type::unspecified;
 
  while (ptr != end) {
 
    auto c = *ptr;
 
    if (c == '}') break;
 
    if (c != '%') {
 
      ++ptr;
 
      continue;
 
    }
 
    if (begin != ptr) handler.on_text(begin, ptr);
 
    ++ptr;  // consume '%'
 
    if (ptr == end) FMT_THROW(format_error("invalid format"));
 
    c = *ptr;
 
    switch (c) {
 
    case '_':
 
      pad = pad_type::space;
 
      ++ptr;
 
      break;
 
    case '-':
 
      pad = pad_type::none;
 
      ++ptr;
 
      break;
 
    case '0':
 
      pad = pad_type::zero;
 
      ++ptr;
 
      break;
 
    }
 
    if (ptr == end) FMT_THROW(format_error("invalid format"));
 
    c = *ptr++;
 
    switch (c) {
 
    case '%':
 
      handler.on_text(ptr - 1, ptr);
 
      break;
 
    case 'n': {
 
      const Char newline[] = {'\n'};
 
      handler.on_text(newline, newline + 1);
 
      break;
 
    }
 
    case 't': {
 
      const Char tab[] = {'\t'};
 
      handler.on_text(tab, tab + 1);
 
      break;
 
    }
 
    // Year:
 
    case 'Y':
 
      handler.on_year(numeric_system::standard);
 
      break;
 
    case 'y':
 
      handler.on_short_year(numeric_system::standard);
 
      break;
 
    case 'C':
 
      handler.on_century(numeric_system::standard);
 
      break;
 
    case 'G':
 
      handler.on_iso_week_based_year();
 
      break;
 
    case 'g':
 
      handler.on_iso_week_based_short_year();
 
      break;
 
    // Day of the week:
 
    case 'a':
 
      handler.on_abbr_weekday();
 
      break;
 
    case 'A':
 
      handler.on_full_weekday();
 
      break;
 
    case 'w':
 
      handler.on_dec0_weekday(numeric_system::standard);
 
      break;
 
    case 'u':
 
      handler.on_dec1_weekday(numeric_system::standard);
 
      break;
 
    // Month:
 
    case 'b':
 
    case 'h':
 
      handler.on_abbr_month();
 
      break;
 
    case 'B':
 
      handler.on_full_month();
 
      break;
 
    case 'm':
 
      handler.on_dec_month(numeric_system::standard);
 
      break;
 
    // Day of the year/month:
 
    case 'U':
 
      handler.on_dec0_week_of_year(numeric_system::standard);
 
      break;
 
    case 'W':
 
      handler.on_dec1_week_of_year(numeric_system::standard);
 
      break;
 
    case 'V':
 
      handler.on_iso_week_of_year(numeric_system::standard);
 
      break;
 
    case 'j':
 
      handler.on_day_of_year();
 
      break;
 
    case 'd':
 
      handler.on_day_of_month(numeric_system::standard);
 
      break;
 
    case 'e':
 
      handler.on_day_of_month_space(numeric_system::standard);
 
      break;
 
    // Hour, minute, second:
 
    case 'H':
 
      handler.on_24_hour(numeric_system::standard, pad);
 
      break;
 
    case 'I':
 
      handler.on_12_hour(numeric_system::standard, pad);
 
      break;
 
    case 'M':
 
      handler.on_minute(numeric_system::standard, pad);
 
      break;
 
    case 'S':
 
      handler.on_second(numeric_system::standard, pad);
 
      break;
 
    // Other:
 
    case 'c':
 
      handler.on_datetime(numeric_system::standard);
 
      break;
 
    case 'x':
 
      handler.on_loc_date(numeric_system::standard);
 
      break;
 
    case 'X':
 
      handler.on_loc_time(numeric_system::standard);
 
      break;
 
    case 'D':
 
      handler.on_us_date();
 
      break;
 
    case 'F':
 
      handler.on_iso_date();
 
      break;
 
    case 'r':
 
      handler.on_12_hour_time();
 
      break;
 
    case 'R':
 
      handler.on_24_hour_time();
 
      break;
 
    case 'T':
 
      handler.on_iso_time();
 
      break;
 
    case 'p':
 
      handler.on_am_pm();
 
      break;
 
    case 'Q':
 
      handler.on_duration_value();
 
      break;
 
    case 'q':
 
      handler.on_duration_unit();
 
      break;
 
    case 'z':
 
      handler.on_utc_offset(numeric_system::standard);
 
      break;
 
    case 'Z':
 
      handler.on_tz_name();
 
      break;
 
    // Alternative representation:
 
    case 'E': {
 
      if (ptr == end) FMT_THROW(format_error("invalid format"));
 
      c = *ptr++;
 
      switch (c) {
 
      case 'Y':
 
        handler.on_year(numeric_system::alternative);
 
        break;
 
      case 'y':
 
        handler.on_offset_year();
 
        break;
 
      case 'C':
 
        handler.on_century(numeric_system::alternative);
 
        break;
 
      case 'c':
 
        handler.on_datetime(numeric_system::alternative);
 
        break;
 
      case 'x':
 
        handler.on_loc_date(numeric_system::alternative);
 
        break;
 
      case 'X':
 
        handler.on_loc_time(numeric_system::alternative);
 
        break;
 
      case 'z':
 
        handler.on_utc_offset(numeric_system::alternative);
 
        break;
 
      default:
 
        FMT_THROW(format_error("invalid format"));
 
      }
 
      break;
 
    }
 
    case 'O':
 
      if (ptr == end) FMT_THROW(format_error("invalid format"));
 
      c = *ptr++;
 
      switch (c) {
 
      case 'y':
 
        handler.on_short_year(numeric_system::alternative);
 
        break;
 
      case 'm':
 
        handler.on_dec_month(numeric_system::alternative);
 
        break;
 
      case 'U':
 
        handler.on_dec0_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'W':
 
        handler.on_dec1_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'V':
 
        handler.on_iso_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'd':
 
        handler.on_day_of_month(numeric_system::alternative);
 
        break;
 
      case 'e':
 
        handler.on_day_of_month_space(numeric_system::alternative);
 
        break;
 
      case 'w':
 
        handler.on_dec0_weekday(numeric_system::alternative);
 
        break;
 
      case 'u':
 
        handler.on_dec1_weekday(numeric_system::alternative);
 
        break;
 
      case 'H':
 
        handler.on_24_hour(numeric_system::alternative, pad);
 
        break;
 
      case 'I':
 
        handler.on_12_hour(numeric_system::alternative, pad);
 
        break;
 
      case 'M':
 
        handler.on_minute(numeric_system::alternative, pad);
 
        break;
 
      case 'S':
 
        handler.on_second(numeric_system::alternative, pad);
 
        break;
 
      case 'z':
 
        handler.on_utc_offset(numeric_system::alternative);
 
        break;
 
      default:
 
        FMT_THROW(format_error("invalid format"));
 
      }
 
      break;
 
    default:
 
      FMT_THROW(format_error("invalid format"));
 
    }
 
    begin = ptr;
 
  }
 
  if (begin != ptr) handler.on_text(begin, ptr);
 
  return ptr;
 
}
 

	
 
template <typename Derived> struct null_chrono_spec_handler {
 
  FMT_CONSTEXPR void unsupported() {
 
    static_cast<Derived*>(this)->unsupported();
 
  }
 
  FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_offset_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
 
  FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
 
  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
 
  FMT_CONSTEXPR void on_full_month() { unsupported(); }
 
  FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_us_date() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_date() { unsupported(); }
 
  FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_am_pm() { unsupported(); }
 
  FMT_CONSTEXPR void on_duration_value() { unsupported(); }
 
  FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
 
  FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_tz_name() { unsupported(); }
 
};
 

	
 
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
 
  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
 

	
 
  template <typename Char>
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
  FMT_CONSTEXPR void on_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_short_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_offset_year() {}
 
  FMT_CONSTEXPR void on_century(numeric_system) {}
 
  FMT_CONSTEXPR void on_iso_week_based_year() {}
 
  FMT_CONSTEXPR void on_iso_week_based_short_year() {}
 
  FMT_CONSTEXPR void on_abbr_weekday() {}
 
  FMT_CONSTEXPR void on_full_weekday() {}
 
  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
 
  FMT_CONSTEXPR void on_abbr_month() {}
 
  FMT_CONSTEXPR void on_full_month() {}
 
  FMT_CONSTEXPR void on_dec_month(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_day_of_year() {}
 
  FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
 
  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
 
  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_datetime(numeric_system) {}
 
  FMT_CONSTEXPR void on_loc_date(numeric_system) {}
 
  FMT_CONSTEXPR void on_loc_time(numeric_system) {}
 
  FMT_CONSTEXPR void on_us_date() {}
 
  FMT_CONSTEXPR void on_iso_date() {}
 
  FMT_CONSTEXPR void on_12_hour_time() {}
 
  FMT_CONSTEXPR void on_24_hour_time() {}
 
  FMT_CONSTEXPR void on_iso_time() {}
 
  FMT_CONSTEXPR void on_am_pm() {}
 
  FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
 
  FMT_CONSTEXPR void on_tz_name() {}
 
};
 

	
 
inline const char* tm_wday_full_name(int wday) {
 
  static constexpr const char* full_name_list[] = {
 
      "Sunday",   "Monday", "Tuesday", "Wednesday",
 
      "Thursday", "Friday", "Saturday"};
 
  return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
 
}
 
inline const char* tm_wday_short_name(int wday) {
 
  static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
 
                                                    "Thu", "Fri", "Sat"};
 
  return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
 
}
 

	
 
inline const char* tm_mon_full_name(int mon) {
 
  static constexpr const char* full_name_list[] = {
 
      "January", "February", "March",     "April",   "May",      "June",
 
      "July",    "August",   "September", "October", "November", "December"};
 
  return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
 
}
 
inline const char* tm_mon_short_name(int mon) {
 
  static constexpr const char* short_name_list[] = {
 
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 
  };
 
  return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
 
}
 

	
 
template <typename T, typename = void>
 
struct has_member_data_tm_gmtoff : std::false_type {};
 
template <typename T>
 
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
 
    : std::true_type {};
 

	
 
template <typename T, typename = void>
 
struct has_member_data_tm_zone : std::false_type {};
 
template <typename T>
 
struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
 
    : std::true_type {};
 

	
 
#if FMT_USE_TZSET
 
inline void tzset_once() {
 
  static bool init = []() -> bool {
 
    _tzset();
 
    return true;
 
  }();
 
  ignore_unused(init);
 
}
 
#endif
 

	
 
// Converts value to Int and checks that it's in the range [0, upper).
 
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
inline Int to_nonnegative_int(T value, Int upper) {
 
  FMT_ASSERT(std::is_unsigned<Int>::value ||
 
                 (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
 
             "invalid value");
 
  (void)upper;
 
  return static_cast<Int>(value);
 
}
 
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
 
inline Int to_nonnegative_int(T value, Int upper) {
 
  if (value < 0 || value > static_cast<T>(upper))
 
    FMT_THROW(format_error("invalid value"));
 
  return static_cast<Int>(value);
 
}
 

	
 
constexpr long long pow10(std::uint32_t n) {
 
  return n == 0 ? 1 : 10 * pow10(n - 1);
 
}
 

	
 
// Counts the number of fractional digits in the range [0, 18] according to the
 
// C++20 spec. If more than 18 fractional digits are required then returns 6 for
 
// microseconds precision.
 
template <long long Num, long long Den, int N = 0,
 
          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
 
struct count_fractional_digits {
 
  static constexpr int value =
 
      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
 
};
 

	
 
// Base case that doesn't instantiate any more templates
 
// in order to avoid overflow.
 
template <long long Num, long long Den, int N>
 
struct count_fractional_digits<Num, Den, N, false> {
 
  static constexpr int value = (Num % Den == 0) ? N : 6;
 
};
 

	
 
// Format subseconds which are given as an integer type with an appropriate
 
// number of digits.
 
template <typename Char, typename OutputIt, typename Duration>
 
void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
 
  constexpr auto num_fractional_digits =
 
      count_fractional_digits<Duration::period::num,
 
                              Duration::period::den>::value;
 

	
 
  using subsecond_precision = std::chrono::duration<
 
      typename std::common_type<typename Duration::rep,
 
                                std::chrono::seconds::rep>::type,
 
      std::ratio<1, detail::pow10(num_fractional_digits)>>;
 

	
 
  const auto fractional =
 
      d - std::chrono::duration_cast<std::chrono::seconds>(d);
 
  const auto subseconds =
 
      std::chrono::treat_as_floating_point<
 
          typename subsecond_precision::rep>::value
 
          ? fractional.count()
 
          : std::chrono::duration_cast<subsecond_precision>(fractional).count();
 
  auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
 
  const int num_digits = detail::count_digits(n);
 

	
 
  int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
 
  if (precision < 0) {
 
    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
 
    if (std::ratio_less<typename subsecond_precision::period,
 
                        std::chrono::seconds::period>::value) {
 
      *out++ = '.';
 
      out = std::fill_n(out, leading_zeroes, '0');
 
      out = format_decimal<Char>(out, n, num_digits).end;
 
    }
 
  } else {
 
    *out++ = '.';
 
    leading_zeroes = (std::min)(leading_zeroes, precision);
 
    out = std::fill_n(out, leading_zeroes, '0');
 
    int remaining = precision - leading_zeroes;
 
    if (remaining != 0 && remaining < num_digits) {
 
      n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
 
      out = format_decimal<Char>(out, n, remaining).end;
 
      return;
 
    }
 
    out = format_decimal<Char>(out, n, num_digits).end;
 
    remaining -= num_digits;
 
    out = std::fill_n(out, remaining, '0');
 
  }
 
}
 

	
 
// Format subseconds which are given as a floating point type with an
 
// appropriate number of digits. We cannot pass the Duration here, as we
 
// explicitly need to pass the Rep value in the chrono_formatter.
 
template <typename Duration>
 
void write_floating_seconds(memory_buffer& buf, Duration duration,
 
                            int num_fractional_digits = -1) {
 
  using rep = typename Duration::rep;
 
  FMT_ASSERT(std::is_floating_point<rep>::value, "");
 

	
 
  auto val = duration.count();
 

	
 
  if (num_fractional_digits < 0) {
 
    // For `std::round` with fallback to `round`:
 
    // On some toolchains `std::round` is not available (e.g. GCC 6).
 
    using namespace std;
 
    num_fractional_digits =
 
        count_fractional_digits<Duration::period::num,
 
                                Duration::period::den>::value;
 
    if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
 
      num_fractional_digits = 6;
 
  }
 

	
 
  format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
 
            std::fmod(val * static_cast<rep>(Duration::period::num) /
 
                          static_cast<rep>(Duration::period::den),
 
                      static_cast<rep>(60)),
 
            num_fractional_digits);
 
}
 

	
 
template <typename OutputIt, typename Char,
 
          typename Duration = std::chrono::seconds>
 
class tm_writer {
 
 private:
 
  static constexpr int days_per_week = 7;
 

	
 
  const std::locale& loc_;
 
  const bool is_classic_;
 
  OutputIt out_;
 
  const Duration* subsecs_;
 
  const std::tm& tm_;
 

	
 
  auto tm_sec() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
 
    return tm_.tm_sec;
 
  }
 
  auto tm_min() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
 
    return tm_.tm_min;
 
  }
 
  auto tm_hour() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
 
    return tm_.tm_hour;
 
  }
 
  auto tm_mday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
 
    return tm_.tm_mday;
 
  }
 
  auto tm_mon() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
 
    return tm_.tm_mon;
 
  }
 
  auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
 
  auto tm_wday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
 
    return tm_.tm_wday;
 
  }
 
  auto tm_yday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
 
    return tm_.tm_yday;
 
  }
 

	
 
  auto tm_hour12() const noexcept -> int {
 
    const auto h = tm_hour();
 
    const auto z = h < 12 ? h : h - 12;
 
    return z == 0 ? 12 : z;
 
  }
 

	
 
  // POSIX and the C Standard are unclear or inconsistent about what %C and %y
 
  // do if the year is negative or exceeds 9999. Use the convention that %C
 
  // concatenated with %y yields the same output as %Y, and that %Y contains at
 
  // least 4 characters, with more only if necessary.
 
  auto split_year_lower(long long year) const noexcept -> int {
 
    auto l = year % 100;
 
    if (l < 0) l = -l;  // l in [0, 99]
 
    return static_cast<int>(l);
 
  }
 

	
 
  // Algorithm:
 
  // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
 
  auto iso_year_weeks(long long curr_year) const noexcept -> int {
 
    const auto prev_year = curr_year - 1;
 
    const auto curr_p =
 
        (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
 
        days_per_week;
 
    const auto prev_p =
 
        (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
 
        days_per_week;
 
    return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
 
  }
 
  auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
 
    return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
 
           days_per_week;
 
  }
 
  auto tm_iso_week_year() const noexcept -> long long {
 
    const auto year = tm_year();
 
    const auto w = iso_week_num(tm_yday(), tm_wday());
 
    if (w < 1) return year - 1;
 
    if (w > iso_year_weeks(year)) return year + 1;
 
    return year;
 
  }
 
  auto tm_iso_week_of_year() const noexcept -> int {
 
    const auto year = tm_year();
 
    const auto w = iso_week_num(tm_yday(), tm_wday());
 
    if (w < 1) return iso_year_weeks(year - 1);
 
    if (w > iso_year_weeks(year)) return 1;
 
    return w;
 
  }
 

	
 
  void write1(int value) {
 
    *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
 
  }
 
  void write2(int value) {
 
    const char* d = digits2(to_unsigned(value) % 100);
 
    *out_++ = *d++;
 
    *out_++ = *d;
 
  }
 
  void write2(int value, pad_type pad) {
 
    unsigned int v = to_unsigned(value) % 100;
 
    if (v >= 10) {
 
      const char* d = digits2(v);
 
      *out_++ = *d++;
 
      *out_++ = *d;
 
    } else {
 
      out_ = detail::write_padding(out_, pad);
 
      *out_++ = static_cast<char>('0' + v);
 
    }
 
  }
 

	
 
  void write_year_extended(long long year) {
 
    // At least 4 characters.
 
    int width = 4;
 
    if (year < 0) {
 
      *out_++ = '-';
 
      year = 0 - year;
 
      --width;
 
    }
 
    uint32_or_64_or_128_t<long long> n = to_unsigned(year);
 
    const int num_digits = count_digits(n);
 
    if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
 
    out_ = format_decimal<Char>(out_, n, num_digits).end;
 
  }
 
  void write_year(long long year) {
 
    if (year >= 0 && year < 10000) {
 
      write2(static_cast<int>(year / 100));
 
      write2(static_cast<int>(year % 100));
 
    } else {
 
      write_year_extended(year);
 
    }
 
  }
 

	
 
  void write_utc_offset(long offset, numeric_system ns) {
 
    if (offset < 0) {
 
      *out_++ = '-';
 
      offset = -offset;
 
    } else {
 
      *out_++ = '+';
 
    }
 
    offset /= 60;
 
    write2(static_cast<int>(offset / 60));
 
    if (ns != numeric_system::standard) *out_++ = ':';
 
    write2(static_cast<int>(offset % 60));
 
  }
 
  template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
 
  void format_utc_offset_impl(const T& tm, numeric_system ns) {
 
    write_utc_offset(tm.tm_gmtoff, ns);
 
  }
 
  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
 
  void format_utc_offset_impl(const T& tm, numeric_system ns) {
 
#if defined(_WIN32) && defined(_UCRT)
 
#  if FMT_USE_TZSET
 
    tzset_once();
 
#  endif
 
    long offset = 0;
 
    _get_timezone(&offset);
 
    if (tm.tm_isdst) {
 
      long dstbias = 0;
 
      _get_dstbias(&dstbias);
 
      offset += dstbias;
 
    }
 
    write_utc_offset(-offset, ns);
 
#else
 
    if (ns == numeric_system::standard) return format_localized('z');
 

	
 
    // Extract timezone offset from timezone conversion functions.
 
    std::tm gtm = tm;
 
    std::time_t gt = std::mktime(&gtm);
 
    std::tm ltm = gmtime(gt);
 
    std::time_t lt = std::mktime(&ltm);
 
    long offset = gt - lt;
 
    write_utc_offset(offset, ns);
 
#endif
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
 
  void format_tz_name_impl(const T& tm) {
 
    if (is_classic_)
 
      out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
 
    else
 
      format_localized('Z');
 
  }
 
  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
 
  void format_tz_name_impl(const T&) {
 
    format_localized('Z');
 
  }
 

	
 
  void format_localized(char format, char modifier = 0) {
 
    out_ = write<Char>(out_, tm_, loc_, format, modifier);
 
  }
 

	
 
 public:
 
  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
 
            const Duration* subsecs = nullptr)
 
      : loc_(loc),
 
        is_classic_(loc_ == get_classic_locale()),
 
        out_(out),
 
        subsecs_(subsecs),
 
        tm_(tm) {}
 

	
 
  OutputIt out() const { return out_; }
 

	
 
  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
 
    out_ = copy_str<Char>(begin, end, out_);
 
  }
 

	
 
  void on_abbr_weekday() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_wday_short_name(tm_wday()));
 
    else
 
      format_localized('a');
 
  }
 
  void on_full_weekday() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_wday_full_name(tm_wday()));
 
    else
 
      format_localized('A');
 
  }
 
  void on_dec0_weekday(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
 
    format_localized('w', 'O');
 
  }
 
  void on_dec1_weekday(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto wday = tm_wday();
 
      write1(wday == 0 ? days_per_week : wday);
 
    } else {
 
      format_localized('u', 'O');
 
    }
 
  }
 

	
 
  void on_abbr_month() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_mon_short_name(tm_mon()));
 
    else
 
      format_localized('b');
 
  }
 
  void on_full_month() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_mon_full_name(tm_mon()));
 
    else
 
      format_localized('B');
 
  }
 

	
 
  void on_datetime(numeric_system ns) {
 
    if (is_classic_) {
 
      on_abbr_weekday();
 
      *out_++ = ' ';
 
      on_abbr_month();
 
      *out_++ = ' ';
 
      on_day_of_month_space(numeric_system::standard);
 
      *out_++ = ' ';
 
      on_iso_time();
 
      *out_++ = ' ';
 
      on_year(numeric_system::standard);
 
    } else {
 
      format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
 
    }
 
  }
 
  void on_loc_date(numeric_system ns) {
 
    if (is_classic_)
 
      on_us_date();
 
    else
 
      format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
 
  }
 
  void on_loc_time(numeric_system ns) {
 
    if (is_classic_)
 
      on_iso_time();
 
    else
 
      format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
 
  }
 
  void on_us_date() {
 
    char buf[8];
 
    write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
 
                           to_unsigned(tm_mday()),
 
                           to_unsigned(split_year_lower(tm_year())), '/');
 
    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
 
  }
 
  void on_iso_date() {
 
    auto year = tm_year();
 
    char buf[10];
 
    size_t offset = 0;
 
    if (year >= 0 && year < 10000) {
 
      copy2(buf, digits2(static_cast<size_t>(year / 100)));
 
    } else {
 
      offset = 4;
 
      write_year_extended(year);
 
      year = 0;
 
    }
 
    write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
 
                           to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
 
                           '-');
 
    out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
 
  }
 

	
 
  void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
 
  void on_tz_name() { format_tz_name_impl(tm_); }
 

	
 
  void on_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write_year(tm_year());
 
    format_localized('Y', 'E');
 
  }
 
  void on_short_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(split_year_lower(tm_year()));
 
    format_localized('y', 'O');
 
  }
 
  void on_offset_year() {
 
    if (is_classic_) return write2(split_year_lower(tm_year()));
 
    format_localized('y', 'E');
 
  }
 

	
 
  void on_century(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto year = tm_year();
 
      auto upper = year / 100;
 
      if (year >= -99 && year < 0) {
 
        // Zero upper on negative year.
 
        *out_++ = '-';
 
        *out_++ = '0';
 
      } else if (upper >= 0 && upper < 100) {
 
        write2(static_cast<int>(upper));
 
      } else {
 
        out_ = write<Char>(out_, upper);
 
      }
 
    } else {
 
      format_localized('C', 'E');
 
    }
 
  }
 

	
 
  void on_dec_month(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_mon() + 1);
 
    format_localized('m', 'O');
 
  }
 

	
 
  void on_dec0_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
 
    format_localized('U', 'O');
 
  }
 
  void on_dec1_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto wday = tm_wday();
 
      write2((tm_yday() + days_per_week -
 
              (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
 
             days_per_week);
 
    } else {
 
      format_localized('W', 'O');
 
    }
 
  }
 
  void on_iso_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_iso_week_of_year());
 
    format_localized('V', 'O');
 
  }
 

	
 
  void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
 
  void on_iso_week_based_short_year() {
 
    write2(split_year_lower(tm_iso_week_year()));
 
  }
 

	
 
  void on_day_of_year() {
 
    auto yday = tm_yday() + 1;
 
    write1(yday / 100);
 
    write2(yday % 100);
 
  }
 
  void on_day_of_month(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
 
    format_localized('d', 'O');
 
  }
 
  void on_day_of_month_space(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto mday = to_unsigned(tm_mday()) % 100;
 
      const char* d2 = digits2(mday);
 
      *out_++ = mday < 10 ? ' ' : d2[0];
 
      *out_++ = d2[1];
 
    } else {
 
      format_localized('e', 'O');
 
    }
 
  }
 

	
 
  void on_24_hour(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_hour(), pad);
 
    format_localized('H', 'O');
 
  }
 
  void on_12_hour(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_hour12(), pad);
 
    format_localized('I', 'O');
 
  }
 
  void on_minute(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_min(), pad);
 
    format_localized('M', 'O');
 
  }
 

	
 
  void on_second(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      write2(tm_sec(), pad);
 
      if (subsecs_) {
 
        if (std::is_floating_point<typename Duration::rep>::value) {
 
          auto buf = memory_buffer();
 
          write_floating_seconds(buf, *subsecs_);
 
          if (buf.size() > 1) {
 
            // Remove the leading "0", write something like ".123".
 
            out_ = std::copy(buf.begin() + 1, buf.end(), out_);
 
          }
 
        } else {
 
          write_fractional_seconds<Char>(out_, *subsecs_);
 
        }
 
      }
 
    } else {
 
      // Currently no formatting of subseconds when a locale is set.
 
      format_localized('S', 'O');
 
    }
 
  }
 

	
 
  void on_12_hour_time() {
 
    if (is_classic_) {
 
      char buf[8];
 
      write_digit2_separated(buf, to_unsigned(tm_hour12()),
 
                             to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
 
      out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
 
      *out_++ = ' ';
 
      on_am_pm();
 
    } else {
 
      format_localized('r');
 
    }
 
  }
 
  void on_24_hour_time() {
 
    write2(tm_hour());
 
    *out_++ = ':';
 
    write2(tm_min());
 
  }
 
  void on_iso_time() {
 
    on_24_hour_time();
 
    *out_++ = ':';
 
    on_second(numeric_system::standard, pad_type::unspecified);
 
  }
 

	
 
  void on_am_pm() {
 
    if (is_classic_) {
 
      *out_++ = tm_hour() < 12 ? 'A' : 'P';
 
      *out_++ = 'M';
 
    } else {
 
      format_localized('p');
 
    }
 
  }
 

	
 
  // These apply to chrono durations but not tm.
 
  void on_duration_value() {}
 
  void on_duration_unit() {}
 
};
 

	
 
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
 
  bool has_precision_integral = false;
 

	
 
  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
 

	
 
  template <typename Char>
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour_time() {}
 
  FMT_CONSTEXPR void on_24_hour_time() {}
 
  FMT_CONSTEXPR void on_iso_time() {}
 
  FMT_CONSTEXPR void on_am_pm() {}
 
  FMT_CONSTEXPR void on_duration_value() const {
 
    if (has_precision_integral) {
 
      FMT_THROW(format_error("precision not allowed for this argument type"));
 
    }
 
  }
 
  FMT_CONSTEXPR void on_duration_unit() {}
 
};
 

	
 
template <typename T,
 
          FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
 
inline bool isfinite(T) {
 
  return true;
 
}
 

	
 
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
inline T mod(T x, int y) {
 
  return x % static_cast<T>(y);
 
}
 
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
 
inline T mod(T x, int y) {
 
  return std::fmod(x, static_cast<T>(y));
 
}
 

	
 
// If T is an integral type, maps T to its unsigned counterpart, otherwise
 
// leaves it unchanged (unlike std::make_unsigned).
 
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
 
struct make_unsigned_or_unchanged {
 
  using type = T;
 
};
 

	
 
template <typename T> struct make_unsigned_or_unchanged<T, true> {
 
  using type = typename std::make_unsigned<T>::type;
 
};
 

	
 
#if FMT_SAFE_DURATION_CAST
 
// throwing version of safe_duration_cast
 
template <typename To, typename FromRep, typename FromPeriod>
 
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
 
  int ec;
 
  To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
 
  if (ec) FMT_THROW(format_error("cannot format duration"));
 
  return to;
 
}
 
#endif
 

	
 
template <typename Rep, typename Period,
 
          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
 
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
 
    std::chrono::duration<Rep, Period> d) {
 
  // this may overflow and/or the result may not fit in the
 
  // target type.
 
#if FMT_SAFE_DURATION_CAST
 
  using CommonSecondsType =
 
      typename std::common_type<decltype(d), std::chrono::seconds>::type;
 
  const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
 
  const auto d_as_whole_seconds =
 
      fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
 
  // this conversion should be nonproblematic
 
  const auto diff = d_as_common - d_as_whole_seconds;
 
  const auto ms =
 
      fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
 
  return ms;
 
#else
 
  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
 
  return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
 
#endif
 
}
 

	
 
template <typename Char, typename Rep, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
 
OutputIt format_duration_value(OutputIt out, Rep val, int) {
 
  return write<Char>(out, val);
 
}
 

	
 
template <typename Char, typename Rep, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
 
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
 
  auto specs = format_specs<Char>();
 
  specs.precision = precision;
 
  specs.type = precision >= 0 ? presentation_type::fixed_lower
 
                              : presentation_type::general_lower;
 
  return write<Char>(out, val, specs);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
 
  return std::copy(unit.begin(), unit.end(), out);
 
}
 

	
 
template <typename OutputIt>
 
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
 
  // This works when wchar_t is UTF-32 because units only contain characters
 
  // that have the same representation in UTF-16 and UTF-32.
 
  utf8_to_utf16 u(unit);
 
  return std::copy(u.c_str(), u.c_str() + u.size(), out);
 
}
 

	
 
template <typename Char, typename Period, typename OutputIt>
 
OutputIt format_duration_unit(OutputIt out) {
 
  if (const char* unit = get_units<Period>())
 
    return copy_unit(string_view(unit), out, Char());
 
  *out++ = '[';
 
  out = write<Char>(out, Period::num);
 
  if (const_check(Period::den != 1)) {
 
    *out++ = '/';
 
    out = write<Char>(out, Period::den);
 
  }
 
  *out++ = ']';
 
  *out++ = 's';
 
  return out;
 
}
 

	
 
class get_locale {
 
 private:
 
  union {
 
    std::locale locale_;
 
  };
 
  bool has_locale_ = false;
 

	
 
 public:
 
  get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
 
    if (localized)
 
      ::new (&locale_) std::locale(loc.template get<std::locale>());
 
  }
 
  ~get_locale() {
 
    if (has_locale_) locale_.~locale();
 
  }
 
  operator const std::locale&() const {
 
    return has_locale_ ? locale_ : get_classic_locale();
 
  }
 
};
 

	
 
template <typename FormatContext, typename OutputIt, typename Rep,
 
          typename Period>
 
struct chrono_formatter {
 
  FormatContext& context;
 
  OutputIt out;
 
  int precision;
 
  bool localized = false;
 
  // rep is unsigned to avoid overflow.
 
  using rep =
 
      conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
 
                    unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
 
  rep val;
 
  using seconds = std::chrono::duration<rep>;
 
  seconds s;
 
  using milliseconds = std::chrono::duration<rep, std::milli>;
 
  bool negative;
 

	
 
  using char_type = typename FormatContext::char_type;
 
  using tm_writer_type = tm_writer<OutputIt, char_type>;
 

	
 
  chrono_formatter(FormatContext& ctx, OutputIt o,
 
                   std::chrono::duration<Rep, Period> d)
 
      : context(ctx),
 
        out(o),
 
        val(static_cast<rep>(d.count())),
 
        negative(false) {
 
    if (d.count() < 0) {
 
      val = 0 - val;
 
      negative = true;
 
    }
 

	
 
    // this may overflow and/or the result may not fit in the
 
    // target type.
 
#if FMT_SAFE_DURATION_CAST
 
    // might need checked conversion (rep!=Rep)
 
    auto tmpval = std::chrono::duration<rep, Period>(val);
 
    s = fmt_safe_duration_cast<seconds>(tmpval);
 
#else
 
    s = std::chrono::duration_cast<seconds>(
 
        std::chrono::duration<rep, Period>(val));
 
#endif
 
  }
 

	
 
  // returns true if nan or inf, writes to out.
 
  bool handle_nan_inf() {
 
    if (isfinite(val)) {
 
      return false;
 
    }
 
    if (isnan(val)) {
 
      write_nan();
 
      return true;
 
    }
 
    // must be +-inf
 
    if (val > 0) {
 
      write_pinf();
 
    } else {
 
      write_ninf();
 
    }
 
    return true;
 
  }
 

	
 
  Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
 

	
 
  Rep hour12() const {
 
    Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
 
    return hour <= 0 ? 12 : hour;
 
  }
 

	
 
  Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
 
  Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
 

	
 
  std::tm time() const {
 
    auto time = std::tm();
 
    time.tm_hour = to_nonnegative_int(hour(), 24);
 
    time.tm_min = to_nonnegative_int(minute(), 60);
 
    time.tm_sec = to_nonnegative_int(second(), 60);
 
    return time;
 
  }
 

	
 
  void write_sign() {
 
    if (negative) {
 
      *out++ = '-';
 
      negative = false;
 
    }
 
  }
 

	
 
  void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
 
    write_sign();
 
    if (isnan(value)) return write_nan();
 
    uint32_or_64_or_128_t<int> n =
 
        to_unsigned(to_nonnegative_int(value, max_value<int>()));
 
    int num_digits = detail::count_digits(n);
 
    if (width > num_digits) {
 
      out = detail::write_padding(out, pad, width - num_digits);
 
    }
 
    out = format_decimal<char_type>(out, n, num_digits).end;
 
  }
 

	
 
  void write_nan() { std::copy_n("nan", 3, out); }
 
  void write_pinf() { std::copy_n("inf", 3, out); }
 
  void write_ninf() { std::copy_n("-inf", 4, out); }
 

	
 
  template <typename Callback, typename... Args>
 
  void format_tm(const tm& time, Callback cb, Args... args) {
 
    if (isnan(val)) return write_nan();
 
    get_locale loc(localized, context.locale());
 
    auto w = tm_writer_type(loc, out, time);
 
    (w.*cb)(args...);
 
    out = w.out();
 
  }
 

	
 
  void on_text(const char_type* begin, const char_type* end) {
 
    std::copy(begin, end, out);
 
  }
 

	
 
  // These are not implemented because durations don't have date information.
 
  void on_abbr_weekday() {}
 
  void on_full_weekday() {}
 
  void on_dec0_weekday(numeric_system) {}
 
  void on_dec1_weekday(numeric_system) {}
 
  void on_abbr_month() {}
 
  void on_full_month() {}
 
  void on_datetime(numeric_system) {}
 
  void on_loc_date(numeric_system) {}
 
  void on_loc_time(numeric_system) {}
 
  void on_us_date() {}
 
  void on_iso_date() {}
 
  void on_utc_offset(numeric_system) {}
 
  void on_tz_name() {}
 
  void on_year(numeric_system) {}
 
  void on_short_year(numeric_system) {}
 
  void on_offset_year() {}
 
  void on_century(numeric_system) {}
 
  void on_iso_week_based_year() {}
 
  void on_iso_week_based_short_year() {}
 
  void on_dec_month(numeric_system) {}
 
  void on_dec0_week_of_year(numeric_system) {}
 
  void on_dec1_week_of_year(numeric_system) {}
 
  void on_iso_week_of_year(numeric_system) {}
 
  void on_day_of_year() {}
 
  void on_day_of_month(numeric_system) {}
 
  void on_day_of_month_space(numeric_system) {}
 

	
 
  void on_24_hour(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(hour(), 2, pad);
 
    auto time = tm();
 
    time.tm_hour = to_nonnegative_int(hour(), 24);
 
    format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
 
  }
 

	
 
  void on_12_hour(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(hour12(), 2, pad);
 
    auto time = tm();
 
    time.tm_hour = to_nonnegative_int(hour12(), 12);
 
    format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
 
  }
 

	
 
  void on_minute(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(minute(), 2, pad);
 
    auto time = tm();
 
    time.tm_min = to_nonnegative_int(minute(), 60);
 
    format_tm(time, &tm_writer_type::on_minute, ns, pad);
 
  }
 

	
 
  void on_second(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) {
 
      if (std::is_floating_point<rep>::value) {
 
        auto buf = memory_buffer();
 
        write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
 
                               precision);
 
        if (negative) *out++ = '-';
 
        if (buf.size() < 2 || buf[1] == '.') {
 
          out = detail::write_padding(out, pad);
 
        }
 
        out = std::copy(buf.begin(), buf.end(), out);
 
      } else {
 
        write(second(), 2, pad);
 
        write_fractional_seconds<char_type>(
 
            out, std::chrono::duration<rep, Period>(val), precision);
 
      }
 
      return;
 
    }
 
    auto time = tm();
 
    time.tm_sec = to_nonnegative_int(second(), 60);
 
    format_tm(time, &tm_writer_type::on_second, ns, pad);
 
  }
 

	
 
  void on_12_hour_time() {
 
    if (handle_nan_inf()) return;
 
    format_tm(time(), &tm_writer_type::on_12_hour_time);
 
  }
 

	
 
  void on_24_hour_time() {
 
    if (handle_nan_inf()) {
 
      *out++ = ':';
 
      handle_nan_inf();
 
      return;
 
    }
 

	
 
    write(hour(), 2);
 
    *out++ = ':';
 
    write(minute(), 2);
 
  }
 

	
 
  void on_iso_time() {
 
    on_24_hour_time();
 
    *out++ = ':';
 
    if (handle_nan_inf()) return;
 
    on_second(numeric_system::standard, pad_type::unspecified);
 
  }
 

	
 
  void on_am_pm() {
 
    if (handle_nan_inf()) return;
 
    format_tm(time(), &tm_writer_type::on_am_pm);
 
  }
 

	
 
  void on_duration_value() {
 
    if (handle_nan_inf()) return;
 
    write_sign();
 
    out = format_duration_value<char_type>(out, val, precision);
 
  }
 

	
 
  void on_duration_unit() {
 
    out = format_duration_unit<char_type, Period>(out);
 
  }
 
};
 

	
 
FMT_END_DETAIL_NAMESPACE
 

	
 
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
 
using weekday = std::chrono::weekday;
 
#else
 
// A fallback version of weekday.
 
class weekday {
 
 private:
 
  unsigned char value;
 

	
 
 public:
 
  weekday() = default;
 
  explicit constexpr weekday(unsigned wd) noexcept
 
      : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
 
  constexpr unsigned c_encoding() const noexcept { return value; }
 
};
 

	
 
class year_month_day {};
 
#endif
 

	
 
// A rudimentary weekday formatter.
 
template <typename Char> struct formatter<weekday, Char> {
 
 private:
 
  bool localized = false;
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin != end && *begin == 'L') {
 
      ++begin;
 
      localized = true;
 
    }
 
    return begin;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    auto time = std::tm();
 
    time.tm_wday = static_cast<int>(wd.c_encoding());
 
    detail::get_locale loc(localized, ctx.locale());
 
    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
 
    w.on_abbr_weekday();
 
    return w.out();
 
  }
 
};
 

	
 
template <typename Rep, typename Period, typename Char>
 
struct formatter<std::chrono::duration<Rep, Period>, Char> {
 
 private:
 
  format_specs<Char> specs;
 
  int precision = -1;
 
  using arg_ref_type = detail::arg_ref<Char>;
 
  arg_ref_type width_ref;
 
  arg_ref_type precision_ref;
 
  bool localized = false;
 
  basic_string_view<Char> format_str;
 
  using duration = std::chrono::duration<Rep, Period>;
 

	
 
  using iterator = typename basic_format_parse_context<Char>::iterator;
 
  struct parse_range {
 
    iterator begin;
 
    iterator end;
 
  };
 

	
 
  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin == end || *begin == '}') return {begin, begin};
 

	
 
    begin = detail::parse_align(begin, end, specs);
 
    if (begin == end) return {begin, begin};
 

	
 
    begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
 
    if (begin == end) return {begin, begin};
 

	
 
    auto checker = detail::chrono_format_checker();
 
    if (*begin == '.') {
 
      checker.has_precision_integral = !std::is_floating_point<Rep>::value;
 
      begin =
 
          detail::parse_precision(begin, end, precision, precision_ref, ctx);
 
    }
 
    if (begin != end && *begin == 'L') {
 
      ++begin;
 
      localized = true;
 
    }
 
    end = detail::parse_chrono_format(begin, end, checker);
 
    return {begin, end};
 
  }
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto range = do_parse(ctx);
 
    format_str = basic_string_view<Char>(
 
        &*range.begin, detail::to_unsigned(range.end - range.begin));
 
    return range.end;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const duration& d, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    auto specs_copy = specs;
 
    auto precision_copy = precision;
 
    auto begin = format_str.begin(), end = format_str.end();
 
    // As a possible future optimization, we could avoid extra copying if width
 
    // is not specified.
 
    basic_memory_buffer<Char> buf;
 
    auto out = std::back_inserter(buf);
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
 
                                                       width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
 
                                                           precision_ref, ctx);
 
    if (begin == end || *begin == '}') {
 
      out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
 
      detail::format_duration_unit<Char, Period>(out);
 
    } else {
 
      detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
 
          ctx, out, d);
 
      f.precision = precision_copy;
 
      f.localized = localized;
 
      detail::parse_chrono_format(begin, end, f);
 
    }
 
    return detail::write(
 
        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
 
  }
 
};
 

	
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
 
                 Char> : formatter<std::tm, Char> {
 
  FMT_CONSTEXPR formatter() {
 
    this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
 
              FormatContext& ctx) const -> decltype(ctx.out()) {
 
    using period = typename Duration::period;
 
    if (period::num != 1 || period::den != 1 ||
 
        std::is_floating_point<typename Duration::rep>::value) {
 
      const auto epoch = val.time_since_epoch();
 
      auto subsecs = std::chrono::duration_cast<Duration>(
 
          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
 

	
 
      if (subsecs.count() < 0) {
 
        auto second = std::chrono::seconds(1);
 
        if (epoch.count() < ((Duration::min)() + second).count())
 
          FMT_THROW(format_error("duration is too small"));
 
        subsecs += second;
 
        val -= second;
 
      }
 

	
 
      return formatter<std::tm, Char>::do_format(
 
          gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
 
          &subsecs);
 
    }
 

	
 
    return formatter<std::tm, Char>::format(
 
        gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
 
  }
 
};
 

	
 
#if FMT_USE_LOCAL_TIME
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::local_time<Duration>, Char>
 
    : formatter<std::tm, Char> {
 
  FMT_CONSTEXPR formatter() {
 
    this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    using period = typename Duration::period;
 
    if (period::num != 1 || period::den != 1 ||
 
        std::is_floating_point<typename Duration::rep>::value) {
 
      const auto epoch = val.time_since_epoch();
 
      const auto subsecs = std::chrono::duration_cast<Duration>(
 
          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
 

	
 
      return formatter<std::tm, Char>::do_format(
 
          localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
 
          ctx, &subsecs);
 
    }
 

	
 
    return formatter<std::tm, Char>::format(
 
        localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
 
        ctx);
 
  }
 
};
 
#endif
 

	
 
#if FMT_USE_UTC_TIME
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
 
                 Char>
 
    : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
 
                Char> {
 
  template <typename FormatContext>
 
  auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
 
              FormatContext& ctx) const -> decltype(ctx.out()) {
 
    return formatter<
 
        std::chrono::time_point<std::chrono::system_clock, Duration>,
 
        Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
 
  }
 
};
 
#endif
 

	
 
template <typename Char> struct formatter<std::tm, Char> {
 
 private:
 
  format_specs<Char> specs;
 
  detail::arg_ref<Char> width_ref;
 

	
 
 protected:
 
  basic_string_view<Char> format_str;
 

	
 
  FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin == end || *begin == '}') return begin;
 

	
 
    begin = detail::parse_align(begin, end, specs);
 
    if (begin == end) return end;
 

	
 
    begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
 
    if (begin == end) return end;
 

	
 
    end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
 
    // Replace default format_str only if the new spec is not empty.
 
    if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
 
    return end;
 
  }
 

	
 
  template <typename FormatContext, typename Duration>
 
  auto do_format(const std::tm& tm, FormatContext& ctx,
 
                 const Duration* subsecs) const -> decltype(ctx.out()) {
 
    auto specs_copy = specs;
 
    basic_memory_buffer<Char> buf;
 
    auto out = std::back_inserter(buf);
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
 
                                                       width_ref, ctx);
 

	
 
    const auto loc_ref = ctx.locale();
 
    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
 
    auto w =
 
        detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
 
    detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
 
    return detail::write(
 
        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
 
  }
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    return this->do_parse(ctx);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const std::tm& tm, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
 
  }
 
};
 

	
 
FMT_END_EXPORT
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_CHRONO_H_
src/3rdparty/fmt/core.h
Show inline comments
 
// Formatting library for C++ - the core API
 
// Formatting library for C++ - the core API for char/UTF-8
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
@@ -8,54 +8,59 @@
 
#ifndef FMT_CORE_H_
 
#define FMT_CORE_H_
 

	
 
#include <cstddef>  // std::byte
 
#include <cstdio>  // std::FILE
 
#include <cstring>
 
#include <functional>
 
#include <cstring>  // std::strlen
 
#include <iterator>
 
#include <memory>
 
#include <limits>
 
#include <string>
 
#include <type_traits>
 
#include <vector>
 

	
 
// The fmt library version in the form major * 10000 + minor * 100 + patch.
 
#define FMT_VERSION 70103
 

	
 
#ifdef __clang__
 
#define FMT_VERSION 100000
 

	
 
#if defined(__clang__) && !defined(__ibmxl__)
 
#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
 
#else
 
#  define FMT_CLANG_VERSION 0
 
#endif
 

	
 
#if defined(__GNUC__) && !defined(__clang__)
 
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
 
    !defined(__NVCOMPILER)
 
#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
 
#else
 
#  define FMT_GCC_VERSION 0
 
#endif
 

	
 
#if defined(__INTEL_COMPILER)
 
#ifndef FMT_GCC_PRAGMA
 
// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
 
#  if FMT_GCC_VERSION >= 504
 
#    define FMT_GCC_PRAGMA(arg) _Pragma(arg)
 
#  else
 
#    define FMT_GCC_PRAGMA(arg)
 
#  endif
 
#endif
 

	
 
#ifdef __ICL
 
#  define FMT_ICC_VERSION __ICL
 
#elif defined(__INTEL_COMPILER)
 
#  define FMT_ICC_VERSION __INTEL_COMPILER
 
#else
 
#  define FMT_ICC_VERSION 0
 
#endif
 

	
 
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
 
#  define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
 
#else
 
#  define FMT_HAS_GXX_CXX11 0
 
#endif
 

	
 
#ifdef __NVCC__
 
#  define FMT_NVCC __NVCC__
 
#ifdef _MSC_VER
 
#  define FMT_MSC_VERSION _MSC_VER
 
#  define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
 
#else
 
#  define FMT_NVCC 0
 
#  define FMT_MSC_VERSION 0
 
#  define FMT_MSC_WARNING(...)
 
#endif
 

	
 
#ifdef _MSC_VER
 
#  define FMT_MSC_VER _MSC_VER
 
#  define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n))
 
#ifdef _MSVC_LANG
 
#  define FMT_CPLUSPLUS _MSVC_LANG
 
#else
 
#  define FMT_MSC_VER 0
 
#  define FMT_SUPPRESS_MSC_WARNING(n)
 
#  define FMT_CPLUSPLUS __cplusplus
 
#endif
 

	
 
#ifdef __has_feature
 
@@ -64,8 +69,7 @@
 
#  define FMT_HAS_FEATURE(x) 0
 
#endif
 

	
 
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
 
    (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
 
#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
 
#  define FMT_HAS_INCLUDE(x) __has_include(x)
 
#else
 
#  define FMT_HAS_INCLUDE(x) 0
 
@@ -78,96 +82,76 @@
 
#endif
 

	
 
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
 
  (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
  (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 

	
 
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
 
  (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
  (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 

	
 
// Check if relaxed C++14 constexpr is supported.
 
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
 
#ifndef FMT_USE_CONSTEXPR
 
#  define FMT_USE_CONSTEXPR                                           \
 
    (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
 
     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) &&           \
 
        !FMT_NVCC && !FMT_ICC_VERSION
 
#  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
 
       (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
 
      !FMT_ICC_VERSION && !defined(__NVCC__)
 
#    define FMT_USE_CONSTEXPR 1
 
#  else
 
#    define FMT_USE_CONSTEXPR 0
 
#  endif
 
#endif
 
#if FMT_USE_CONSTEXPR
 
#  define FMT_CONSTEXPR constexpr
 
#  define FMT_CONSTEXPR_DECL constexpr
 
#else
 
#  define FMT_CONSTEXPR inline
 
#  define FMT_CONSTEXPR_DECL
 
#  define FMT_CONSTEXPR
 
#endif
 

	
 
#if ((FMT_CPLUSPLUS >= 202002L) &&                            \
 
     (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
 
    (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
 
#  define FMT_CONSTEXPR20 constexpr
 
#else
 
#  define FMT_CONSTEXPR20
 
#endif
 

	
 
#ifndef FMT_OVERRIDE
 
#  if FMT_HAS_FEATURE(cxx_override_control) || \
 
      (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
 
#    define FMT_OVERRIDE override
 
#  else
 
#    define FMT_OVERRIDE
 
// Check if constexpr std::char_traits<>::{compare,length} are supported.
 
#if defined(__GLIBCXX__)
 
#  if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
 
      _GLIBCXX_RELEASE >= 7  // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
 
#    define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#  endif
 
#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
 
    _LIBCPP_VERSION >= 4000
 
#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
 
#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#endif
 
#ifndef FMT_CONSTEXPR_CHAR_TRAITS
 
#  define FMT_CONSTEXPR_CHAR_TRAITS
 
#endif
 

	
 
// Check if exceptions are disabled.
 
#ifndef FMT_EXCEPTIONS
 
#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
 
      FMT_MSC_VER && !_HAS_EXCEPTIONS
 
      (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
 
#    define FMT_EXCEPTIONS 0
 
#  else
 
#    define FMT_EXCEPTIONS 1
 
#  endif
 
#endif
 

	
 
// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
 
#ifndef FMT_USE_NOEXCEPT
 
#  define FMT_USE_NOEXCEPT 0
 
#endif
 

	
 
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
 
    (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
 
#  define FMT_DETECTED_NOEXCEPT noexcept
 
#  define FMT_HAS_CXX11_NOEXCEPT 1
 
#else
 
#  define FMT_DETECTED_NOEXCEPT throw()
 
#  define FMT_HAS_CXX11_NOEXCEPT 0
 
#endif
 

	
 
#ifndef FMT_NOEXCEPT
 
#  if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
 
#    define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
 
#  else
 
#    define FMT_NOEXCEPT
 
#  endif
 
#endif
 

	
 
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
 
// warnings.
 
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
 
    !FMT_NVCC
 
// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
 
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
 
    !defined(__NVCC__)
 
#  define FMT_NORETURN [[noreturn]]
 
#else
 
#  define FMT_NORETURN
 
#endif
 

	
 
#ifndef FMT_DEPRECATED
 
#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
 
#    define FMT_DEPRECATED [[deprecated]]
 
#  else
 
#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
 
#      define FMT_DEPRECATED __attribute__((deprecated))
 
#    elif FMT_MSC_VER
 
#      define FMT_DEPRECATED __declspec(deprecated)
 
#ifndef FMT_NODISCARD
 
#  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
 
#    define FMT_NODISCARD [[nodiscard]]
 
#    else
 
#      define FMT_DEPRECATED /* deprecated */
 
#    endif
 
#  endif
 
#    define FMT_NODISCARD
 
#endif
 

	
 
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
 
#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
 
#  define FMT_DEPRECATED_ALIAS
 
#else
 
#  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
 
#endif
 

	
 
#ifndef FMT_INLINE
 
@@ -178,86 +162,107 @@
 
#  endif
 
#endif
 

	
 
#ifndef FMT_USE_INLINE_NAMESPACES
 
#  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
 
      (FMT_MSC_VER >= 1900 && !_MANAGED)
 
#    define FMT_USE_INLINE_NAMESPACES 1
 
// An inline std::forward replacement.
 
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
 

	
 
#ifdef _MSC_VER
 
#  define FMT_UNCHECKED_ITERATOR(It) \
 
    using _Unchecked_type = It  // Mark iterator as checked.
 
#  else
 
#    define FMT_USE_INLINE_NAMESPACES 0
 
#  endif
 
#  define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
 
#endif
 

	
 
#ifndef FMT_BEGIN_NAMESPACE
 
#  if FMT_USE_INLINE_NAMESPACES
 
#    define FMT_INLINE_NAMESPACE inline namespace
 
#  define FMT_BEGIN_NAMESPACE \
 
    namespace fmt {           \
 
    inline namespace v10 {
 
#    define FMT_END_NAMESPACE \
 
      }                       \
 
      }
 
#  else
 
#    define FMT_INLINE_NAMESPACE namespace
 
#    define FMT_END_NAMESPACE \
 
      }                       \
 
      using namespace v7;     \
 
      }
 
#  endif
 
#  define FMT_BEGIN_NAMESPACE \
 
    namespace fmt {           \
 
    FMT_INLINE_NAMESPACE v7 {
 

	
 
#ifndef FMT_MODULE_EXPORT
 
#  define FMT_MODULE_EXPORT
 
#  define FMT_BEGIN_EXPORT
 
#  define FMT_END_EXPORT
 
#endif
 

	
 
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
 
#  define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275)
 
#  ifdef FMT_EXPORT
 
#  ifdef FMT_LIB_EXPORT
 
#    define FMT_API __declspec(dllexport)
 
#    define FMT_EXTERN_TEMPLATE_API FMT_API
 
#    define FMT_EXPORTED
 
#  elif defined(FMT_SHARED)
 
#    define FMT_API __declspec(dllimport)
 
#    define FMT_EXTERN_TEMPLATE_API FMT_API
 
#  endif
 
#else
 
#  define FMT_CLASS_API
 
#  if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
 
#    if defined(__GNUC__) || defined(__clang__)
 
#      define FMT_API __attribute__((visibility("default")))
 
#    endif
 
#  endif
 
#endif
 
#ifndef FMT_API
 
#  define FMT_API
 
#endif
 
#ifndef FMT_EXTERN_TEMPLATE_API
 
#  define FMT_EXTERN_TEMPLATE_API
 
#endif
 
#ifndef FMT_INSTANTIATION_DEF_API
 
#  define FMT_INSTANTIATION_DEF_API FMT_API
 
#endif
 

	
 
#ifndef FMT_HEADER_ONLY
 
#  define FMT_EXTERN extern
 
#else
 
#  define FMT_EXTERN
 
#endif
 

	
 
// libc++ supports string_view in pre-c++17.
 
#if (FMT_HAS_INCLUDE(<string_view>) &&                       \
 
     (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
 
    (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
 
#if FMT_HAS_INCLUDE(<string_view>) && \
 
    (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
 
#  include <string_view>
 
#  define FMT_USE_STRING_VIEW
 
#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
 
#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
 
#  include <experimental/string_view>
 
#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
 
#endif
 

	
 
#ifndef FMT_UNICODE
 
#  define FMT_UNICODE !FMT_MSC_VER
 
#  define FMT_UNICODE !FMT_MSC_VERSION
 
#endif
 

	
 
#ifndef FMT_CONSTEVAL
 
#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
 
       (!defined(__apple_build_version__) ||                     \
 
        __apple_build_version__ >= 14000029L) &&                 \
 
       FMT_CPLUSPLUS >= 202002L) ||                              \
 
      (defined(__cpp_consteval) &&                               \
 
       (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
 
// consteval is broken in MSVC before VS2022 and Apple clang before 14.
 
#    define FMT_CONSTEVAL consteval
 
#    define FMT_HAS_CONSTEVAL
 
#  else
 
#    define FMT_CONSTEVAL
 
#  endif
 
#endif
 
#if FMT_UNICODE && FMT_MSC_VER
 
#  pragma execution_character_set("utf-8")
 

	
 
#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
 
#  if defined(__cpp_nontype_template_args) &&                  \
 
      ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
 
       __cpp_nontype_template_args >= 201911L) &&              \
 
      !defined(__NVCOMPILER) && !defined(__LCC__)
 
#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
 
#  else
 
#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
 
#  endif
 
#endif
 

	
 
#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
 
#  define FMT_INLINE_VARIABLE inline
 
#else
 
#  define FMT_INLINE_VARIABLE
 
#endif
 

	
 
// Enable minimal optimizations for more compact code in debug mode.
 
FMT_GCC_PRAGMA("GCC push_options")
 
#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
 
    !defined(__CUDACC__)
 
FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
 
#endif
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
// Implementations of enable_if_t and other metafunctions for older systems.
 
template <bool B, class T = void>
 
template <bool B, typename T = void>
 
using enable_if_t = typename std::enable_if<B, T>::type;
 
template <bool B, class T, class F>
 
template <bool B, typename T, typename F>
 
using conditional_t = typename std::conditional<B, T, F>::type;
 
template <bool B> using bool_constant = std::integral_constant<bool, B>;
 
template <typename T>
 
@@ -268,31 +273,70 @@ template <typename T>
 
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
 
template <typename T> struct type_identity { using type = T; };
 
template <typename T> using type_identity_t = typename type_identity<T>::type;
 

	
 
struct monostate {};
 
template <typename T>
 
using underlying_t = typename std::underlying_type<T>::type;
 

	
 
struct monostate {
 
  constexpr monostate() {}
 
};
 

	
 
// An enable_if helper to be used in template parameters which results in much
 
// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
 
// to workaround a bug in MSVC 2019 (see #1140 and #1186).
 
#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
 
#ifdef FMT_DOC
 
#  define FMT_ENABLE_IF(...)
 
#else
 
#  define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
 
#endif
 

	
 
#ifdef __cpp_lib_byte
 
inline auto format_as(std::byte b) -> unsigned char {
 
  return static_cast<unsigned char>(b);
 
}
 
#endif
 

	
 
namespace detail {
 

	
 
// A helper function to suppress "conditional expression is constant" warnings.
 
template <typename T> constexpr T const_check(T value) { return value; }
 
// Suppresses "unused variable" warnings with the method described in
 
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
 
// (void)var does not work on many Intel compilers.
 
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
 

	
 
constexpr FMT_INLINE auto is_constant_evaluated(
 
    bool default_value = false) noexcept -> bool {
 
// Workaround for incompatibility between libstdc++ consteval-based
 
// std::is_constant_evaluated() implementation and clang-14.
 
// https://github.com/fmtlib/fmt/issues/3247
 
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
 
    _GLIBCXX_RELEASE >= 12 &&                                \
 
    (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
 
  ignore_unused(default_value);
 
  return __builtin_is_constant_evaluated();
 
#elif defined(__cpp_lib_is_constant_evaluated)
 
  ignore_unused(default_value);
 
  return std::is_constant_evaluated();
 
#else
 
  return default_value;
 
#endif
 
}
 

	
 
// Suppresses "conditional expression is constant" warnings.
 
template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
 
  return value;
 
}
 

	
 
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
 
                                      const char* message);
 

	
 
#ifndef FMT_ASSERT
 
#  ifdef NDEBUG
 
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
 
#    define FMT_ASSERT(condition, message) ((void)0)
 
// FMT_ASSERT is not empty to avoid -Wempty-body.
 
#    define FMT_ASSERT(condition, message) \
 
      fmt::detail::ignore_unused((condition), (message))
 
#  else
 
#    define FMT_ASSERT(condition, message)                                    \
 
      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
 
           ? (void)0                                                          \
 
           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 
           : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 
#  endif
 
#endif
 

	
 
@@ -307,44 +351,42 @@ template <typename T> struct std_string_
 

	
 
#ifdef FMT_USE_INT128
 
// Do nothing.
 
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
 
    !(FMT_CLANG_VERSION && FMT_MSC_VER)
 
#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
 
    !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
 
#  define FMT_USE_INT128 1
 
using int128_t = __int128_t;
 
using uint128_t = __uint128_t;
 
using int128_opt = __int128_t;  // An optional native 128-bit integer.
 
using uint128_opt = __uint128_t;
 
template <typename T> inline auto convert_for_visit(T value) -> T {
 
  return value;
 
}
 
#else
 
#  define FMT_USE_INT128 0
 
#endif
 
#if !FMT_USE_INT128
 
struct int128_t {};
 
struct uint128_t {};
 
enum class int128_opt {};
 
enum class uint128_opt {};
 
// Reduce template instantiations.
 
template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
 
#endif
 

	
 
// Casts a nonnegative integer to unsigned.
 
template <typename Int>
 
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
 
  FMT_ASSERT(value >= 0, "negative value");
 
FMT_CONSTEXPR auto to_unsigned(Int value) ->
 
    typename std::make_unsigned<Int>::type {
 
  FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
 
  return static_cast<typename std::make_unsigned<Int>::type>(value);
 
}
 

	
 
FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5";
 

	
 
template <typename Char> constexpr bool is_unicode() {
 
  return FMT_UNICODE || sizeof(Char) != 1 ||
 
         (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
 
FMT_CONSTEXPR inline auto is_utf8() -> bool {
 
  FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
 

	
 
  // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
 
  using uchar = unsigned char;
 
  return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
 
                         uchar(section[1]) == 0xA7);
 
}
 

	
 
#ifdef __cpp_char8_t
 
using char8_type = char8_t;
 
#else
 
enum char8_type : unsigned char {};
 
#endif
 
}  // namespace detail
 

	
 
#ifdef FMT_USE_INTERNAL
 
namespace internal = detail;  // DEPRECATED
 
#endif
 

	
 
/**
 
  An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
 
  subset of the API. ``fmt::basic_string_view`` is used for format strings even
 
@@ -352,6 +394,7 @@ namespace internal = detail;  // DEPRECA
 
  compiled with a different ``-std`` option than the client code (which is not
 
  recommended).
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Char> class basic_string_view {
 
 private:
 
  const Char* data_;
 
@@ -361,12 +404,11 @@ template <typename Char> class basic_str
 
  using value_type = Char;
 
  using iterator = const Char*;
 

	
 
  constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
 
  constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
 

	
 
  /** Constructs a string reference object from a C string and a size. */
 
  constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
 
      : data_(s),
 
        size_(count) {}
 
  constexpr basic_string_view(const Char* s, size_t count) noexcept
 
      : data_(s), size_(count) {}
 

	
 
  /**
 
    \rst
 
@@ -374,42 +416,58 @@ template <typename Char> class basic_str
 
    the size with ``std::char_traits<Char>::length``.
 
    \endrst
 
   */
 
#if __cplusplus >= 201703L  // C++17's char_traits::length() is constexpr.
 
  FMT_CONSTEXPR
 
#endif
 
  FMT_CONSTEXPR_CHAR_TRAITS
 
  FMT_INLINE
 
  basic_string_view(const Char* s)
 
      : data_(s), size_(std::char_traits<Char>::length(s)) {}
 
      : data_(s),
 
        size_(detail::const_check(std::is_same<Char, char>::value &&
 
                                  !detail::is_constant_evaluated(true))
 
                  ? std::strlen(reinterpret_cast<const char*>(s))
 
                  : std::char_traits<Char>::length(s)) {}
 

	
 
  /** Constructs a string reference from a ``std::basic_string`` object. */
 
  template <typename Traits, typename Alloc>
 
  FMT_CONSTEXPR basic_string_view(
 
      const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
 
      : data_(s.data()),
 
        size_(s.size()) {}
 
      const std::basic_string<Char, Traits, Alloc>& s) noexcept
 
      : data_(s.data()), size_(s.size()) {}
 

	
 
  template <typename S, FMT_ENABLE_IF(std::is_same<
 
                                      S, detail::std_string_view<Char>>::value)>
 
  FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
 
                                                      size_(s.size()) {}
 
  FMT_CONSTEXPR basic_string_view(S s) noexcept
 
      : data_(s.data()), size_(s.size()) {}
 

	
 
  /** Returns a pointer to the string data. */
 
  constexpr const Char* data() const { return data_; }
 
  constexpr auto data() const noexcept -> const Char* { return data_; }
 

	
 
  /** Returns the string size. */
 
  constexpr size_t size() const { return size_; }
 

	
 
  constexpr iterator begin() const { return data_; }
 
  constexpr iterator end() const { return data_ + size_; }
 

	
 
  constexpr const Char& operator[](size_t pos) const { return data_[pos]; }
 

	
 
  FMT_CONSTEXPR void remove_prefix(size_t n) {
 
  constexpr auto size() const noexcept -> size_t { return size_; }
 

	
 
  constexpr auto begin() const noexcept -> iterator { return data_; }
 
  constexpr auto end() const noexcept -> iterator { return data_ + size_; }
 

	
 
  constexpr auto operator[](size_t pos) const noexcept -> const Char& {
 
    return data_[pos];
 
  }
 

	
 
  FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
 
    data_ += n;
 
    size_ -= n;
 
  }
 

	
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
 
      basic_string_view<Char> sv) const noexcept {
 
    return size_ >= sv.size_ &&
 
           std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
 
  }
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
 
    return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
 
  }
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
 
    return starts_with(basic_string_view<Char>(s));
 
  }
 

	
 
  // Lexicographically compare this string reference to other.
 
  int compare(basic_string_view other) const {
 
  FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
 
    size_t str_size = size_ < other.size_ ? size_ : other.size_;
 
    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
 
    if (result == 0)
 
@@ -417,97 +475,77 @@ template <typename Char> class basic_str
 
    return result;
 
  }
 

	
 
  friend bool operator==(basic_string_view lhs, basic_string_view rhs) {
 
  FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
 
                                                   basic_string_view rhs)
 
      -> bool {
 
    return lhs.compare(rhs) == 0;
 
  }
 
  friend bool operator!=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) != 0;
 
  }
 
  friend bool operator<(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) < 0;
 
  }
 
  friend bool operator<=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) <= 0;
 
  }
 
  friend bool operator>(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) > 0;
 
  }
 
  friend bool operator>=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) >= 0;
 
  }
 
};
 

	
 
FMT_MODULE_EXPORT
 
using string_view = basic_string_view<char>;
 
using wstring_view = basic_string_view<wchar_t>;
 

	
 
/** Specifies if ``T`` is a character type. Can be specialized by users. */
 
FMT_MODULE_EXPORT
 
template <typename T> struct is_char : std::false_type {};
 
template <> struct is_char<char> : std::true_type {};
 
template <> struct is_char<wchar_t> : std::true_type {};
 
template <> struct is_char<detail::char8_type> : std::true_type {};
 
template <> struct is_char<char16_t> : std::true_type {};
 
template <> struct is_char<char32_t> : std::true_type {};
 

	
 
/**
 
  \rst
 
  Returns a string view of `s`. In order to add custom string type support to
 
  {fmt} provide an overload of `to_string_view` for it in the same namespace as
 
  the type for the argument-dependent lookup to work.
 

	
 
  **Example**::
 

	
 
    namespace my_ns {
 
    inline string_view to_string_view(const my_string& s) {
 
      return {s.data(), s.length()};
 
    }
 
    }
 
    std::string message = fmt::format(my_string("The answer is {}"), 42);
 
  \endrst
 
 */
 
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
 
inline basic_string_view<Char> to_string_view(const Char* s) {
 
  return s;
 
}
 

	
 
template <typename Char, typename Traits, typename Alloc>
 
inline basic_string_view<Char> to_string_view(
 
    const std::basic_string<Char, Traits, Alloc>& s) {
 
  return s;
 
}
 

	
 
template <typename Char>
 
inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
 
  return s;
 
}
 

	
 
template <typename Char,
 
          FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
 
inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) {
 
  return s;
 
}
 

	
 
// A base class for compile-time strings. It is defined in the fmt namespace to
 
// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
 

	
 
namespace detail {
 

	
 
// A base class for compile-time strings.
 
struct compile_string {};
 

	
 
template <typename S>
 
struct is_compile_string : std::is_base_of<compile_string, S> {};
 

	
 
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
 
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
 
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename Char, typename Traits, typename Alloc>
 
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
 
    -> basic_string_view<Char> {
 
  return s;
 
}
 

	
 
namespace detail {
 
template <typename Char>
 
constexpr auto to_string_view(basic_string_view<Char> s)
 
    -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename Char,
 
          FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
 
inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
constexpr auto to_string_view(const S& s)
 
    -> basic_string_view<typename S::char_type> {
 
  return basic_string_view<typename S::char_type>(s);
 
}
 
void to_string_view(...);
 
using fmt::v7::to_string_view;
 

	
 
// Specifies whether S is a string type convertible to fmt::basic_string_view.
 
// It should be a constexpr function but MSVC 2017 fails to compile it in
 
// enable_if and MSVC 2015 fails to compile it as an alias template.
 
// ADL is intentionally disabled as to_string_view is not an extension point.
 
template <typename S>
 
struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
 
};
 
struct is_string
 
    : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
 

	
 
template <typename S, typename = void> struct char_t_impl {};
 
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
 
@@ -515,464 +553,6 @@ template <typename S> struct char_t_impl
 
  using type = typename result::value_type;
 
};
 

	
 
// Reports a compile-time error if S is not a valid format string.
 
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
 
FMT_INLINE void check_format_string(const S&) {
 
#ifdef FMT_ENFORCE_COMPILE_STRING
 
  static_assert(is_compile_string<S>::value,
 
                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
 
                "FMT_STRING.");
 
#endif
 
}
 
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
void check_format_string(S);
 

	
 
struct error_handler {
 
  constexpr error_handler() = default;
 

	
 
  // This function is intentionally not constexpr to give a compile-time error.
 
  FMT_NORETURN FMT_API void on_error(const char* message);
 
};
 
}  // namespace detail
 

	
 
/** String's character type. */
 
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
 

	
 
/**
 
  \rst
 
  Parsing context consisting of a format string range being parsed and an
 
  argument counter for automatic indexing.
 

	
 
  You can use one of the following type aliases for common character types:
 

	
 
  +-----------------------+-------------------------------------+
 
  | Type                  | Definition                          |
 
  +=======================+=====================================+
 
  | format_parse_context  | basic_format_parse_context<char>    |
 
  +-----------------------+-------------------------------------+
 
  | wformat_parse_context | basic_format_parse_context<wchar_t> |
 
  +-----------------------+-------------------------------------+
 
  \endrst
 
 */
 
template <typename Char, typename ErrorHandler = detail::error_handler>
 
class basic_format_parse_context : private ErrorHandler {
 
 private:
 
  basic_string_view<Char> format_str_;
 
  int next_arg_id_;
 

	
 
 public:
 
  using char_type = Char;
 
  using iterator = typename basic_string_view<Char>::iterator;
 

	
 
  explicit constexpr basic_format_parse_context(
 
      basic_string_view<Char> format_str, ErrorHandler eh = {},
 
      int next_arg_id = 0)
 
      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
 

	
 
  /**
 
    Returns an iterator to the beginning of the format string range being
 
    parsed.
 
   */
 
  constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); }
 

	
 
  /**
 
    Returns an iterator past the end of the format string range being parsed.
 
   */
 
  constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
 

	
 
  /** Advances the begin iterator to ``it``. */
 
  FMT_CONSTEXPR void advance_to(iterator it) {
 
    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
 
  }
 

	
 
  /**
 
    Reports an error if using the manual argument indexing; otherwise returns
 
    the next argument index and switches to the automatic indexing.
 
   */
 
  FMT_CONSTEXPR int next_arg_id() {
 
    // Don't check if the argument id is valid to avoid overhead and because it
 
    // will be checked during formatting anyway.
 
    if (next_arg_id_ >= 0) return next_arg_id_++;
 
    on_error("cannot switch from manual to automatic argument indexing");
 
    return 0;
 
  }
 

	
 
  /**
 
    Reports an error if using the automatic argument indexing; otherwise
 
    switches to the manual indexing.
 
   */
 
  FMT_CONSTEXPR void check_arg_id(int) {
 
    if (next_arg_id_ > 0)
 
      on_error("cannot switch from automatic to manual argument indexing");
 
    else
 
      next_arg_id_ = -1;
 
  }
 

	
 
  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    ErrorHandler::on_error(message);
 
  }
 

	
 
  constexpr ErrorHandler error_handler() const { return *this; }
 
};
 

	
 
using format_parse_context = basic_format_parse_context<char>;
 
using wformat_parse_context = basic_format_parse_context<wchar_t>;
 

	
 
template <typename Context> class basic_format_arg;
 
template <typename Context> class basic_format_args;
 
template <typename Context> class dynamic_format_arg_store;
 

	
 
// A formatter for objects of type T.
 
template <typename T, typename Char = char, typename Enable = void>
 
struct formatter {
 
  // A deleted default constructor indicates a disabled formatter.
 
  formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled formatter specialization. A type can be
 
// formattable even if it doesn't have a formatter e.g. via a conversion.
 
template <typename T, typename Context>
 
using has_formatter =
 
    std::is_constructible<typename Context::template formatter_type<T>>;
 

	
 
// Checks whether T is a container with contiguous storage.
 
template <typename T> struct is_contiguous : std::false_type {};
 
template <typename Char>
 
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
 

	
 
namespace detail {
 

	
 
// Extracts a reference to the container from back_insert_iterator.
 
template <typename Container>
 
inline Container& get_container(std::back_insert_iterator<Container> it) {
 
  using bi_iterator = std::back_insert_iterator<Container>;
 
  struct accessor : bi_iterator {
 
    accessor(bi_iterator iter) : bi_iterator(iter) {}
 
    using bi_iterator::container;
 
  };
 
  return *accessor(it).container;
 
}
 

	
 
/**
 
  \rst
 
  A contiguous memory buffer with an optional growing ability. It is an internal
 
  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
 
  \endrst
 
 */
 
template <typename T> class buffer {
 
 private:
 
  T* ptr_;
 
  size_t size_;
 
  size_t capacity_;
 

	
 
 protected:
 
  // Don't initialize ptr_ since it is not accessed to save a few cycles.
 
  FMT_SUPPRESS_MSC_WARNING(26495)
 
  buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
 

	
 
  buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
 
      : ptr_(p),
 
        size_(sz),
 
        capacity_(cap) {}
 

	
 
  ~buffer() = default;
 

	
 
  /** Sets the buffer data and capacity. */
 
  void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
 
    ptr_ = buf_data;
 
    capacity_ = buf_capacity;
 
  }
 

	
 
  /** Increases the buffer capacity to hold at least *capacity* elements. */
 
  virtual void grow(size_t capacity) = 0;
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  buffer(const buffer&) = delete;
 
  void operator=(const buffer&) = delete;
 

	
 
  T* begin() FMT_NOEXCEPT { return ptr_; }
 
  T* end() FMT_NOEXCEPT { return ptr_ + size_; }
 

	
 
  const T* begin() const FMT_NOEXCEPT { return ptr_; }
 
  const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
 

	
 
  /** Returns the size of this buffer. */
 
  size_t size() const FMT_NOEXCEPT { return size_; }
 

	
 
  /** Returns the capacity of this buffer. */
 
  size_t capacity() const FMT_NOEXCEPT { return capacity_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  T* data() FMT_NOEXCEPT { return ptr_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  const T* data() const FMT_NOEXCEPT { return ptr_; }
 

	
 
  /** Clears this buffer. */
 
  void clear() { size_ = 0; }
 

	
 
  // Tries resizing the buffer to contain *count* elements. If T is a POD type
 
  // the new elements may not be initialized.
 
  void try_resize(size_t count) {
 
    try_reserve(count);
 
    size_ = count <= capacity_ ? count : capacity_;
 
  }
 

	
 
  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
 
  // capacity by a smaller amount than requested but guarantees there is space
 
  // for at least one additional element either by increasing the capacity or by
 
  // flushing the buffer if it is full.
 
  void try_reserve(size_t new_capacity) {
 
    if (new_capacity > capacity_) grow(new_capacity);
 
  }
 

	
 
  void push_back(const T& value) {
 
    try_reserve(size_ + 1);
 
    ptr_[size_++] = value;
 
  }
 

	
 
  /** Appends data to the end of the buffer. */
 
  template <typename U> void append(const U* begin, const U* end);
 

	
 
  template <typename I> T& operator[](I index) { return ptr_[index]; }
 
  template <typename I> const T& operator[](I index) const {
 
    return ptr_[index];
 
  }
 
};
 

	
 
struct buffer_traits {
 
  explicit buffer_traits(size_t) {}
 
  size_t count() const { return 0; }
 
  size_t limit(size_t size) { return size; }
 
};
 

	
 
class fixed_buffer_traits {
 
 private:
 
  size_t count_ = 0;
 
  size_t limit_;
 

	
 
 public:
 
  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
 
  size_t count() const { return count_; }
 
  size_t limit(size_t size) {
 
    size_t n = limit_ > count_ ? limit_ - count_ : 0;
 
    count_ += size;
 
    return size < n ? size : n;
 
  }
 
};
 

	
 
// A buffer that writes to an output iterator when flushed.
 
template <typename OutputIt, typename T, typename Traits = buffer_traits>
 
class iterator_buffer final : public Traits, public buffer<T> {
 
 private:
 
  OutputIt out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {
 
    if (this->size() == buffer_size) flush();
 
  }
 
  void flush();
 

	
 
 public:
 
  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
 
      : Traits(n),
 
        buffer<T>(data_, 0, buffer_size),
 
        out_(out) {}
 
  ~iterator_buffer() { flush(); }
 

	
 
  OutputIt out() {
 
    flush();
 
    return out_;
 
  }
 
  size_t count() const { return Traits::count() + this->size(); }
 
};
 

	
 
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {}
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
 

	
 
  T* out() { return &*this->end(); }
 
};
 

	
 
// A buffer that writes to a container with the contiguous storage.
 
template <typename Container>
 
class iterator_buffer<std::back_insert_iterator<Container>,
 
                      enable_if_t<is_contiguous<Container>::value,
 
                                  typename Container::value_type>>
 
    final : public buffer<typename Container::value_type> {
 
 private:
 
  Container& container_;
 

	
 
 protected:
 
  void grow(size_t capacity) final FMT_OVERRIDE {
 
    container_.resize(capacity);
 
    this->set(&container_[0], capacity);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(Container& c)
 
      : buffer<typename Container::value_type>(c.size()), container_(c) {}
 
  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
 
      : iterator_buffer(get_container(out)) {}
 
  std::back_insert_iterator<Container> out() {
 
    return std::back_inserter(container_);
 
  }
 
};
 

	
 
// A buffer that counts the number of code units written discarding the output.
 
template <typename T = char> class counting_buffer final : public buffer<T> {
 
 private:
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 
  size_t count_ = 0;
 

	
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {
 
    if (this->size() != buffer_size) return;
 
    count_ += this->size();
 
    this->clear();
 
  }
 

	
 
 public:
 
  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
 

	
 
  size_t count() { return count_ + this->size(); }
 
};
 

	
 
// An output iterator that appends to the buffer.
 
// It is used to reduce symbol sizes for the common case.
 
template <typename T>
 
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
 
  using base = std::back_insert_iterator<buffer<T>>;
 

	
 
 public:
 
  explicit buffer_appender(buffer<T>& buf) : base(buf) {}
 
  buffer_appender(base it) : base(it) {}
 

	
 
  buffer_appender& operator++() {
 
    base::operator++();
 
    return *this;
 
  }
 

	
 
  buffer_appender operator++(int) {
 
    buffer_appender tmp = *this;
 
    ++*this;
 
    return tmp;
 
  }
 
};
 

	
 
// Maps an output iterator into a buffer.
 
template <typename T, typename OutputIt>
 
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
 
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
 

	
 
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
 
  return out;
 
}
 
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
 
  return get_container(out);
 
}
 

	
 
template <typename Buffer>
 
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
 
  return buf.out();
 
}
 
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
 
  return buffer_appender<T>(buf);
 
}
 

	
 
template <typename T, typename Char = char, typename Enable = void>
 
struct fallback_formatter {
 
  fallback_formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled fallback_formatter specialization.
 
template <typename T, typename Context>
 
using has_fallback_formatter =
 
    std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
 

	
 
struct view {};
 

	
 
template <typename Char, typename T> struct named_arg : view {
 
  const Char* name;
 
  const T& value;
 
  named_arg(const Char* n, const T& v) : name(n), value(v) {}
 
};
 

	
 
template <typename Char> struct named_arg_info {
 
  const Char* name;
 
  int id;
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
 
struct arg_data {
 
  // args_[0].named_args points to named_args_ to avoid bloating format_args.
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
 
  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
 

	
 
  template <typename... U>
 
  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
 
  arg_data(const arg_data& other) = delete;
 
  const T* args() const { return args_ + 1; }
 
  named_arg_info<Char>* named_args() { return named_args_; }
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS>
 
struct arg_data<T, Char, NUM_ARGS, 0> {
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
 

	
 
  template <typename... U>
 
  FMT_INLINE arg_data(const U&... init) : args_{init...} {}
 
  FMT_INLINE const T* args() const { return args_; }
 
  FMT_INLINE std::nullptr_t named_args() { return nullptr; }
 
};
 

	
 
template <typename Char>
 
inline void init_named_args(named_arg_info<Char>*, int, int) {}
 

	
 
template <typename Char, typename T, typename... Tail>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T&, const Tail&... args) {
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename Char, typename T, typename... Tail>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const named_arg<Char, T>& arg,
 
                     const Tail&... args) {
 
  named_args[named_arg_count++] = {arg.name, arg_count};
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename... Args>
 
FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
 

	
 
template <typename T> struct is_named_arg : std::false_type {};
 

	
 
template <typename T, typename Char>
 
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
 

	
 
template <bool B = false> constexpr size_t count() { return B ? 1 : 0; }
 
template <bool B1, bool B2, bool... Tail> constexpr size_t count() {
 
  return (B1 ? 1 : 0) + count<B2, Tail...>();
 
}
 

	
 
template <typename... Args> constexpr size_t count_named_args() {
 
  return count<is_named_arg<Args>::value...>();
 
}
 

	
 
enum class type {
 
  none_type,
 
  // Integer types should go first,
 
@@ -1009,8 +589,8 @@ FMT_TYPE_CONSTANT(int, int_type);
 
FMT_TYPE_CONSTANT(unsigned, uint_type);
 
FMT_TYPE_CONSTANT(long long, long_long_type);
 
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
 
FMT_TYPE_CONSTANT(int128_t, int128_type);
 
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
 
FMT_TYPE_CONSTANT(int128_opt, int128_type);
 
FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
 
FMT_TYPE_CONSTANT(bool, bool_type);
 
FMT_TYPE_CONSTANT(Char, char_type);
 
FMT_TYPE_CONSTANT(float, float_type);
 
@@ -1023,11 +603,613 @@ FMT_TYPE_CONSTANT(const void*, pointer_t
 
constexpr bool is_integral_type(type t) {
 
  return t > type::none_type && t <= type::last_integer_type;
 
}
 

	
 
constexpr bool is_arithmetic_type(type t) {
 
  return t > type::none_type && t <= type::last_numeric_type;
 
}
 

	
 
constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
 
constexpr auto in(type t, int set) -> bool {
 
  return ((set >> static_cast<int>(t)) & 1) != 0;
 
}
 

	
 
// Bitsets of types.
 
enum {
 
  sint_set =
 
      set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
 
  uint_set = set(type::uint_type) | set(type::ulong_long_type) |
 
             set(type::uint128_type),
 
  bool_set = set(type::bool_type),
 
  char_set = set(type::char_type),
 
  float_set = set(type::float_type) | set(type::double_type) |
 
              set(type::long_double_type),
 
  string_set = set(type::string_type),
 
  cstring_set = set(type::cstring_type),
 
  pointer_set = set(type::pointer_type)
 
};
 

	
 
FMT_NORETURN FMT_API void throw_format_error(const char* message);
 

	
 
struct error_handler {
 
  constexpr error_handler() = default;
 

	
 
  // This function is intentionally not constexpr to give a compile-time error.
 
  FMT_NORETURN void on_error(const char* message) {
 
    throw_format_error(message);
 
  }
 
};
 
}  // namespace detail
 

	
 
/** String's character type. */
 
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
 

	
 
/**
 
  \rst
 
  Parsing context consisting of a format string range being parsed and an
 
  argument counter for automatic indexing.
 
  You can use the ``format_parse_context`` type alias for ``char`` instead.
 
  \endrst
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Char> class basic_format_parse_context {
 
 private:
 
  basic_string_view<Char> format_str_;
 
  int next_arg_id_;
 

	
 
  FMT_CONSTEXPR void do_check_arg_id(int id);
 

	
 
 public:
 
  using char_type = Char;
 
  using iterator = const Char*;
 

	
 
  explicit constexpr basic_format_parse_context(
 
      basic_string_view<Char> format_str, int next_arg_id = 0)
 
      : format_str_(format_str), next_arg_id_(next_arg_id) {}
 

	
 
  /**
 
    Returns an iterator to the beginning of the format string range being
 
    parsed.
 
   */
 
  constexpr auto begin() const noexcept -> iterator {
 
    return format_str_.begin();
 
  }
 

	
 
  /**
 
    Returns an iterator past the end of the format string range being parsed.
 
   */
 
  constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
 

	
 
  /** Advances the begin iterator to ``it``. */
 
  FMT_CONSTEXPR void advance_to(iterator it) {
 
    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
 
  }
 

	
 
  /**
 
    Reports an error if using the manual argument indexing; otherwise returns
 
    the next argument index and switches to the automatic indexing.
 
   */
 
  FMT_CONSTEXPR auto next_arg_id() -> int {
 
    if (next_arg_id_ < 0) {
 
      detail::throw_format_error(
 
          "cannot switch from manual to automatic argument indexing");
 
      return 0;
 
    }
 
    int id = next_arg_id_++;
 
    do_check_arg_id(id);
 
    return id;
 
  }
 

	
 
  /**
 
    Reports an error if using the automatic argument indexing; otherwise
 
    switches to the manual indexing.
 
   */
 
  FMT_CONSTEXPR void check_arg_id(int id) {
 
    if (next_arg_id_ > 0) {
 
      detail::throw_format_error(
 
          "cannot switch from automatic to manual argument indexing");
 
      return;
 
    }
 
    next_arg_id_ = -1;
 
    do_check_arg_id(id);
 
  }
 
  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
 
  FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
 
};
 

	
 
FMT_MODULE_EXPORT
 
using format_parse_context = basic_format_parse_context<char>;
 

	
 
namespace detail {
 
// A parse context with extra data used only in compile-time checks.
 
template <typename Char>
 
class compile_parse_context : public basic_format_parse_context<Char> {
 
 private:
 
  int num_args_;
 
  const type* types_;
 
  using base = basic_format_parse_context<Char>;
 

	
 
 public:
 
  explicit FMT_CONSTEXPR compile_parse_context(
 
      basic_string_view<Char> format_str, int num_args, const type* types,
 
      int next_arg_id = 0)
 
      : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
 

	
 
  constexpr auto num_args() const -> int { return num_args_; }
 
  constexpr auto arg_type(int id) const -> type { return types_[id]; }
 

	
 
  FMT_CONSTEXPR auto next_arg_id() -> int {
 
    int id = base::next_arg_id();
 
    if (id >= num_args_) throw_format_error("argument not found");
 
    return id;
 
  }
 

	
 
  FMT_CONSTEXPR void check_arg_id(int id) {
 
    base::check_arg_id(id);
 
    if (id >= num_args_) throw_format_error("argument not found");
 
  }
 
  using base::check_arg_id;
 

	
 
  FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
 
    detail::ignore_unused(arg_id);
 
#if !defined(__LCC__)
 
    if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
 
      throw_format_error("width/precision is not integer");
 
#endif
 
  }
 
};
 
}  // namespace detail
 

	
 
template <typename Char>
 
FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
 
  // Argument id is only checked at compile-time during parsing because
 
  // formatting has its own validation.
 
  if (detail::is_constant_evaluated() &&
 
      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
 
    using context = detail::compile_parse_context<Char>;
 
    if (id >= static_cast<context*>(this)->num_args())
 
      detail::throw_format_error("argument not found");
 
  }
 
}
 

	
 
template <typename Char>
 
FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
 
    int arg_id) {
 
  if (detail::is_constant_evaluated() &&
 
      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
 
    using context = detail::compile_parse_context<Char>;
 
    static_cast<context*>(this)->check_dynamic_spec(arg_id);
 
  }
 
}
 

	
 
FMT_MODULE_EXPORT template <typename Context> class basic_format_arg;
 
FMT_MODULE_EXPORT template <typename Context> class basic_format_args;
 
FMT_MODULE_EXPORT template <typename Context> class dynamic_format_arg_store;
 

	
 
// A formatter for objects of type T.
 
FMT_MODULE_EXPORT
 
template <typename T, typename Char = char, typename Enable = void>
 
struct formatter {
 
  // A deleted default constructor indicates a disabled formatter.
 
  formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled formatter specialization. A type can be
 
// formattable even if it doesn't have a formatter e.g. via a conversion.
 
template <typename T, typename Context>
 
using has_formatter =
 
    std::is_constructible<typename Context::template formatter_type<T>>;
 

	
 
// Checks whether T is a container with contiguous storage.
 
template <typename T> struct is_contiguous : std::false_type {};
 
template <typename Char>
 
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
 

	
 
class appender;
 

	
 
namespace detail {
 

	
 
template <typename Context, typename T>
 
constexpr auto has_const_formatter_impl(T*)
 
    -> decltype(typename Context::template formatter_type<T>().format(
 
                    std::declval<const T&>(), std::declval<Context&>()),
 
                true) {
 
  return true;
 
}
 
template <typename Context>
 
constexpr auto has_const_formatter_impl(...) -> bool {
 
  return false;
 
}
 
template <typename T, typename Context>
 
constexpr auto has_const_formatter() -> bool {
 
  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
 
}
 

	
 
// Extracts a reference to the container from back_insert_iterator.
 
template <typename Container>
 
inline auto get_container(std::back_insert_iterator<Container> it)
 
    -> Container& {
 
  using base = std::back_insert_iterator<Container>;
 
  struct accessor : base {
 
    accessor(base b) : base(b) {}
 
    using base::container;
 
  };
 
  return *accessor(it).container;
 
}
 

	
 
template <typename Char, typename InputIt, typename OutputIt>
 
FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
 
    -> OutputIt {
 
  while (begin != end) *out++ = static_cast<Char>(*begin++);
 
  return out;
 
}
 

	
 
template <typename Char, typename T, typename U,
 
          FMT_ENABLE_IF(
 
              std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
 
FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
 
  if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
 
  auto size = to_unsigned(end - begin);
 
  if (size > 0) memcpy(out, begin, size * sizeof(U));
 
  return out + size;
 
}
 

	
 
/**
 
  \rst
 
  A contiguous memory buffer with an optional growing ability. It is an internal
 
  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
 
  \endrst
 
 */
 
template <typename T> class buffer {
 
 private:
 
  T* ptr_;
 
  size_t size_;
 
  size_t capacity_;
 

	
 
 protected:
 
  // Don't initialize ptr_ since it is not accessed to save a few cycles.
 
  FMT_MSC_WARNING(suppress : 26495)
 
  buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
 

	
 
  FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
 
      : ptr_(p), size_(sz), capacity_(cap) {}
 

	
 
  FMT_CONSTEXPR20 ~buffer() = default;
 
  buffer(buffer&&) = default;
 

	
 
  /** Sets the buffer data and capacity. */
 
  FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
 
    ptr_ = buf_data;
 
    capacity_ = buf_capacity;
 
  }
 

	
 
  /** Increases the buffer capacity to hold at least *capacity* elements. */
 
  virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  buffer(const buffer&) = delete;
 
  void operator=(const buffer&) = delete;
 

	
 
  FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
 
  FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
 

	
 
  FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
 
  FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
 

	
 
  /** Returns the size of this buffer. */
 
  constexpr auto size() const noexcept -> size_t { return size_; }
 

	
 
  /** Returns the capacity of this buffer. */
 
  constexpr auto capacity() const noexcept -> size_t { return capacity_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
 

	
 
  /** Clears this buffer. */
 
  void clear() { size_ = 0; }
 

	
 
  // Tries resizing the buffer to contain *count* elements. If T is a POD type
 
  // the new elements may not be initialized.
 
  FMT_CONSTEXPR20 void try_resize(size_t count) {
 
    try_reserve(count);
 
    size_ = count <= capacity_ ? count : capacity_;
 
  }
 

	
 
  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
 
  // capacity by a smaller amount than requested but guarantees there is space
 
  // for at least one additional element either by increasing the capacity or by
 
  // flushing the buffer if it is full.
 
  FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
 
    if (new_capacity > capacity_) grow(new_capacity);
 
  }
 

	
 
  FMT_CONSTEXPR20 void push_back(const T& value) {
 
    try_reserve(size_ + 1);
 
    ptr_[size_++] = value;
 
  }
 

	
 
  /** Appends data to the end of the buffer. */
 
  template <typename U> void append(const U* begin, const U* end);
 

	
 
  template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
 
    return ptr_[index];
 
  }
 
  template <typename Idx>
 
  FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
 
    return ptr_[index];
 
  }
 
};
 

	
 
struct buffer_traits {
 
  explicit buffer_traits(size_t) {}
 
  auto count() const -> size_t { return 0; }
 
  auto limit(size_t size) -> size_t { return size; }
 
};
 

	
 
class fixed_buffer_traits {
 
 private:
 
  size_t count_ = 0;
 
  size_t limit_;
 

	
 
 public:
 
  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
 
  auto count() const -> size_t { return count_; }
 
  auto limit(size_t size) -> size_t {
 
    size_t n = limit_ > count_ ? limit_ - count_ : 0;
 
    count_ += size;
 
    return size < n ? size : n;
 
  }
 
};
 

	
 
// A buffer that writes to an output iterator when flushed.
 
template <typename OutputIt, typename T, typename Traits = buffer_traits>
 
class iterator_buffer final : public Traits, public buffer<T> {
 
 private:
 
  OutputIt out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() == buffer_size) flush();
 
  }
 

	
 
  void flush() {
 
    auto size = this->size();
 
    this->clear();
 
    out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
 
      : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
 
  iterator_buffer(iterator_buffer&& other)
 
      : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
 
  ~iterator_buffer() { flush(); }
 

	
 
  auto out() -> OutputIt {
 
    flush();
 
    return out_;
 
  }
 
  auto count() const -> size_t { return Traits::count() + this->size(); }
 
};
 

	
 
template <typename T>
 
class iterator_buffer<T*, T, fixed_buffer_traits> final
 
    : public fixed_buffer_traits,
 
      public buffer<T> {
 
 private:
 
  T* out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() == this->capacity()) flush();
 
  }
 

	
 
  void flush() {
 
    size_t n = this->limit(this->size());
 
    if (this->data() == out_) {
 
      out_ += n;
 
      this->set(data_, buffer_size);
 
    }
 
    this->clear();
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t n = buffer_size)
 
      : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
 
  iterator_buffer(iterator_buffer&& other)
 
      : fixed_buffer_traits(other),
 
        buffer<T>(std::move(other)),
 
        out_(other.out_) {
 
    if (this->data() != out_) {
 
      this->set(data_, buffer_size);
 
      this->clear();
 
    }
 
  }
 
  ~iterator_buffer() { flush(); }
 

	
 
  auto out() -> T* {
 
    flush();
 
    return out_;
 
  }
 
  auto count() const -> size_t {
 
    return fixed_buffer_traits::count() + this->size();
 
  }
 
};
 

	
 
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {}
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
 

	
 
  auto out() -> T* { return &*this->end(); }
 
};
 

	
 
// A buffer that writes to a container with the contiguous storage.
 
template <typename Container>
 
class iterator_buffer<std::back_insert_iterator<Container>,
 
                      enable_if_t<is_contiguous<Container>::value,
 
                                  typename Container::value_type>>
 
    final : public buffer<typename Container::value_type> {
 
 private:
 
  Container& container_;
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t capacity) override {
 
    container_.resize(capacity);
 
    this->set(&container_[0], capacity);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(Container& c)
 
      : buffer<typename Container::value_type>(c.size()), container_(c) {}
 
  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
 
      : iterator_buffer(get_container(out)) {}
 

	
 
  auto out() -> std::back_insert_iterator<Container> {
 
    return std::back_inserter(container_);
 
  }
 
};
 

	
 
// A buffer that counts the number of code units written discarding the output.
 
template <typename T = char> class counting_buffer final : public buffer<T> {
 
 private:
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 
  size_t count_ = 0;
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() != buffer_size) return;
 
    count_ += this->size();
 
    this->clear();
 
  }
 

	
 
 public:
 
  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
 

	
 
  auto count() -> size_t { return count_ + this->size(); }
 
};
 

	
 
template <typename T>
 
using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
 
                                      std::back_insert_iterator<buffer<T>>>;
 

	
 
// Maps an output iterator to a buffer.
 
template <typename T, typename OutputIt>
 
auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
 
  return iterator_buffer<OutputIt, T>(out);
 
}
 
template <typename T, typename Buf,
 
          FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
 
auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
 
  return get_container(out);
 
}
 

	
 
template <typename Buf, typename OutputIt>
 
FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
 
  return buf.out();
 
}
 
template <typename T, typename OutputIt>
 
auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
 
  return out;
 
}
 

	
 
struct view {};
 

	
 
template <typename Char, typename T> struct named_arg : view {
 
  const Char* name;
 
  const T& value;
 
  named_arg(const Char* n, const T& v) : name(n), value(v) {}
 
};
 

	
 
template <typename Char> struct named_arg_info {
 
  const Char* name;
 
  int id;
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
 
struct arg_data {
 
  // args_[0].named_args points to named_args_ to avoid bloating format_args.
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
 
  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
 

	
 
  template <typename... U>
 
  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
 
  arg_data(const arg_data& other) = delete;
 
  auto args() const -> const T* { return args_ + 1; }
 
  auto named_args() -> named_arg_info<Char>* { return named_args_; }
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS>
 
struct arg_data<T, Char, NUM_ARGS, 0> {
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
 

	
 
  template <typename... U>
 
  FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
 
  FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
 
  FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
 
    return nullptr;
 
  }
 
};
 

	
 
template <typename Char>
 
inline void init_named_args(named_arg_info<Char>*, int, int) {}
 

	
 
template <typename T> struct is_named_arg : std::false_type {};
 
template <typename T> struct is_statically_named_arg : std::false_type {};
 

	
 
template <typename T, typename Char>
 
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
 

	
 
template <typename Char, typename T, typename... Tail,
 
          FMT_ENABLE_IF(!is_named_arg<T>::value)>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T&, const Tail&... args) {
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename Char, typename T, typename... Tail,
 
          FMT_ENABLE_IF(is_named_arg<T>::value)>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T& arg, const Tail&... args) {
 
  named_args[named_arg_count++] = {arg.name, arg_count};
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename... Args>
 
FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
 
                                              const Args&...) {}
 

	
 
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
 
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
 
  return (B1 ? 1 : 0) + count<B2, Tail...>();
 
}
 

	
 
template <typename... Args> constexpr auto count_named_args() -> size_t {
 
  return count<is_named_arg<Args>::value...>();
 
}
 

	
 
template <typename... Args>
 
constexpr auto count_statically_named_args() -> size_t {
 
  return count<is_statically_named_arg<Args>::value...>();
 
}
 

	
 
struct unformattable {};
 
struct unformattable_char : unformattable {};
 
struct unformattable_pointer : unformattable {};
 

	
 
template <typename Char> struct string_value {
 
  const Char* data;
 
  size_t size;
 
@@ -1040,8 +1222,8 @@ template <typename Char> struct named_ar
 

	
 
template <typename Context> struct custom_value {
 
  using parse_context = typename Context::parse_context_type;
 
  const void* value;
 
  void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
 
  void* value;
 
  void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
 
};
 

	
 
// A formatting argument value.
 
@@ -1050,12 +1232,13 @@ template <typename Context> class value 
 
  using char_type = typename Context::char_type;
 

	
 
  union {
 
    monostate no_value;
 
    int int_value;
 
    unsigned uint_value;
 
    long long long_long_value;
 
    unsigned long long ulong_long_value;
 
    int128_t int128_value;
 
    uint128_t uint128_value;
 
    int128_opt int128_value;
 
    uint128_opt uint128_value;
 
    bool bool_value;
 
    char_type char_value;
 
    float float_value;
 
@@ -1067,19 +1250,23 @@ template <typename Context> class value 
 
    named_arg_value<char_type> named_args;
 
  };
 

	
 
  constexpr FMT_INLINE value(int val = 0) : int_value(val) {}
 
  constexpr FMT_INLINE value() : no_value() {}
 
  constexpr FMT_INLINE value(int val) : int_value(val) {}
 
  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
 
  FMT_INLINE value(long long val) : long_long_value(val) {}
 
  FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
 
  FMT_INLINE value(int128_t val) : int128_value(val) {}
 
  FMT_INLINE value(uint128_t val) : uint128_value(val) {}
 
  FMT_INLINE value(float val) : float_value(val) {}
 
  FMT_INLINE value(double val) : double_value(val) {}
 
  constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
 
  constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
 
  FMT_INLINE value(int128_opt val) : int128_value(val) {}
 
  FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
 
  constexpr FMT_INLINE value(float val) : float_value(val) {}
 
  constexpr FMT_INLINE value(double val) : double_value(val) {}
 
  FMT_INLINE value(long double val) : long_double_value(val) {}
 
  FMT_INLINE value(bool val) : bool_value(val) {}
 
  FMT_INLINE value(char_type val) : char_value(val) {}
 
  FMT_INLINE value(const char_type* val) { string.data = val; }
 
  FMT_INLINE value(basic_string_view<char_type> val) {
 
  constexpr FMT_INLINE value(bool val) : bool_value(val) {}
 
  constexpr FMT_INLINE value(char_type val) : char_value(val) {}
 
  FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
 
    string.data = val;
 
    if (is_constant_evaluated()) string.size = {};
 
  }
 
  FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
 
    string.data = val.data();
 
    string.size = val.size();
 
  }
 
@@ -1087,31 +1274,35 @@ template <typename Context> class value 
 
  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
 
      : named_args{args, size} {}
 

	
 
  template <typename T> FMT_INLINE value(const T& val) {
 
    custom.value = &val;
 
  template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
 
    using value_type = remove_cvref_t<T>;
 
    custom.value = const_cast<value_type*>(&val);
 
    // Get the formatter type through the context to allow different contexts
 
    // have different extension points, e.g. `formatter<T>` for `format` and
 
    // `printf_formatter<T>` for `printf`.
 
    custom.format = format_custom_arg<
 
        T, conditional_t<has_formatter<T, Context>::value,
 
                         typename Context::template formatter_type<T>,
 
                         fallback_formatter<T, char_type>>>;
 
        value_type, typename Context::template formatter_type<value_type>>;
 
  }
 
  value(unformattable);
 
  value(unformattable_char);
 
  value(unformattable_pointer);
 

	
 
 private:
 
  // Formats an argument of a custom type, such as a user-defined class.
 
  template <typename T, typename Formatter>
 
  static void format_custom_arg(const void* arg,
 
  static void format_custom_arg(void* arg,
 
                                typename Context::parse_context_type& parse_ctx,
 
                                Context& ctx) {
 
    Formatter f;
 
    auto f = Formatter();
 
    parse_ctx.advance_to(f.parse(parse_ctx));
 
    ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
 
    using qualified_type =
 
        conditional_t<has_const_formatter<T, Context>(), const T, T>;
 
    ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
 
  }
 
};
 

	
 
template <typename Context, typename T>
 
FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value);
 
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
 

	
 
// To minimize the number of types we need to deal with, long is translated
 
// either to int or to long long depending on its size.
 
@@ -1119,117 +1310,164 @@ enum { long_short = sizeof(long) == size
 
using long_type = conditional_t<long_short, int, long long>;
 
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
 

	
 
struct unformattable {};
 
template <typename T> struct format_as_result {
 
  template <typename U,
 
            FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
 
  static auto map(U*) -> decltype(format_as(std::declval<U>()));
 
  static auto map(...) -> void;
 

	
 
  using type = decltype(map(static_cast<T*>(nullptr)));
 
};
 
template <typename T> using format_as_t = typename format_as_result<T>::type;
 

	
 
template <typename T>
 
struct has_format_as
 
    : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
 

	
 
// Maps formatting arguments to core types.
 
// arg_mapper reports errors by returning unformattable instead of using
 
// static_assert because it's used in the is_formattable trait.
 
template <typename Context> struct arg_mapper {
 
  using char_type = typename Context::char_type;
 

	
 
  FMT_CONSTEXPR int map(signed char val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned char val) { return val; }
 
  FMT_CONSTEXPR int map(short val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned short val) { return val; }
 
  FMT_CONSTEXPR int map(int val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned val) { return val; }
 
  FMT_CONSTEXPR long_type map(long val) { return val; }
 
  FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
 
  FMT_CONSTEXPR long long map(long long val) { return val; }
 
  FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
 
  FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
 
  FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
 
  FMT_CONSTEXPR bool map(bool val) { return val; }
 

	
 
  template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
 
  FMT_CONSTEXPR char_type map(T val) {
 
    static_assert(
 
        std::is_same<T, char>::value || std::is_same<T, char_type>::value,
 
        "mixing character types is disallowed");
 
  FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
 
      -> unsigned long long {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
 
    return val;
 
  }
 

	
 
  FMT_CONSTEXPR float map(float val) { return val; }
 
  FMT_CONSTEXPR double map(double val) { return val; }
 
  FMT_CONSTEXPR long double map(long double val) { return val; }
 

	
 
  FMT_CONSTEXPR const char_type* map(char_type* val) { return val; }
 
  FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; }
 
  template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    static_assert(std::is_same<char_type, char_t<T>>::value,
 
                  "mixing character types is disallowed");
 
  FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
 

	
 
  template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
 
                                      std::is_same<T, char_type>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
 
    return val;
 
  }
 
  template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
 
#ifdef __cpp_char8_t
 
                                     std::is_same<T, char8_t>::value ||
 
#endif
 
                                     std::is_same<T, char16_t>::value ||
 
                                     std::is_same<T, char32_t>::value) &&
 
                                        !std::is_same<T, char_type>::value,
 
                                    int> = 0>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
 
    return {};
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
 
    return val;
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
 
    return val;
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
 
                          std::is_same<char_type, char_t<T>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
 
      -> basic_string_view<char_type> {
 
    return to_string_view(val);
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(
 
                std::is_constructible<basic_string_view<char_type>, T>::value &&
 
                !is_string<T>::value && !has_formatter<T, Context>::value &&
 
                !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    return basic_string_view<char_type>(val);
 
            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
 
                          !std::is_same<char_type, char_t<T>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
 
    return {};
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
 
    return val;
 
  }
 

	
 
  // Use SFINAE instead of a const T* parameter to avoid a conflict with the
 
  // array overload.
 
  template <
 
      typename T,
 
      FMT_ENABLE_IF(
 
          std::is_constructible<std_string_view<char_type>, T>::value &&
 
          !std::is_constructible<basic_string_view<char_type>, T>::value &&
 
          !is_string<T>::value && !has_formatter<T, Context>::value &&
 
          !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    return std_string_view<char_type>(val);
 
  }
 
  FMT_CONSTEXPR const char* map(const signed char* val) {
 
    static_assert(std::is_same<char_type, char>::value, "invalid string type");
 
    return reinterpret_cast<const char*>(val);
 
          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
 
          std::is_function<typename std::remove_pointer<T>::type>::value ||
 
          (std::is_convertible<const T&, const void*>::value &&
 
           !std::is_convertible<const T&, const char_type*>::value &&
 
           !has_formatter<T, Context>::value))>
 
  FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
 
    return {};
 
  }
 
  FMT_CONSTEXPR const char* map(const unsigned char* val) {
 
    static_assert(std::is_same<char_type, char>::value, "invalid string type");
 
    return reinterpret_cast<const char*>(val);
 
  }
 
  FMT_CONSTEXPR const char* map(signed char* val) {
 
    const auto* const_val = val;
 
    return map(const_val);
 
  }
 
  FMT_CONSTEXPR const char* map(unsigned char* val) {
 
    const auto* const_val = val;
 
    return map(const_val);
 

	
 
  template <typename T, std::size_t N,
 
            FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
 
    return values;
 
  }
 

	
 
  FMT_CONSTEXPR const void* map(void* val) { return val; }
 
  FMT_CONSTEXPR const void* map(const void* val) { return val; }
 
  FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; }
 
  template <typename T> FMT_CONSTEXPR int map(const T*) {
 
    // Formatting of arbitrary pointers is disallowed. If you want to output
 
    // a pointer cast it to "void *" or "const void *". In particular, this
 
    // forbids formatting of "[const] volatile char *" which is printed as bool
 
    // by iostreams.
 
    static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
 
    return 0;
 
  // Only map owning types because mapping views can be unsafe.
 
  template <typename T, typename U = format_as_t<T>,
 
            FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
 
    return map(format_as(val));
 
  }
 

	
 
  template <typename T,
 
            FMT_ENABLE_IF(std::is_enum<T>::value &&
 
                          !has_formatter<T, Context>::value &&
 
                          !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR auto map(const T& val)
 
      -> decltype(std::declval<arg_mapper>().map(
 
          static_cast<typename std::underlying_type<T>::type>(val))) {
 
    return map(static_cast<typename std::underlying_type<T>::type>(val));
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
 
                          (has_formatter<T, Context>::value ||
 
                           has_fallback_formatter<T, Context>::value))>
 
  FMT_CONSTEXPR const T& map(const T& val) {
 
  template <typename T, typename U = remove_cvref_t<T>>
 
  struct formattable
 
      : bool_constant<has_const_formatter<U, Context>() ||
 
                      (has_formatter<U, Context>::value &&
 
                       !std::is_const<remove_reference_t<T>>::value)> {};
 

	
 
  template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
 
    return val;
 
  }
 

	
 
  template <typename T>
 
  FMT_CONSTEXPR auto map(const named_arg<char_type, T>& val)
 
      -> decltype(std::declval<arg_mapper>().map(val.value)) {
 
    return map(val.value);
 
  template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable {
 
    return {};
 
  }
 

	
 
  unformattable map(...) { return {}; }
 
  template <typename T, typename U = remove_cvref_t<T>,
 
            FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
 
                           std::is_union<U>::value) &&
 
                          !is_string<U>::value && !is_char<U>::value &&
 
                          !is_named_arg<U>::value &&
 
                          !std::is_arithmetic<format_as_t<U>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
 
      -> decltype(this->do_map(std::forward<T>(val))) {
 
    return do_map(std::forward<T>(val));
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
 
      -> decltype(this->map(named_arg.value)) {
 
    return map(named_arg.value);
 
  }
 

	
 
  auto map(...) -> unformattable { return {}; }
 
};
 

	
 
// A type constant after applying arg_mapper<Context>.
 
@@ -1245,6 +1483,20 @@ enum : unsigned long long { is_unpacked_
 
enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
 
}  // namespace detail
 

	
 
// An output iterator that appends to a buffer.
 
// It is used to reduce symbol sizes for the common case.
 
class appender : public std::back_insert_iterator<detail::buffer<char>> {
 
  using base = std::back_insert_iterator<detail::buffer<char>>;
 

	
 
 public:
 
  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
 
  appender(base it) noexcept : base(it) {}
 
  FMT_UNCHECKED_ITERATOR(appender);
 

	
 
  auto operator++() noexcept -> appender& { return *this; }
 
  auto operator++(int) noexcept -> appender { return *this; }
 
};
 

	
 
// A formatting argument. It is a trivially copyable/constructible type to
 
// allow storage in basic_memory_buffer.
 
template <typename Context> class basic_format_arg {
 
@@ -1253,8 +1505,8 @@ template <typename Context> class basic_
 
  detail::type type_;
 

	
 
  template <typename ContextType, typename T>
 
  friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg(
 
      const T& value);
 
  friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
 
      -> basic_format_arg<ContextType>;
 

	
 
  template <typename Visitor, typename Ctx>
 
  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
 
@@ -1288,14 +1540,16 @@ template <typename Context> class basic_
 

	
 
  constexpr basic_format_arg() : type_(detail::type::none_type) {}
 

	
 
  constexpr explicit operator bool() const FMT_NOEXCEPT {
 
  constexpr explicit operator bool() const noexcept {
 
    return type_ != detail::type::none_type;
 
  }
 

	
 
  detail::type type() const { return type_; }
 

	
 
  bool is_integral() const { return detail::is_integral_type(type_); }
 
  bool is_arithmetic() const { return detail::is_arithmetic_type(type_); }
 
  auto type() const -> detail::type { return type_; }
 

	
 
  auto is_integral() const -> bool { return detail::is_integral_type(type_); }
 
  auto is_arithmetic() const -> bool {
 
    return detail::is_arithmetic_type(type_);
 
  }
 
};
 

	
 
/**
 
@@ -1305,10 +1559,10 @@ template <typename Context> class basic_
 
  ``vis(value)`` will be called with the value of type ``double``.
 
  \endrst
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Visitor, typename Context>
 
FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
 
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
 
    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
 
  using char_type = typename Context::char_type;
 
  switch (arg.type_) {
 
  case detail::type::none_type:
 
    break;
 
@@ -1320,16 +1574,10 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
    return vis(arg.value_.long_long_value);
 
  case detail::type::ulong_long_type:
 
    return vis(arg.value_.ulong_long_value);
 
#if FMT_USE_INT128
 
  case detail::type::int128_type:
 
    return vis(arg.value_.int128_value);
 
    return vis(detail::convert_for_visit(arg.value_.int128_value));
 
  case detail::type::uint128_type:
 
    return vis(arg.value_.uint128_value);
 
#else
 
  case detail::type::int128_type:
 
  case detail::type::uint128_type:
 
    break;
 
#endif
 
    return vis(detail::convert_for_visit(arg.value_.uint128_value));
 
  case detail::type::bool_type:
 
    return vis(arg.value_.bool_value);
 
  case detail::type::char_type:
 
@@ -1343,8 +1591,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
  case detail::type::cstring_type:
 
    return vis(arg.value_.string.data);
 
  case detail::type::string_type:
 
    return vis(basic_string_view<char_type>(arg.value_.string.data,
 
                                            arg.value_.string.size));
 
    using sv = basic_string_view<typename Context::char_type>;
 
    return vis(sv(arg.value_.string.data, arg.value_.string.size));
 
  case detail::type::pointer_type:
 
    return vis(arg.value_.pointer);
 
  case detail::type::custom_type:
 
@@ -1353,14 +1601,26 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
  return vis(monostate());
 
}
 

	
 
template <typename T> struct formattable : std::false_type {};
 

	
 
namespace detail {
 

	
 
template <typename Char, typename InputIt>
 
auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
 
  get_container(out).append(begin, end);
 
  return out;
 
}
 

	
 
template <typename Char, typename R, typename OutputIt>
 
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
 
  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
 
}
 

	
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
 
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
 
template <typename... Ts> struct void_t_impl { using type = void; };
 
template <typename... Ts>
 
using void_t = typename detail::void_t_impl<Ts...>::type;
 
template <typename...> struct void_t_impl { using type = void; };
 
template <typename... T> using void_t = typename void_t_impl<T...>::type;
 
#else
 
template <typename...> using void_t = void;
 
#endif
 

	
 
template <typename It, typename T, typename Enable = void>
 
struct is_output_iterator : std::false_type {};
 
@@ -1372,125 +1632,95 @@ struct is_output_iterator<
 
           decltype(*std::declval<It>() = std::declval<T>())>>
 
    : std::true_type {};
 

	
 
template <typename OutputIt>
 
struct is_back_insert_iterator : std::false_type {};
 
template <typename It> struct is_back_insert_iterator : std::false_type {};
 
template <typename Container>
 
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
 
    : std::true_type {};
 

	
 
template <typename OutputIt>
 
template <typename It>
 
struct is_contiguous_back_insert_iterator : std::false_type {};
 
template <typename Container>
 
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
 
    : is_contiguous<Container> {};
 
template <typename Char>
 
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
 
    : std::true_type {};
 

	
 
// A type-erased reference to an std::locale to avoid heavy <locale> include.
 
template <>
 
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
 

	
 
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
 
class locale_ref {
 
 private:
 
  const void* locale_;  // A type-erased pointer to std::locale.
 

	
 
 public:
 
  locale_ref() : locale_(nullptr) {}
 
  constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
 
  template <typename Locale> explicit locale_ref(const Locale& loc);
 

	
 
  explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
 

	
 
  template <typename Locale> Locale get() const;
 
  explicit operator bool() const noexcept { return locale_ != nullptr; }
 

	
 
  template <typename Locale> auto get() const -> Locale;
 
};
 

	
 
template <typename> constexpr unsigned long long encode_types() { return 0; }
 
template <typename> constexpr auto encode_types() -> unsigned long long {
 
  return 0;
 
}
 

	
 
template <typename Context, typename Arg, typename... Args>
 
constexpr unsigned long long encode_types() {
 
constexpr auto encode_types() -> unsigned long long {
 
  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
 
         (encode_types<Context, Args...>() << packed_arg_bits);
 
}
 

	
 
template <typename Context, typename T>
 
FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
 
  basic_format_arg<Context> arg;
 
  arg.type_ = mapped_type_constant<T, Context>::value;
 
  arg.value_ = arg_mapper<Context>().map(value);
 
  return arg;
 
}
 

	
 
template <typename T> int check(unformattable) {
 
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
 
  auto&& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
 
  using arg_type = remove_cvref_t<decltype(arg)>;
 

	
 
  constexpr bool formattable_char =
 
      !std::is_same<arg_type, unformattable_char>::value;
 
  static_assert(formattable_char, "Mixing character types is disallowed.");
 

	
 
  // Formatting of arbitrary pointers is disallowed. If you want to format a
 
  // pointer cast it to `void*` or `const void*`. In particular, this forbids
 
  // formatting of `[const] volatile char*` printed as bool by iostreams.
 
  constexpr bool formattable_pointer =
 
      !std::is_same<arg_type, unformattable_pointer>::value;
 
  static_assert(formattable_pointer,
 
                "Formatting of non-void pointers is disallowed.");
 

	
 
  constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
 
  static_assert(
 
      formattable<T>(),
 
      formattable,
 
      "Cannot format an argument. To make type T formattable provide a "
 
      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
 
  return 0;
 
}
 
template <typename T, typename U> inline const U& check(const U& val) {
 
  return val;
 
  return {arg};
 
}
 

	
 
// The type template parameter is there to avoid an ODR violation when using
 
// a fallback formatter in one translation unit and an implicit conversion in
 
// another (not recommended).
 
template <typename Context, typename T>
 
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
 
  auto arg = basic_format_arg<Context>();
 
  arg.type_ = mapped_type_constant<T, Context>::value;
 
  arg.value_ = make_value<Context>(value);
 
  return arg;
 
}
 

	
 
// The DEPRECATED type template parameter is there to avoid an ODR violation
 
// when using a fallback formatter in one translation unit and an implicit
 
// conversion in another (not recommended).
 
template <bool IS_PACKED, typename Context, type, typename T,
 
          FMT_ENABLE_IF(IS_PACKED)>
 
inline value<Context> make_arg(const T& val) {
 
  return check<T>(arg_mapper<Context>().map(val));
 
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
 
  return make_value<Context>(val);
 
}
 

	
 
template <bool IS_PACKED, typename Context, type, typename T,
 
          FMT_ENABLE_IF(!IS_PACKED)>
 
inline basic_format_arg<Context> make_arg(const T& value) {
 
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
 
  return make_arg<Context>(value);
 
}
 

	
 
template <typename T> struct is_reference_wrapper : std::false_type {};
 
template <typename T>
 
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
 

	
 
template <typename T> const T& unwrap(const T& v) { return v; }
 
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
 
  return static_cast<const T&>(v);
 
}
 

	
 
class dynamic_arg_list {
 
  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
 
  // templates it doesn't complain about inability to deduce single translation
 
  // unit for placing vtable. So storage_node_base is made a fake template.
 
  template <typename = void> struct node {
 
    virtual ~node() = default;
 
    std::unique_ptr<node<>> next;
 
  };
 

	
 
  template <typename T> struct typed_node : node<> {
 
    T value;
 

	
 
    template <typename Arg>
 
    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
 

	
 
    template <typename Char>
 
    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
 
        : value(arg.data(), arg.size()) {}
 
  };
 

	
 
  std::unique_ptr<node<>> head_;
 

	
 
 public:
 
  template <typename T, typename Arg> const T& push(const Arg& arg) {
 
    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
 
    auto& value = new_node->value;
 
    new_node->next = std::move(head_);
 
    head_ = std::move(new_node);
 
    return value;
 
  }
 
};
 
}  // namespace detail
 
FMT_BEGIN_EXPORT
 

	
 
// Formatting context.
 
template <typename OutputIt, typename Char> class basic_format_context {
 
 public:
 
  /** The character type for the output. */
 
  using char_type = Char;
 

	
 
 private:
 
  OutputIt out_;
 
  basic_format_args<basic_format_context> args_;
 
@@ -1499,48 +1729,56 @@ template <typename OutputIt, typename Ch
 
 public:
 
  using iterator = OutputIt;
 
  using format_arg = basic_format_arg<basic_format_context>;
 
  using format_args = basic_format_args<basic_format_context>;
 
  using parse_context_type = basic_format_parse_context<Char>;
 
  template <typename T> using formatter_type = formatter<T, char_type>;
 

	
 
  template <typename T> using formatter_type = formatter<T, Char>;
 

	
 
  /** The character type for the output. */
 
  using char_type = Char;
 

	
 
  basic_format_context(basic_format_context&&) = default;
 
  basic_format_context(const basic_format_context&) = delete;
 
  void operator=(const basic_format_context&) = delete;
 
  /**
 
   Constructs a ``basic_format_context`` object. References to the arguments are
 
   stored in the object so make sure they have appropriate lifetimes.
 
    Constructs a ``basic_format_context`` object. References to the arguments
 
    are stored in the object so make sure they have appropriate lifetimes.
 
   */
 
  basic_format_context(OutputIt out,
 
                       basic_format_args<basic_format_context> ctx_args,
 
                       detail::locale_ref loc = detail::locale_ref())
 
  constexpr basic_format_context(OutputIt out, format_args ctx_args,
 
                                 detail::locale_ref loc = {})
 
      : out_(out), args_(ctx_args), loc_(loc) {}
 

	
 
  format_arg arg(int id) const { return args_.get(id); }
 
  format_arg arg(basic_string_view<char_type> name) { return args_.get(name); }
 
  int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
 
  const basic_format_args<basic_format_context>& args() const { return args_; }
 

	
 
  detail::error_handler error_handler() { return {}; }
 
  constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
 
  FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
 
    return args_.get(name);
 
  }
 
  FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
 
    return args_.get_id(name);
 
  }
 
  auto args() const -> const format_args& { return args_; }
 

	
 
  FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
 
  void on_error(const char* message) { error_handler().on_error(message); }
 

	
 
  // Returns an iterator to the beginning of the output range.
 
  iterator out() { return out_; }
 
  FMT_CONSTEXPR auto out() -> iterator { return out_; }
 

	
 
  // Advances the begin iterator to ``it``.
 
  void advance_to(iterator it) {
 
    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
 
  }
 

	
 
  detail::locale_ref locale() { return loc_; }
 
  FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
 
};
 

	
 
template <typename Char>
 
using buffer_context =
 
    basic_format_context<detail::buffer_appender<Char>, Char>;
 
using format_context = buffer_context<char>;
 
using wformat_context = buffer_context<wchar_t>;
 

	
 
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
 
#define FMT_BUFFER_CONTEXT(Char) \
 
  basic_format_context<detail::buffer_appender<Char>, Char>
 

	
 
template <typename T, typename Char = char>
 
using is_formattable = bool_constant<!std::is_base_of<
 
    detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
 
                                        .map(std::declval<T>()))>::value>;
 

	
 
/**
 
  \rst
 
@@ -1578,14 +1816,16 @@ class format_arg_store
 
           : 0);
 

	
 
 public:
 
  format_arg_store(const Args&... args)
 
  template <typename... T>
 
  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
 
      :
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
        basic_format_args<Context>(*this),
 
#endif
 
        data_{detail::make_arg<
 
            is_packed, Context,
 
            detail::mapped_type_constant<Args, Context>::value>(args)...} {
 
            detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
 
            FMT_FORWARD(args))...} {
 
    detail::init_named_args(data_.named_args(), 0, 0, args...);
 
  }
 
};
 
@@ -1598,37 +1838,17 @@ class format_arg_store
 
  See `~fmt::arg` for lifetime considerations.
 
  \endrst
 
 */
 
template <typename Context = format_context, typename... Args>
 
inline format_arg_store<Context, Args...> make_format_args(
 
    const Args&... args) {
 
  return {args...};
 
template <typename Context = format_context, typename... T>
 
constexpr auto make_format_args(T&&... args)
 
    -> format_arg_store<Context, remove_cvref_t<T>...> {
 
  return {FMT_FORWARD(args)...};
 
}
 

	
 
/**
 
  \rst
 
  Constructs a `~fmt::format_arg_store` object that contains references
 
  to arguments and can be implicitly converted to `~fmt::format_args`.
 
  If ``format_str`` is a compile-time string then `make_args_checked` checks
 
  its validity at compile time.
 
  \endrst
 
 */
 
template <typename... Args, typename S, typename Char = char_t<S>>
 
inline auto make_args_checked(const S& format_str,
 
                              const remove_reference_t<Args>&... args)
 
    -> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
 
  static_assert(
 
      detail::count<(
 
              std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
 
              std::is_reference<Args>::value)...>() == 0,
 
      "passing views as lvalues is disallowed");
 
  detail::check_format_string<Args...>(format_str);
 
  return {args...};
 
}
 

	
 
/**
 
  \rst
 
  Returns a named argument to be used in a formatting function. It should only
 
  be used in a call to a formatting function.
 
  Returns a named argument to be used in a formatting function.
 
  It should only be used in a call to a formatting function or
 
  `dynamic_format_arg_store::push_back`.
 

	
 
  **Example**::
 

	
 
@@ -1636,183 +1856,11 @@ inline auto make_args_checked(const S& f
 
  \endrst
 
 */
 
template <typename Char, typename T>
 
inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) {
 
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
 
  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
 
  return {name, arg};
 
}
 

	
 
/**
 
  \rst
 
  A dynamic version of `fmt::format_arg_store`.
 
  It's equipped with a storage to potentially temporary objects which lifetimes
 
  could be shorter than the format arguments object.
 

	
 
  It can be implicitly converted into `~fmt::basic_format_args` for passing
 
  into type-erased formatting functions such as `~fmt::vformat`.
 
  \endrst
 
 */
 
template <typename Context>
 
class dynamic_format_arg_store
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
    // Workaround a GCC template argument substitution bug.
 
    : public basic_format_args<Context>
 
#endif
 
{
 
 private:
 
  using char_type = typename Context::char_type;
 

	
 
  template <typename T> struct need_copy {
 
    static constexpr detail::type mapped_type =
 
        detail::mapped_type_constant<T, Context>::value;
 

	
 
    enum {
 
      value = !(detail::is_reference_wrapper<T>::value ||
 
                std::is_same<T, basic_string_view<char_type>>::value ||
 
                std::is_same<T, detail::std_string_view<char_type>>::value ||
 
                (mapped_type != detail::type::cstring_type &&
 
                 mapped_type != detail::type::string_type &&
 
                 mapped_type != detail::type::custom_type))
 
    };
 
  };
 

	
 
  template <typename T>
 
  using stored_type = conditional_t<detail::is_string<T>::value,
 
                                    std::basic_string<char_type>, T>;
 

	
 
  // Storage of basic_format_arg must be contiguous.
 
  std::vector<basic_format_arg<Context>> data_;
 
  std::vector<detail::named_arg_info<char_type>> named_info_;
 

	
 
  // Storage of arguments not fitting into basic_format_arg must grow
 
  // without relocation because items in data_ refer to it.
 
  detail::dynamic_arg_list dynamic_args_;
 

	
 
  friend class basic_format_args<Context>;
 

	
 
  unsigned long long get_types() const {
 
    return detail::is_unpacked_bit | data_.size() |
 
           (named_info_.empty()
 
                ? 0ULL
 
                : static_cast<unsigned long long>(detail::has_named_args_bit));
 
  }
 

	
 
  const basic_format_arg<Context>* data() const {
 
    return named_info_.empty() ? data_.data() : data_.data() + 1;
 
  }
 

	
 
  template <typename T> void emplace_arg(const T& arg) {
 
    data_.emplace_back(detail::make_arg<Context>(arg));
 
  }
 

	
 
  template <typename T>
 
  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
 
    if (named_info_.empty()) {
 
      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
 
      data_.insert(data_.begin(), {zero_ptr, 0});
 
    }
 
    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
 
    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
 
      data->pop_back();
 
    };
 
    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
 
        guard{&data_, pop_one};
 
    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
 
    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
 
    guard.release();
 
  }
 

	
 
 public:
 
  /**
 
    \rst
 
    Adds an argument into the dynamic store for later passing to a formatting
 
    function.
 

	
 
    Note that custom types and string types (but not string views) are copied
 
    into the store dynamically allocating memory if necessary.
 

	
 
    **Example**::
 

	
 
      fmt::dynamic_format_arg_store<fmt::format_context> store;
 
      store.push_back(42);
 
      store.push_back("abc");
 
      store.push_back(1.5f);
 
      std::string result = fmt::vformat("{} and {} and {}", store);
 
    \endrst
 
  */
 
  template <typename T> void push_back(const T& arg) {
 
    if (detail::const_check(need_copy<T>::value))
 
      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
 
    else
 
      emplace_arg(detail::unwrap(arg));
 
  }
 

	
 
  /**
 
    \rst
 
    Adds a reference to the argument into the dynamic store for later passing to
 
    a formatting function. Supports named arguments wrapped in
 
    ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``.
 

	
 
    **Example**::
 

	
 
      fmt::dynamic_format_arg_store<fmt::format_context> store;
 
      char str[] = "1234567890";
 
      store.push_back(std::cref(str));
 
      int a1_val{42};
 
      auto a1 = fmt::arg("a1_", a1_val);
 
      store.push_back(std::cref(a1));
 

	
 
      // Changing str affects the output but only for string and custom types.
 
      str[0] = 'X';
 

	
 
      std::string result = fmt::vformat("{} and {a1_}");
 
      assert(result == "X234567890 and 42");
 
    \endrst
 
  */
 
  template <typename T> void push_back(std::reference_wrapper<T> arg) {
 
    static_assert(
 
        detail::is_named_arg<typename std::remove_cv<T>::type>::value ||
 
            need_copy<T>::value,
 
        "objects of built-in types and string views are always copied");
 
    emplace_arg(arg.get());
 
  }
 

	
 
  /**
 
    Adds named argument into the dynamic store for later passing to a formatting
 
    function. ``std::reference_wrapper`` is supported to avoid copying of the
 
    argument.
 
  */
 
  template <typename T>
 
  void push_back(const detail::named_arg<char_type, T>& arg) {
 
    const char_type* arg_name =
 
        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
 
    if (detail::const_check(need_copy<T>::value)) {
 
      emplace_arg(
 
          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
 
    } else {
 
      emplace_arg(fmt::arg(arg_name, arg.value));
 
    }
 
  }
 

	
 
  /** Erase all elements from the store */
 
  void clear() {
 
    data_.clear();
 
    named_info_.clear();
 
    dynamic_args_ = detail::dynamic_arg_list();
 
  }
 

	
 
  /**
 
    \rst
 
    Reserves space to store at least *new_cap* arguments including
 
    *new_cap_named* named arguments.
 
    \endrst
 
  */
 
  void reserve(size_t new_cap, size_t new_cap_named) {
 
    FMT_ASSERT(new_cap >= new_cap_named,
 
               "Set of arguments includes set of named arguments");
 
    data_.reserve(new_cap);
 
    named_info_.reserve(new_cap_named);
 
  }
 
};
 
FMT_END_EXPORT
 

	
 
/**
 
  \rst
 
@@ -1845,25 +1893,27 @@ template <typename Context> class basic_
 
    const format_arg* args_;
 
  };
 

	
 
  bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; }
 
  bool has_named_args() const {
 
  constexpr auto is_packed() const -> bool {
 
    return (desc_ & detail::is_unpacked_bit) == 0;
 
  }
 
  auto has_named_args() const -> bool {
 
    return (desc_ & detail::has_named_args_bit) != 0;
 
  }
 

	
 
  detail::type type(int index) const {
 
  FMT_CONSTEXPR auto type(int index) const -> detail::type {
 
    int shift = index * detail::packed_arg_bits;
 
    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
 
    return static_cast<detail::type>((desc_ >> shift) & mask);
 
  }
 

	
 
  basic_format_args(unsigned long long desc,
 
  constexpr FMT_INLINE basic_format_args(unsigned long long desc,
 
                    const detail::value<Context>* values)
 
      : desc_(desc), values_(values) {}
 
  basic_format_args(unsigned long long desc, const format_arg* args)
 
  constexpr basic_format_args(unsigned long long desc, const format_arg* args)
 
      : desc_(desc), args_(args) {}
 

	
 
 public:
 
  basic_format_args() : desc_(0) {}
 
  constexpr basic_format_args() : desc_(0), args_(nullptr) {}
 

	
 
  /**
 
   \rst
 
@@ -1871,8 +1921,10 @@ template <typename Context> class basic_
 
   \endrst
 
   */
 
  template <typename... Args>
 
  FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& store)
 
      : basic_format_args(store.desc, store.data_.args()) {}
 
  constexpr FMT_INLINE basic_format_args(
 
      const format_arg_store<Context, Args...>& store)
 
      : basic_format_args(format_arg_store<Context, Args...>::desc,
 
                          store.data_.args()) {}
 

	
 
  /**
 
   \rst
 
@@ -1880,7 +1932,8 @@ template <typename Context> class basic_
 
   `~fmt::dynamic_format_arg_store`.
 
   \endrst
 
   */
 
  FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store)
 
  constexpr FMT_INLINE basic_format_args(
 
      const dynamic_format_arg_store<Context>& store)
 
      : basic_format_args(store.get_types(), store.data()) {}
 

	
 
  /**
 
@@ -1888,12 +1941,12 @@ template <typename Context> class basic_
 
   Constructs a `basic_format_args` object from a dynamic set of arguments.
 
   \endrst
 
   */
 
  basic_format_args(const format_arg* args, int count)
 
  constexpr basic_format_args(const format_arg* args, int count)
 
      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
 
                          args) {}
 

	
 
  /** Returns the argument with the specified id. */
 
  format_arg get(int id) const {
 
  FMT_CONSTEXPR auto get(int id) const -> format_arg {
 
    format_arg arg;
 
    if (!is_packed()) {
 
      if (id < max_size()) arg = args_[id];
 
@@ -1906,12 +1959,14 @@ template <typename Context> class basic_
 
    return arg;
 
  }
 

	
 
  template <typename Char> format_arg get(basic_string_view<Char> name) const {
 
  template <typename Char>
 
  auto get(basic_string_view<Char> name) const -> format_arg {
 
    int id = get_id(name);
 
    return id >= 0 ? get(id) : format_arg();
 
  }
 

	
 
  template <typename Char> int get_id(basic_string_view<Char> name) const {
 
  template <typename Char>
 
  auto get_id(basic_string_view<Char> name) const -> int {
 
    if (!has_named_args()) return -1;
 
    const auto& named_args =
 
        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
 
@@ -1921,49 +1976,711 @@ template <typename Context> class basic_
 
    return -1;
 
  }
 

	
 
  int max_size() const {
 
  auto max_size() const -> int {
 
    unsigned long long max_packed = detail::max_packed_args;
 
    return static_cast<int>(is_packed() ? max_packed
 
                                        : desc_ & ~detail::is_unpacked_bit);
 
  }
 
};
 

	
 
#ifdef FMT_ARM_ABI_COMPATIBILITY
 
/** An alias to ``basic_format_args<format_context>``. */
 
// Separate types would result in shorter symbols but break ABI compatibility
 
// A separate type would result in shorter symbols but break ABI compatibility
 
// between clang and gcc on ARM (#1919).
 
using format_args = basic_format_args<format_context>;
 
using wformat_args = basic_format_args<wformat_context>;
 
FMT_MODULE_EXPORT using format_args = basic_format_args<format_context>;
 

	
 
// We cannot use enum classes as bit fields because of a gcc bug, so we put them
 
// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
 
// Additionally, if an underlying type is specified, older gcc incorrectly warns
 
// that the type is too small. Both bugs are fixed in gcc 9.3.
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
 
#  define FMT_ENUM_UNDERLYING_TYPE(type)
 
#else
 
// DEPRECATED! These are kept for ABI compatibility.
 
// It is a separate type rather than an alias to make symbols readable.
 
struct format_args : basic_format_args<format_context> {
 
  template <typename... Args>
 
  FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {}
 
#  define FMT_ENUM_UNDERLYING_TYPE(type) : type
 
#endif
 
namespace align {
 
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
 
                                                  numeric};
 
}
 
using align_t = align::type;
 
namespace sign {
 
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
 
}
 
using sign_t = sign::type;
 

	
 
namespace detail {
 

	
 
// Workaround an array initialization issue in gcc 4.8.
 
template <typename Char> struct fill_t {
 
 private:
 
  enum { max_size = 4 };
 
  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
 
  unsigned char size_ = 1;
 

	
 
 public:
 
  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
 
    auto size = s.size();
 
    FMT_ASSERT(size <= max_size, "invalid fill");
 
    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
 
    size_ = static_cast<unsigned char>(size);
 
  }
 

	
 
  constexpr auto size() const -> size_t { return size_; }
 
  constexpr auto data() const -> const Char* { return data_; }
 

	
 
  FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
 
  FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
 
    return data_[index];
 
  }
 
};
 
struct wformat_args : basic_format_args<wformat_context> {
 
  using basic_format_args::basic_format_args;
 
}  // namespace detail
 

	
 
enum class presentation_type : unsigned char {
 
  none,
 
  dec,             // 'd'
 
  oct,             // 'o'
 
  hex_lower,       // 'x'
 
  hex_upper,       // 'X'
 
  bin_lower,       // 'b'
 
  bin_upper,       // 'B'
 
  hexfloat_lower,  // 'a'
 
  hexfloat_upper,  // 'A'
 
  exp_lower,       // 'e'
 
  exp_upper,       // 'E'
 
  fixed_lower,     // 'f'
 
  fixed_upper,     // 'F'
 
  general_lower,   // 'g'
 
  general_upper,   // 'G'
 
  chr,             // 'c'
 
  string,          // 's'
 
  pointer,         // 'p'
 
  debug            // '?'
 
};
 
#endif
 

	
 
// Format specifiers for built-in and string types.
 
template <typename Char = char> struct format_specs {
 
  int width;
 
  int precision;
 
  presentation_type type;
 
  align_t align : 4;
 
  sign_t sign : 3;
 
  bool alt : 1;  // Alternate form ('#').
 
  bool localized : 1;
 
  detail::fill_t<Char> fill;
 

	
 
  constexpr format_specs()
 
      : width(0),
 
        precision(-1),
 
        type(presentation_type::none),
 
        align(align::none),
 
        sign(sign::none),
 
        alt(false),
 
        localized(false) {}
 
};
 

	
 
namespace detail {
 

	
 
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
std::basic_string<Char> vformat(
 
    basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args);
 

	
 
FMT_API std::string vformat(string_view format_str, format_args args);
 
enum class arg_id_kind { none, index, name };
 

	
 
// An argument reference.
 
template <typename Char> struct arg_ref {
 
  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
 

	
 
  FMT_CONSTEXPR explicit arg_ref(int index)
 
      : kind(arg_id_kind::index), val(index) {}
 
  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
 
      : kind(arg_id_kind::name), val(name) {}
 

	
 
  FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
 
    kind = arg_id_kind::index;
 
    val.index = idx;
 
    return *this;
 
  }
 

	
 
  arg_id_kind kind;
 
  union value {
 
    FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
 
    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 

	
 
    int index;
 
    basic_string_view<Char> name;
 
  } val;
 
};
 

	
 
// Format specifiers with width and precision resolved at formatting rather
 
// than parsing time to allow reusing the same parsed specifiers with
 
// different sets of arguments (precompilation of format strings).
 
template <typename Char = char>
 
struct dynamic_format_specs : format_specs<Char> {
 
  arg_ref<Char> width_ref;
 
  arg_ref<Char> precision_ref;
 
};
 

	
 
// Converts a character to ASCII. Returns '\0' on conversion failure.
 
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
 
constexpr auto to_ascii(Char c) -> char {
 
  return c <= 0xff ? static_cast<char>(c) : '\0';
 
}
 
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
 
constexpr auto to_ascii(Char c) -> char {
 
  return c <= 0xff ? static_cast<char>(c) : '\0';
 
}
 

	
 
// Returns the number of code units in a code point or 1 on error.
 
template <typename Char>
 
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
 
  if (const_check(sizeof(Char) != 1)) return 1;
 
  auto c = static_cast<unsigned char>(*begin);
 
  return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
 
}
 

	
 
// Return the result via the out param to workaround gcc bug 77539.
 
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
 
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
 
  for (out = first; out != last; ++out) {
 
    if (*out == value) return true;
 
  }
 
  return false;
 
}
 

	
 
template <>
 
inline auto find<false, char>(const char* first, const char* last, char value,
 
                              const char*& out) -> bool {
 
  out = static_cast<const char*>(
 
      std::memchr(first, value, to_unsigned(last - first)));
 
  return out != nullptr;
 
}
 

	
 
// Parses the range [begin, end) as an unsigned integer. This function assumes
 
// that the range is non-empty and the first character is a digit.
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
 
                                         int error_value) noexcept -> int {
 
  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
 
  unsigned value = 0, prev = 0;
 
  auto p = begin;
 
  do {
 
    prev = value;
 
    value = value * 10 + unsigned(*p - '0');
 
    ++p;
 
  } while (p != end && '0' <= *p && *p <= '9');
 
  auto num_digits = p - begin;
 
  begin = p;
 
  if (num_digits <= std::numeric_limits<int>::digits10)
 
    return static_cast<int>(value);
 
  // Check for overflow.
 
  const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
 
  return num_digits == std::numeric_limits<int>::digits10 + 1 &&
 
                 prev * 10ull + unsigned(p[-1] - '0') <= max
 
             ? static_cast<int>(value)
 
             : error_value;
 
}
 

	
 
FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
 
  switch (c) {
 
  case '<':
 
    return align::left;
 
  case '>':
 
    return align::right;
 
  case '^':
 
    return align::center;
 
  }
 
  return align::none;
 
}
 

	
 
template <typename Char> constexpr auto is_name_start(Char c) -> bool {
 
  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
 
                                   Handler&& handler) -> const Char* {
 
  Char c = *begin;
 
  if (c >= '0' && c <= '9') {
 
    int index = 0;
 
    constexpr int max = (std::numeric_limits<int>::max)();
 
    if (c != '0')
 
      index = parse_nonnegative_int(begin, end, max);
 
    else
 
      ++begin;
 
    if (begin == end || (*begin != '}' && *begin != ':'))
 
      throw_format_error("invalid format string");
 
    else
 
      handler.on_index(index);
 
    return begin;
 
  }
 
  if (!is_name_start(c)) {
 
    throw_format_error("invalid format string");
 
    return begin;
 
  }
 
  auto it = begin;
 
  do {
 
    ++it;
 
  } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
 
  handler.on_name({begin, to_unsigned(it - begin)});
 
  return it;
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
 
                                           Handler&& handler) -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  Char c = *begin;
 
  if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
 
  handler.on_auto();
 
  return begin;
 
}
 

	
 
template <typename Char> struct dynamic_spec_id_handler {
 
  basic_format_parse_context<Char>& ctx;
 
  arg_ref<Char>& ref;
 

	
 
  FMT_CONSTEXPR void on_auto() {
 
    int id = ctx.next_arg_id();
 
    ref = arg_ref<Char>(id);
 
    ctx.check_dynamic_spec(id);
 
  }
 
  FMT_CONSTEXPR void on_index(int id) {
 
    ref = arg_ref<Char>(id);
 
    ctx.check_arg_id(id);
 
    ctx.check_dynamic_spec(id);
 
  }
 
  FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
 
    ref = arg_ref<Char>(id);
 
    ctx.check_arg_id(id);
 
  }
 
};
 

	
 
// Parses [integer | "{" [arg_id] "}"].
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
 
                                      int& value, arg_ref<Char>& ref,
 
                                      basic_format_parse_context<Char>& ctx)
 
    -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  if ('0' <= *begin && *begin <= '9') {
 
    int val = parse_nonnegative_int(begin, end, -1);
 
    if (val != -1)
 
      value = val;
 
    else
 
      throw_format_error("number is too big");
 
  } else if (*begin == '{') {
 
    ++begin;
 
    auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
 
    if (begin != end) begin = parse_arg_id(begin, end, handler);
 
    if (begin != end && *begin == '}') return ++begin;
 
    throw_format_error("invalid format string");
 
  }
 
  return begin;
 
}
 

	
 
template <typename Char>
 
void vformat_to(
 
    buffer<Char>& buf, basic_string_view<Char> format_str,
 
    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
 
    detail::locale_ref loc = {});
 

	
 
template <typename Char, typename Args,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
 
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
 
                                   int& value, arg_ref<Char>& ref,
 
                                   basic_format_parse_context<Char>& ctx)
 
    -> const Char* {
 
  ++begin;
 
  if (begin == end || *begin == '}') {
 
    throw_format_error("invalid precision");
 
    return begin;
 
  }
 
  return parse_dynamic_spec(begin, end, value, ref, ctx);
 
}
 

	
 
enum class state { start, align, sign, hash, zero, width, precision, locale };
 

	
 
// Parses standard format specifiers.
 
template <typename Char>
 
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
 
    const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
 
    basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
 
  auto c = '\0';
 
  if (end - begin > 1) {
 
    auto next = to_ascii(begin[1]);
 
    c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
 
  } else {
 
    if (begin == end) return begin;
 
    c = to_ascii(*begin);
 
  }
 

	
 
  struct {
 
    state current_state = state::start;
 
    FMT_CONSTEXPR void operator()(state s, bool valid = true) {
 
      if (current_state >= s || !valid)
 
        throw_format_error("invalid format specifier");
 
      current_state = s;
 
    }
 
  } enter_state;
 

	
 
  using pres = presentation_type;
 
  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
 
  struct {
 
    const Char*& begin;
 
    dynamic_format_specs<Char>& specs;
 
    type arg_type;
 

	
 
    FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
 
      if (!in(arg_type, set)) throw_format_error("invalid format specifier");
 
      specs.type = type;
 
      return begin + 1;
 
    }
 
  } parse_presentation_type{begin, specs, arg_type};
 

	
 
  for (;;) {
 
    switch (c) {
 
    case '<':
 
    case '>':
 
    case '^':
 
      enter_state(state::align);
 
      specs.align = parse_align(c);
 
      ++begin;
 
      break;
 
    case '+':
 
    case '-':
 
    case ' ':
 
      enter_state(state::sign, in(arg_type, sint_set | float_set));
 
      switch (c) {
 
      case '+':
 
        specs.sign = sign::plus;
 
        break;
 
      case '-':
 
        specs.sign = sign::minus;
 
        break;
 
      case ' ':
 
        specs.sign = sign::space;
 
        break;
 
      }
 
      ++begin;
 
      break;
 
    case '#':
 
      enter_state(state::hash, is_arithmetic_type(arg_type));
 
      specs.alt = true;
 
      ++begin;
 
      break;
 
    case '0':
 
      enter_state(state::zero);
 
      if (!is_arithmetic_type(arg_type))
 
        throw_format_error("format specifier requires numeric argument");
 
      if (specs.align == align::none) {
 
        // Ignore 0 if align is specified for compatibility with std::format.
 
        specs.align = align::numeric;
 
        specs.fill[0] = Char('0');
 
      }
 
      ++begin;
 
      break;
 
    case '1':
 
    case '2':
 
    case '3':
 
    case '4':
 
    case '5':
 
    case '6':
 
    case '7':
 
    case '8':
 
    case '9':
 
    case '{':
 
      enter_state(state::width);
 
      begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
 
      break;
 
    case '.':
 
      enter_state(state::precision,
 
                  in(arg_type, float_set | string_set | cstring_set));
 
      begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
 
                              ctx);
 
      break;
 
    case 'L':
 
      enter_state(state::locale, is_arithmetic_type(arg_type));
 
      specs.localized = true;
 
      ++begin;
 
      break;
 
    case 'd':
 
      return parse_presentation_type(pres::dec, integral_set);
 
    case 'o':
 
      return parse_presentation_type(pres::oct, integral_set);
 
    case 'x':
 
      return parse_presentation_type(pres::hex_lower, integral_set);
 
    case 'X':
 
      return parse_presentation_type(pres::hex_upper, integral_set);
 
    case 'b':
 
      return parse_presentation_type(pres::bin_lower, integral_set);
 
    case 'B':
 
      return parse_presentation_type(pres::bin_upper, integral_set);
 
    case 'a':
 
      return parse_presentation_type(pres::hexfloat_lower, float_set);
 
    case 'A':
 
      return parse_presentation_type(pres::hexfloat_upper, float_set);
 
    case 'e':
 
      return parse_presentation_type(pres::exp_lower, float_set);
 
    case 'E':
 
      return parse_presentation_type(pres::exp_upper, float_set);
 
    case 'f':
 
      return parse_presentation_type(pres::fixed_lower, float_set);
 
    case 'F':
 
      return parse_presentation_type(pres::fixed_upper, float_set);
 
    case 'g':
 
      return parse_presentation_type(pres::general_lower, float_set);
 
    case 'G':
 
      return parse_presentation_type(pres::general_upper, float_set);
 
    case 'c':
 
      return parse_presentation_type(pres::chr, integral_set);
 
    case 's':
 
      return parse_presentation_type(pres::string,
 
                                     bool_set | string_set | cstring_set);
 
    case 'p':
 
      return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
 
    case '?':
 
      return parse_presentation_type(pres::debug,
 
                                     char_set | string_set | cstring_set);
 
    case '}':
 
      return begin;
 
    default: {
 
      if (*begin == '}') return begin;
 
      // Parse fill and alignment.
 
      auto fill_end = begin + code_point_length(begin);
 
      if (end - fill_end <= 0) {
 
        throw_format_error("invalid format specifier");
 
        return begin;
 
      }
 
      if (*begin == '{') {
 
        throw_format_error("invalid fill character '{'");
 
        return begin;
 
      }
 
      auto align = parse_align(to_ascii(*fill_end));
 
      enter_state(state::align, align != align::none);
 
      specs.fill = {begin, to_unsigned(fill_end - begin)};
 
      specs.align = align;
 
      begin = fill_end + 1;
 
    }
 
    }
 
    if (begin == end) return begin;
 
    c = to_ascii(*begin);
 
  }
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
 
                                           Handler&& handler) -> const Char* {
 
  struct id_adapter {
 
    Handler& handler;
 
    int arg_id;
 

	
 
    FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
 
    FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
 
    FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
 
      arg_id = handler.on_arg_id(id);
 
    }
 
  };
 

	
 
  ++begin;
 
  if (begin == end) return handler.on_error("invalid format string"), end;
 
  if (*begin == '}') {
 
    handler.on_replacement_field(handler.on_arg_id(), begin);
 
  } else if (*begin == '{') {
 
    handler.on_text(begin, begin + 1);
 
  } else {
 
    auto adapter = id_adapter{handler, 0};
 
    begin = parse_arg_id(begin, end, adapter);
 
    Char c = begin != end ? *begin : Char();
 
    if (c == '}') {
 
      handler.on_replacement_field(adapter.arg_id, begin);
 
    } else if (c == ':') {
 
      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
 
      if (begin == end || *begin != '}')
 
        return handler.on_error("unknown format specifier"), end;
 
    } else {
 
      return handler.on_error("missing '}' in format string"), end;
 
    }
 
  }
 
  return begin + 1;
 
}
 

	
 
template <bool IS_CONSTEXPR, typename Char, typename Handler>
 
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
 
    basic_string_view<Char> format_str, Handler&& handler) {
 
  auto begin = format_str.data();
 
  auto end = begin + format_str.size();
 
  if (end - begin < 32) {
 
    // Use a simple loop instead of memchr for small strings.
 
    const Char* p = begin;
 
    while (p != end) {
 
      auto c = *p++;
 
      if (c == '{') {
 
        handler.on_text(begin, p - 1);
 
        begin = p = parse_replacement_field(p - 1, end, handler);
 
      } else if (c == '}') {
 
        if (p == end || *p != '}')
 
          return handler.on_error("unmatched '}' in format string");
 
        handler.on_text(begin, p);
 
        begin = ++p;
 
      }
 
    }
 
    handler.on_text(begin, end);
 
    return;
 
  }
 
  struct writer {
 
    FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
 
      if (from == to) return;
 
      for (;;) {
 
        const Char* p = nullptr;
 
        if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
 
          return handler_.on_text(from, to);
 
        ++p;
 
        if (p == to || *p != '}')
 
          return handler_.on_error("unmatched '}' in format string");
 
        handler_.on_text(from, p);
 
        from = p + 1;
 
      }
 
    }
 
    Handler& handler_;
 
  } write = {handler};
 
  while (begin != end) {
 
    // Doing two passes with memchr (one for '{' and another for '}') is up to
 
    // 2.5x faster than the naive one-pass implementation on big format strings.
 
    const Char* p = begin;
 
    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
 
      return write(begin, end);
 
    write(begin, p);
 
    begin = parse_replacement_field(p, end, handler);
 
  }
 
}
 

	
 
template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
 
  using type = T;
 
};
 
template <typename T> struct strip_named_arg<T, true> {
 
  using type = remove_cvref_t<decltype(T::value)>;
 
};
 

	
 
template <typename T, typename ParseContext>
 
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
 
    -> decltype(ctx.begin()) {
 
  using char_type = typename ParseContext::char_type;
 
  using context = buffer_context<char_type>;
 
  using mapped_type = conditional_t<
 
      mapped_type_constant<T, context>::value != type::custom_type,
 
      decltype(arg_mapper<context>().map(std::declval<const T&>())),
 
      typename strip_named_arg<T>::type>;
 
  return formatter<mapped_type, char_type>().parse(ctx);
 
}
 

	
 
// Checks char specs and returns true iff the presentation type is char-like.
 
template <typename Char>
 
FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
 
  if (specs.type != presentation_type::none &&
 
      specs.type != presentation_type::chr &&
 
      specs.type != presentation_type::debug) {
 
    return false;
 
  }
 
  if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
 
    throw_format_error("invalid format specifier for char");
 
  return true;
 
}
 

	
 
constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1;
 

	
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
template <int N, typename T, typename... Args, typename Char>
 
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
 
  if constexpr (is_statically_named_arg<T>()) {
 
    if (name == T::name) return N;
 
  }
 
  if constexpr (sizeof...(Args) > 0)
 
    return get_arg_index_by_name<N + 1, Args...>(name);
 
  (void)name;  // Workaround an MSVC bug about "unused" parameter.
 
  return invalid_arg_index;
 
}
 
#endif
 

	
 
template <typename... Args, typename Char>
 
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
  if constexpr (sizeof...(Args) > 0)
 
    return get_arg_index_by_name<0, Args...>(name);
 
#endif
 
  (void)name;
 
  return invalid_arg_index;
 
}
 

	
 
template <typename Char, typename... Args> class format_string_checker {
 
 private:
 
  using parse_context_type = compile_parse_context<Char>;
 
  static constexpr int num_args = sizeof...(Args);
 

	
 
  // Format specifier parsing function.
 
  // In the future basic_format_parse_context will replace compile_parse_context
 
  // here and will use is_constant_evaluated and downcasting to access the data
 
  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
 
  using parse_func = const Char* (*)(parse_context_type&);
 

	
 
  parse_context_type context_;
 
  parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
 
  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
 

	
 
 public:
 
  explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
 
      : context_(fmt, num_args, types_),
 
        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
 
        types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {}
 

	
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 

	
 
  FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
 
  FMT_CONSTEXPR auto on_arg_id(int id) -> int {
 
    return context_.check_arg_id(id), id;
 
  }
 
  FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
    auto index = get_arg_index_by_name<Args...>(id);
 
    if (index == invalid_arg_index) on_error("named argument is not found");
 
    return index;
 
#else
 
    (void)id;
 
    on_error("compile-time checks for named arguments require C++20 support");
 
    return 0;
 
#endif
 
  }
 

	
 
  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
 

	
 
  FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
 
      -> const Char* {
 
    context_.advance_to(begin);
 
    // id >= 0 check is a workaround for gcc 10 bug (#2065).
 
    return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    throw_format_error(message);
 
  }
 
};
 

	
 
// Reports a compile-time error if S is not a valid format string.
 
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
 
FMT_INLINE void check_format_string(const S&) {
 
#ifdef FMT_ENFORCE_COMPILE_STRING
 
  static_assert(is_compile_string<S>::value,
 
                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
 
                "FMT_STRING.");
 
#endif
 
}
 
template <typename... Args, typename S,
 
          FMT_ENABLE_IF(is_compile_string<S>::value)>
 
void check_format_string(S format_str) {
 
  using char_t = typename S::char_type;
 
  FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
 
  using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
 
  FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
 
  ignore_unused(error);
 
}
 

	
 
template <typename Char = char> struct vformat_args {
 
  using type = basic_format_args<
 
      basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
 
};
 
template <> struct vformat_args<char> { using type = format_args; };
 

	
 
// Use vformat_args and avoid type_identity to keep symbols short.
 
template <typename Char>
 
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
 
                typename vformat_args<Char>::type args, locale_ref loc = {});
 

	
 
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
 
#ifndef _WIN32
 
@@ -1971,37 +2688,161 @@ inline void vprint_mojibake(std::FILE*, 
 
#endif
 
}  // namespace detail
 

	
 
FMT_BEGIN_EXPORT
 

	
 
// A formatter specialization for natively supported types.
 
template <typename T, typename Char>
 
struct formatter<T, Char,
 
                 enable_if_t<detail::type_constant<T, Char>::value !=
 
                             detail::type::custom_type>> {
 
 private:
 
  detail::dynamic_format_specs<Char> specs_;
 

	
 
 public:
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
 
    auto type = detail::type_constant<T, Char>::value;
 
    auto end =
 
        detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
 
    if (type == detail::type::char_type) detail::check_char_specs(specs_);
 
    return end;
 
  }
 

	
 
  template <detail::type U = detail::type_constant<T, Char>::value,
 
            FMT_ENABLE_IF(U == detail::type::string_type ||
 
                          U == detail::type::cstring_type ||
 
                          U == detail::type::char_type)>
 
  FMT_CONSTEXPR void set_debug_format(bool set = true) {
 
    specs_.type = set ? presentation_type::debug : presentation_type::none;
 
  }
 

	
 
  template <typename FormatContext>
 
  FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
 
      -> decltype(ctx.out());
 
};
 

	
 
#define FMT_FORMAT_AS(Type, Base)                                        \
 
  template <typename Char>                                               \
 
  struct formatter<Type, Char> : formatter<Base, Char> {                 \
 
    template <typename FormatContext>                                    \
 
    auto format(const Type& val, FormatContext& ctx) const               \
 
        -> decltype(ctx.out()) {                                         \
 
      return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
 
    }                                                                    \
 
  }
 

	
 
FMT_FORMAT_AS(signed char, int);
 
FMT_FORMAT_AS(unsigned char, unsigned);
 
FMT_FORMAT_AS(short, int);
 
FMT_FORMAT_AS(unsigned short, unsigned);
 
FMT_FORMAT_AS(long, long long);
 
FMT_FORMAT_AS(unsigned long, unsigned long long);
 
FMT_FORMAT_AS(Char*, const Char*);
 
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
 
FMT_FORMAT_AS(std::nullptr_t, const void*);
 
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
 

	
 
template <typename Char = char> struct runtime_format_string {
 
  basic_string_view<Char> str;
 
};
 

	
 
/** A compile-time format string. */
 
template <typename Char, typename... Args> class basic_format_string {
 
 private:
 
  basic_string_view<Char> str_;
 

	
 
 public:
 
  template <typename S,
 
            FMT_ENABLE_IF(
 
                std::is_convertible<const S&, basic_string_view<Char>>::value)>
 
  FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
 
    static_assert(
 
        detail::count<
 
            (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
 
             std::is_reference<Args>::value)...>() == 0,
 
        "passing views as lvalues is disallowed");
 
#ifdef FMT_HAS_CONSTEVAL
 
    if constexpr (detail::count_named_args<Args...>() ==
 
                  detail::count_statically_named_args<Args...>()) {
 
      using checker =
 
          detail::format_string_checker<Char, remove_cvref_t<Args>...>;
 
      detail::parse_format_string<true>(str_, checker(s));
 
    }
 
#else
 
    detail::check_format_string<Args...>(s);
 
#endif
 
  }
 
  basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
 

	
 
  FMT_INLINE operator basic_string_view<Char>() const { return str_; }
 
  FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
 
};
 

	
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
// Workaround broken conversion on older gcc.
 
template <typename...> using format_string = string_view;
 
inline auto runtime(string_view s) -> string_view { return s; }
 
#else
 
template <typename... Args>
 
using format_string = basic_format_string<char, type_identity_t<Args>...>;
 
/**
 
  \rst
 
  Creates a runtime format string.
 

	
 
  **Example**::
 

	
 
    // Check format string at runtime instead of compile-time.
 
    fmt::print(fmt::runtime("{:d}"), "I am not a number");
 
  \endrst
 
 */
 
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
 
#endif
 

	
 
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``fmt`` and returns the result
 
  as a string.
 

	
 
  **Example**::
 

	
 
    #include <fmt/core.h>
 
    std::string message = fmt::format("The answer is {}.", 42);
 
  \endrst
 
*/
 
template <typename... T>
 
FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
 
    -> std::string {
 
  return vformat(fmt, fmt::make_format_args(args...));
 
}
 

	
 
/** Formats a string and writes the output to ``out``. */
 
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
 
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
 
template <typename OutputIt, typename S, typename Char = char_t<S>,
 
          bool enable = detail::is_output_iterator<OutputIt, Char>::value>
 
auto vformat_to(OutputIt out, const S& format_str,
 
                basic_format_args<buffer_context<type_identity_t<Char>>> args)
 
    -> typename std::enable_if<enable, OutputIt>::type {
 
  decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
 
  detail::vformat_to(buf, to_string_view(format_str), args);
 
  return detail::get_iterator(buf);
 
template <typename OutputIt,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
 
  auto&& buf = detail::get_buffer<char>(out);
 
  detail::vformat_to(buf, fmt, args, {});
 
  return detail::get_iterator(buf, out);
 
}
 

	
 
/**
 
 \rst
 
 Formats arguments, writes the result to the output iterator ``out`` and returns
 
 the iterator past the end of the output range.
 
 Formats ``args`` according to specifications in ``fmt``, writes the result to
 
 the output iterator ``out`` and returns the iterator past the end of the output
 
 range. `format_to` does not append a terminating null character.
 

	
 
 **Example**::
 

	
 
   std::vector<char> out;
 
   auto out = std::vector<char>();
 
   fmt::format_to(std::back_inserter(out), "{}", 42);
 
 \endrst
 
 */
 
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
 
template <typename OutputIt, typename S, typename... Args,
 
          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
 
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
 
    typename std::enable_if<enable, OutputIt>::type {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return vformat_to(out, to_string_view(format_str), vargs);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
 
    -> OutputIt {
 
  return vformat_to(out, fmt, fmt::make_format_args(args...));
 
}
 

	
 
template <typename OutputIt> struct format_to_n_result {
 
@@ -2011,111 +2852,100 @@ template <typename OutputIt> struct form
 
  size_t size;
 
};
 

	
 
template <typename OutputIt, typename Char, typename... Args,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
 
inline format_to_n_result<OutputIt> vformat_to_n(
 
    OutputIt out, size_t n, basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
 
                                                                           n);
 
  detail::vformat_to(buf, format_str, args);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
 
    -> format_to_n_result<OutputIt> {
 
  using traits = detail::fixed_buffer_traits;
 
  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
 
  detail::vformat_to(buf, fmt, args, {});
 
  return {buf.out(), buf.count()};
 
}
 

	
 
/**
 
 \rst
 
 Formats arguments, writes up to ``n`` characters of the result to the output
 
 iterator ``out`` and returns the total output size and the iterator past the
 
 end of the output range.
 
  Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
 
  characters of the result to the output iterator ``out`` and returns the total
 
  (not truncated) output size and the iterator past the end of the output range.
 
  `format_to_n` does not append a terminating null character.
 
 \endrst
 
 */
 
template <typename OutputIt, typename S, typename... Args,
 
          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
 
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
 
                        const Args&... args) ->
 
    typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return vformat_to_n(out, n, to_string_view(format_str), vargs);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
 
                            T&&... args) -> format_to_n_result<OutputIt> {
 
  return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
 
}
 

	
 
/** Returns the number of chars in the output of ``format(fmt, args...)``. */
 
template <typename... T>
 
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
 
                                             T&&... args) -> size_t {
 
  auto buf = detail::counting_buffer<>();
 
  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
 
  return buf.count();
 
}
 

	
 
FMT_API void vprint(string_view fmt, format_args args);
 
FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
 

	
 
/**
 
  Returns the number of characters in the output of
 
  ``format(format_str, args...)``.
 
  \rst
 
  Formats ``args`` according to specifications in ``fmt`` and writes the output
 
  to ``stdout``.
 

	
 
  **Example**::
 

	
 
    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
 
  \endrst
 
 */
 
template <typename... Args>
 
inline size_t formatted_size(string_view format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  detail::counting_buffer<> buf;
 
  detail::vformat_to(buf, format_str, vargs);
 
  return buf.count();
 
}
 

	
 
template <typename S, typename Char = char_t<S>>
 
FMT_INLINE std::basic_string<Char> vformat(
 
    const S& format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  return detail::vformat(to_string_view(format_str), args);
 
template <typename... T>
 
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
 
  const auto& vargs = fmt::make_format_args(args...);
 
  return detail::is_utf8() ? vprint(fmt, vargs)
 
                           : detail::vprint_mojibake(stdout, fmt, vargs);
 
}
 

	
 
/**
 
  \rst
 
  Formats arguments and returns the result as a string.
 

	
 
  **Example**::
 

	
 
    #include <fmt/core.h>
 
    std::string message = fmt::format("The answer is {}", 42);
 
  \endrst
 
*/
 
// Pass char_t as a default template parameter instead of using
 
// std::basic_string<char_t<S>> to reduce the symbol size.
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::vformat(to_string_view(format_str), vargs);
 
}
 

	
 
FMT_API void vprint(string_view, format_args);
 
FMT_API void vprint(std::FILE*, string_view, format_args);
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``format_str`` and writes the
 
  output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
 
  ``FMT_UNICODE`` macro is set to 0.
 
  Formats ``args`` according to specifications in ``fmt`` and writes the
 
  output to the file ``f``.
 

	
 
  **Example**::
 

	
 
    fmt::print(stderr, "Don't {}!", "panic");
 
  \endrst
 
 */
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::is_unicode<Char>()
 
             ? vprint(f, to_string_view(format_str), vargs)
 
             : detail::vprint_mojibake(f, to_string_view(format_str), vargs);
 
template <typename... T>
 
FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
 
  const auto& vargs = fmt::make_format_args(args...);
 
  return detail::is_utf8() ? vprint(f, fmt, vargs)
 
                           : detail::vprint_mojibake(f, fmt, vargs);
 
}
 

	
 
/**
 
  Formats ``args`` according to specifications in ``fmt`` and writes the
 
  output to the file ``f`` followed by a newline.
 
 */
 
template <typename... T>
 
FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
 
  return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
 
}
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``format_str`` and writes
 
  the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
 
  the ``FMT_UNICODE`` macro is set to 0.
 

	
 
  **Example**::
 

	
 
    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
 
  \endrst
 
  Formats ``args`` according to specifications in ``fmt`` and writes the output
 
  to ``stdout`` followed by a newline.
 
 */
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
inline void print(const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::is_unicode<Char>()
 
             ? vprint(to_string_view(format_str), vargs)
 
             : detail::vprint_mojibake(stdout, to_string_view(format_str),
 
                                       vargs);
 
template <typename... T>
 
FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
 
  return fmt::println(stdout, fmt, std::forward<T>(args)...);
 
}
 

	
 
FMT_END_EXPORT
 
FMT_GCC_PRAGMA("GCC pop_options")
 
FMT_END_NAMESPACE
 

	
 
#ifdef FMT_HEADER_ONLY
 
#  include "format.h"
 
#endif
 
#endif  // FMT_CORE_H_
src/3rdparty/fmt/format-inl.h
Show inline comments
 
@@ -8,13 +8,10 @@
 
#ifndef FMT_FORMAT_INL_H_
 
#define FMT_FORMAT_INL_H_
 

	
 
/* Do not include cassert as that breaks our own asserts. */
 
#include <cctype>
 
#include <algorithm>
 
#include <cerrno>  // errno
 
#include <climits>
 
#include <cmath>
 
#include <cstdarg>
 
#include <cstring>  // std::memmove
 
#include <cwchar>
 
#include <exception>
 

	
 
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 
@@ -27,11 +24,6 @@
 

	
 
#include "format.h"
 

	
 
// Dummy implementations of strerror_r and strerror_s called if corresponding
 
// system functions are not available.
 
inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
 
inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
 

	
 
FMT_BEGIN_NAMESPACE
 
namespace detail {
 

	
 
@@ -44,91 +36,12 @@ FMT_FUNC void assert_fail(const char* fi
 
  std::terminate();
 
}
 

	
 
#ifndef _MSC_VER
 
#  define FMT_SNPRINTF snprintf
 
#else  // _MSC_VER
 
inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
 
  va_list args;
 
  va_start(args, format);
 
  int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
 
  va_end(args);
 
  return result;
 
}
 
#  define FMT_SNPRINTF fmt_snprintf
 
#endif  // _MSC_VER
 

	
 
// A portable thread-safe version of strerror.
 
// Sets buffer to point to a string describing the error code.
 
// This can be either a pointer to a string stored in buffer,
 
// or a pointer to some static immutable string.
 
// Returns one of the following values:
 
//   0      - success
 
//   ERANGE - buffer is not large enough to store the error message
 
//   other  - failure
 
// Buffer should be at least of size 1.
 
inline int safe_strerror(int error_code, char*& buffer,
 
                         size_t buffer_size) FMT_NOEXCEPT {
 
  FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
 

	
 
  class dispatcher {
 
   private:
 
    int error_code_;
 
    char*& buffer_;
 
    size_t buffer_size_;
 

	
 
    // A noop assignment operator to avoid bogus warnings.
 
    void operator=(const dispatcher&) {}
 

	
 
    // Handle the result of XSI-compliant version of strerror_r.
 
    int handle(int result) {
 
      // glibc versions before 2.13 return result in errno.
 
      return result == -1 ? errno : result;
 
    }
 

	
 
    // Handle the result of GNU-specific version of strerror_r.
 
    FMT_MAYBE_UNUSED
 
    int handle(char* message) {
 
      // If the buffer is full then the message is probably truncated.
 
      if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
 
        return ERANGE;
 
      buffer_ = message;
 
      return 0;
 
    }
 

	
 
    // Handle the case when strerror_r is not available.
 
    FMT_MAYBE_UNUSED
 
    int handle(detail::null<>) {
 
      return fallback(strerror_s(buffer_, buffer_size_, error_code_));
 
    }
 

	
 
    // Fallback to strerror_s when strerror_r is not available.
 
    FMT_MAYBE_UNUSED
 
    int fallback(int result) {
 
      // If the buffer is full then the message is probably truncated.
 
      return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
 
                                                                : result;
 
    }
 

	
 
#if !FMT_MSC_VER
 
    // Fallback to strerror if strerror_r and strerror_s are not available.
 
    int fallback(detail::null<>) {
 
      errno = 0;
 
      buffer_ = strerror(error_code_);
 
      return errno;
 
    }
 
#endif
 

	
 
   public:
 
    dispatcher(int err_code, char*& buf, size_t buf_size)
 
        : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
 

	
 
    int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
 
  };
 
  return dispatcher(error_code, buffer, buffer_size).run();
 
FMT_FUNC void throw_format_error(const char* message) {
 
  FMT_THROW(format_error(message));
 
}
 

	
 
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
 
                                string_view message) FMT_NOEXCEPT {
 
                                string_view message) noexcept {
 
  // Report error code making sure that the output fits into
 
  // inline_buffer_size to avoid dynamic memory allocation and potential
 
  // bad_alloc.
 
@@ -145,17 +58,17 @@ FMT_FUNC void format_error_code(detail::
 
  error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
 
  auto it = buffer_appender<char>(out);
 
  if (message.size() <= inline_buffer_size - error_code_size)
 
    format_to(it, "{}{}", message, SEP);
 
  format_to(it, "{}{}", ERROR_STR, error_code);
 
  assert(out.size() <= inline_buffer_size);
 
    format_to(it, FMT_STRING("{}{}"), message, SEP);
 
  format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
 
  FMT_ASSERT(out.size() <= inline_buffer_size, "");
 
}
 

	
 
FMT_FUNC void report_error(format_func func, int error_code,
 
                           string_view message) FMT_NOEXCEPT {
 
                           const char* message) noexcept {
 
  memory_buffer full_message;
 
  func(full_message, error_code, message);
 
  // Don't use fwrite_fully because the latter may throw.
 
  (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
 
  if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
 
  std::fputc('\n', stderr);
 
}
 

	
 
@@ -163,13 +76,11 @@ FMT_FUNC void report_error(format_func f
 
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
 
                         FILE* stream) {
 
  size_t written = std::fwrite(ptr, size, count, stream);
 
  if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
 
  if (written < count)
 
    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 
}
 
}  // namespace detail
 

	
 
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
 
namespace detail {
 

	
 
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 
template <typename Locale>
 
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
 
  static_assert(std::is_same<Locale, std::locale>::value, "");
 
@@ -180,189 +91,174 @@ template <typename Locale> Locale locale
 
  return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
 
}
 

	
 
template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
 
  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
 
}
 
template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
 
  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
 
      .thousands_sep();
 
template <typename Char>
 
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
 
  auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
 
  auto grouping = facet.grouping();
 
  auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
 
  return {std::move(grouping), thousands_sep};
 
}
 
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
 
  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
 
      .decimal_point();
 
}
 
}  // namespace detail
 
#else
 
template <typename Char>
 
FMT_FUNC std::string detail::grouping_impl(locale_ref) {
 
  return "\03";
 
FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
 
  return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
 
}
 
template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
 
  return FMT_STATIC_THOUSANDS_SEPARATOR;
 
}
 
template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
 
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
 
  return '.';
 
}
 
#endif
 

	
 
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
 
FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
 
FMT_FUNC auto write_loc(appender out, loc_value value,
 
                        const format_specs<>& specs, locale_ref loc) -> bool {
 
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 
  auto locale = loc.get<std::locale>();
 
  // We cannot use the num_put<char> facet because it may produce output in
 
  // a wrong encoding.
 
  using facet = format_facet<std::locale>;
 
  if (std::has_facet<facet>(locale))
 
    return std::use_facet<facet>(locale).put(out, value, specs);
 
  return facet(locale).put(out, value, specs);
 
#endif
 
  return false;
 
}
 
}  // namespace detail
 

	
 
template <typename Locale> typename Locale::id format_facet<Locale>::id;
 

	
 
FMT_FUNC void system_error::init(int err_code, string_view format_str,
 
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
 
  auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
 
  grouping_ = numpunct.grouping();
 
  if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
 
}
 

	
 
template <>
 
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
 
    appender out, loc_value val, const format_specs<>& specs) const -> bool {
 
  return val.visit(
 
      detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
 
}
 
#endif
 

	
 
FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
 
                                 format_args args) {
 
  error_code_ = err_code;
 
  memory_buffer buffer;
 
  format_system_error(buffer, err_code, vformat(format_str, args));
 
  std::runtime_error& base = *this;
 
  base = std::runtime_error(to_string(buffer));
 
  auto ec = std::error_code(error_code, std::generic_category());
 
  return std::system_error(ec, vformat(fmt, args));
 
}
 

	
 
namespace detail {
 

	
 
template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
 
  // fallback_uintptr is always stored in little endian.
 
  int i = static_cast<int>(sizeof(void*)) - 1;
 
  while (i > 0 && n.value[i] == 0) --i;
 
  auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
 
  return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
 
template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
 
  return x.f == y.f && x.e == y.e;
 
}
 

	
 
// Compilers should be able to optimize this into the ror instruction.
 
FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
 
  r &= 31;
 
  return (n >> r) | (n << (32 - r));
 
}
 
FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
 
  r &= 63;
 
  return (n >> r) | (n << (64 - r));
 
}
 

	
 
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
 
namespace dragonbox {
 
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
 
// 64-bit unsigned integer.
 
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
 
  return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
 
}
 

	
 
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
 
// 128-bit unsigned integer.
 
inline uint128_fallback umul192_lower128(uint64_t x,
 
                                         uint128_fallback y) noexcept {
 
  uint64_t high = x * y.high();
 
  uint128_fallback high_low = umul128(x, y.low());
 
  return {high + high_low.high(), high_low.low()};
 
}
 

	
 
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
 
// 64-bit unsigned integer.
 
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
 
  return x * y;
 
}
 

	
 
// Various fast log computations.
 
inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
 
  FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
 
  return (e * 631305 - 261663) >> 21;
 
}
 

	
 
template <typename T>
 
const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
 
    {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
 
    {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
 
    {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
 
    {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
 
    {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
 
    {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
 
    {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
 
    {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
 
    {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
 
    {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
 
    {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
 
    {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
 
    {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
 
    {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
 
    {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
 
    {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
 
    {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
 

	
 
template <typename T>
 
const char basic_data<T>::hex_digits[] = "0123456789abcdef";
 
FMT_INLINE_VARIABLE constexpr struct {
 
  uint32_t divisor;
 
  int shift_amount;
 
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
 

	
 
#define FMT_POWERS_OF_10(factor)                                             \
 
  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
 
      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
 
      (factor)*1000000000
 

	
 
template <typename T>
 
const uint64_t basic_data<T>::powers_of_10_64[] = {
 
    1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
 
    10000000000000000000ULL};
 

	
 
template <typename T>
 
const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
 
                                                           FMT_POWERS_OF_10(1)};
 
template <typename T>
 
const uint64_t basic_data<T>::zero_or_powers_of_10_64[] = {
 
    0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
 
    10000000000000000000ULL};
 

	
 
template <typename T>
 
const uint32_t basic_data<T>::zero_or_powers_of_10_32_new[] = {
 
    0, 0, FMT_POWERS_OF_10(1)};
 

	
 
template <typename T>
 
const uint64_t basic_data<T>::zero_or_powers_of_10_64_new[] = {
 
    0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
 
    10000000000000000000ULL};
 
// Replaces n by floor(n / pow(10, N)) returning true if and only if n is
 
// divisible by pow(10, N).
 
// Precondition: n <= pow(10, N + 1).
 
template <int N>
 
bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
 
  // The numbers below are chosen such that:
 
  //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
 
  //   2. nm mod 2^k < m if and only if n is divisible by d,
 
  // where m is magic_number, k is shift_amount
 
  // and d is divisor.
 
  //
 
  // Item 1 is a common technique of replacing division by a constant with
 
  // multiplication, see e.g. "Division by Invariant Integers Using
 
  // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
 
  // to ceil(2^k/d) for large enough k.
 
  // The idea for item 2 originates from Schubfach.
 
  constexpr auto info = div_small_pow10_infos[N - 1];
 
  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
 
  constexpr uint32_t magic_number =
 
      (1u << info.shift_amount) / info.divisor + 1;
 
  n *= magic_number;
 
  const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
 
  bool result = (n & comparison_mask) < magic_number;
 
  n >>= info.shift_amount;
 
  return result;
 
}
 

	
 
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
 
// These are generated by support/compute-powers.py.
 
template <typename T>
 
const uint64_t basic_data<T>::grisu_pow10_significands[] = {
 
    0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
 
    0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
 
    0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
 
    0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
 
    0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
 
    0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
 
    0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
 
    0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
 
    0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
 
    0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
 
    0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
 
    0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
 
    0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
 
    0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
 
    0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
 
    0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
 
    0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
 
    0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
 
    0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
 
    0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
 
    0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
 
    0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
 
    0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
 
    0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
 
    0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
 
    0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
 
    0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
 
    0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
 
    0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
 
};
 
// Computes floor(n / pow(10, N)) for small n and N.
 
// Precondition: n <= pow(10, N + 1).
 
template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
 
  constexpr auto info = div_small_pow10_infos[N - 1];
 
  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
 
  constexpr uint32_t magic_number =
 
      (1u << info.shift_amount) / info.divisor + 1;
 
  return (n * magic_number) >> info.shift_amount;
 
}
 

	
 
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
 
// to significands above.
 
template <typename T>
 
const int16_t basic_data<T>::grisu_pow10_exponents[] = {
 
    -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
 
    -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
 
    -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
 
    -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
 
    -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
 
    242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
 
    534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
 
    827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
 

	
 
template <typename T>
 
const divtest_table_entry<uint32_t> basic_data<T>::divtest_table_for_pow5_32[] =
 
    {{0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333},
 
     {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba},
 
     {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5},
 
     {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf},
 
     {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897},
 
     {0x3ed61f49, 0x000001b7}};
 
// Computes floor(n / 10^(kappa + 1)) (float)
 
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
 
  // 1374389535 = ceil(2^37/100)
 
  return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
 
}
 
// Computes floor(n / 10^(kappa + 1)) (double)
 
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
 
  // 2361183241434822607 = ceil(2^(64+7)/1000)
 
  return umul128_upper64(n, 2361183241434822607ull) >> 7;
 
}
 

	
 
template <typename T>
 
const divtest_table_entry<uint64_t> basic_data<T>::divtest_table_for_pow5_64[] =
 
    {{0x0000000000000001, 0xffffffffffffffff},
 
     {0xcccccccccccccccd, 0x3333333333333333},
 
     {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70},
 
     {0x1cac083126e978d5, 0x020c49ba5e353f7c},
 
     {0xd288ce703afb7e91, 0x0068db8bac710cb2},
 
     {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0},
 
     {0x790fb65668c26139, 0x000431bde82d7b63},
 
     {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a},
 
     {0xc767074b22e90e21, 0x00002af31dc46118},
 
     {0x8e47ce423a2e9c6d, 0x0000089705f4136b},
 
     {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b},
 
     {0x0fee64690c913975, 0x00000057f5ff85e5},
 
     {0x3662e0e1cf503eb1, 0x000000119799812d},
 
     {0xa47a2cf9f6433fbd, 0x0000000384b84d09},
 
     {0x54186f653140a659, 0x00000000b424dc35},
 
     {0x7738164770402145, 0x0000000024075f3d},
 
     {0xe4a4d1417cd9a041, 0x000000000734aca5},
 
     {0xc75429d9e5c5200d, 0x000000000170ef54},
 
     {0xc1773b91fac10669, 0x000000000049c977},
 
     {0x26b172506559ce15, 0x00000000000ec1e4},
 
     {0xd489e3a9addec2d1, 0x000000000002f394},
 
     {0x90e860bb892c8d5d, 0x000000000000971d},
 
     {0x502e79bf1b6f4f79, 0x0000000000001e39},
 
     {0xdcd618596be30fe5, 0x000000000000060b}};
 
// Various subroutines using pow10 cache
 
template <typename T> struct cache_accessor;
 

	
 
template <typename T>
 
const uint64_t basic_data<T>::dragonbox_pow10_significands_64[] = {
 
template <> struct cache_accessor<float> {
 
  using carrier_uint = float_info<float>::carrier_uint;
 
  using cache_entry_type = uint64_t;
 

	
 
  static uint64_t get_cached_power(int k) noexcept {
 
    FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
 
               "k is out of range");
 
    static constexpr const uint64_t pow10_significands[] = {
 
    0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
 
    0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
 
    0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
 
@@ -382,16 +278,79 @@ const uint64_t basic_data<T>::dragonbox_
 
    0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
 
    0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
 
    0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
 
    0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984,
 
    0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296,
 
    0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6,
 
    0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20,
 
    0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd,
 
    0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719,
 
    0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e};
 
        0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
 
        0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297,
 
        0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7,
 
        0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21,
 
        0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe,
 
        0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
 
        0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
 
    return pow10_significands[k - float_info<float>::min_k];
 
  }
 

	
 
  struct compute_mul_result {
 
    carrier_uint result;
 
    bool is_integer;
 
  };
 
  struct compute_mul_parity_result {
 
    bool parity;
 
    bool is_integer;
 
  };
 

	
 
  static compute_mul_result compute_mul(
 
      carrier_uint u, const cache_entry_type& cache) noexcept {
 
    auto r = umul96_upper64(u, cache);
 
    return {static_cast<carrier_uint>(r >> 32),
 
            static_cast<carrier_uint>(r) == 0};
 
  }
 

	
 
  static uint32_t compute_delta(const cache_entry_type& cache,
 
                                int beta) noexcept {
 
    return static_cast<uint32_t>(cache >> (64 - 1 - beta));
 
  }
 

	
 
  static compute_mul_parity_result compute_mul_parity(
 
      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
 
    FMT_ASSERT(beta >= 1, "");
 
    FMT_ASSERT(beta < 64, "");
 

	
 
template <typename T>
 
const uint128_wrapper basic_data<T>::dragonbox_pow10_significands_128[] = {
 
    auto r = umul96_lower64(two_f, cache);
 
    return {((r >> (64 - beta)) & 1) != 0,
 
            static_cast<uint32_t>(r >> (32 - beta)) == 0};
 
  }
 

	
 
  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return static_cast<carrier_uint>(
 
        (cache - (cache >> (num_significand_bits<float>() + 2))) >>
 
        (64 - num_significand_bits<float>() - 1 - beta));
 
  }
 

	
 
  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return static_cast<carrier_uint>(
 
        (cache + (cache >> (num_significand_bits<float>() + 1))) >>
 
        (64 - num_significand_bits<float>() - 1 - beta));
 
  }
 

	
 
  static carrier_uint compute_round_up_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return (static_cast<carrier_uint>(
 
                cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
 
            1) /
 
           2;
 
  }
 
};
 

	
 
template <> struct cache_accessor<double> {
 
  using carrier_uint = float_info<double>::carrier_uint;
 
  using cache_entry_type = uint128_fallback;
 

	
 
  static uint128_fallback get_cached_power(int k) noexcept {
 
    FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
 
               "k is out of range");
 

	
 
    static constexpr const uint128_fallback pow10_significands[] = {
 
#if FMT_USE_FULL_CACHE_DRAGONBOX
 
    {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
 
    {0x9faacf3df73609b1, 0x77b191618c54e9ad},
 
@@ -741,277 +700,293 @@ const uint128_wrapper basic_data<T>::dra
 
    {0x85a36366eb71f041, 0x47a6da2b7f864750},
 
    {0xa70c3c40a64e6c51, 0x999090b65f67d924},
 
    {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
 
    {0x82818f1281ed449f, 0xbff8f10e7a8921a4},
 
    {0xa321f2d7226895c7, 0xaff72d52192b6a0d},
 
    {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490},
 
    {0xfee50b7025c36a08, 0x02f236d04753d5b4},
 
    {0x9f4f2726179a2245, 0x01d762422c946590},
 
    {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5},
 
    {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2},
 
    {0x9b934c3b330c8577, 0x63cc55f49f88eb2f},
 
    {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb},
 
    {0xf316271c7fc3908a, 0x8bef464e3945ef7a},
 
    {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac},
 
    {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317},
 
    {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd},
 
    {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a},
 
    {0xb975d6b6ee39e436, 0xb3e2fd538e122b44},
 
    {0xe7d34c64a9c85d44, 0x60dbbca87196b616},
 
    {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd},
 
    {0xb51d13aea4a488dd, 0x6babab6398bdbe41},
 
    {0xe264589a4dcdab14, 0xc696963c7eed2dd1},
 
    {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2},
 
    {0xb0de65388cc8ada8, 0x3b25a55f43294bcb},
 
    {0xdd15fe86affad912, 0x49ef0eb713f39ebe},
 
    {0x8a2dbf142dfcc7ab, 0x6e3569326c784337},
 
    {0xacb92ed9397bf996, 0x49c2c37f07965404},
 
    {0xd7e77a8f87daf7fb, 0xdc33745ec97be906},
 
    {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3},
 
    {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c},
 
    {0xd2d80db02aabd62b, 0xf50a3fa490c30190},
 
    {0x83c7088e1aab65db, 0x792667c6da79e0fa},
 
    {0xa4b8cab1a1563f52, 0x577001b891185938},
 
    {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
 
    {0x80b05e5ac60b6178, 0x544f8158315b05b4},
 
    {0xa0dc75f1778e39d6, 0x696361ae3db1c721},
 
    {0xc913936dd571c84c, 0x03bc3a19cd1e38e9},
 
    {0xfb5878494ace3a5f, 0x04ab48a04065c723},
 
    {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76},
 
    {0xc45d1df942711d9a, 0x3ba5d0bd324f8394},
 
    {0xf5746577930d6500, 0xca8f44ec7ee36479},
 
    {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb},
 
    {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e},
 
    {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e},
 
    {0x95d04aee3b80ece5, 0xbba1f1d158724a12},
 
    {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97},
 
    {0xea1575143cf97226, 0xf52d09d71a3293bd},
 
    {0x924d692ca61be758, 0x593c2626705f9c56},
 
    {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c},
 
    {0xe498f455c38b997a, 0x0b6dfb9c0f956447},
 
    {0x8edf98b59a373fec, 0x4724bd4189bd5eac},
 
    {0xb2977ee300c50fe7, 0x58edec91ec2cb657},
 
    {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed},
 
    {0x8b865b215899f46c, 0xbd79e0d20082ee74},
 
    {0xae67f1e9aec07187, 0xecd8590680a3aa11},
 
    {0xda01ee641a708de9, 0xe80e6f4820cc9495},
 
    {0x884134fe908658b2, 0x3109058d147fdcdd},
 
    {0xaa51823e34a7eede, 0xbd4b46f0599fd415},
 
    {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a},
 
    {0x850fadc09923329e, 0x03e2cf6bc604ddb0},
 
    {0xa6539930bf6bff45, 0x84db8346b786151c},
 
    {0xcfe87f7cef46ff16, 0xe612641865679a63},
 
    {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e},
 
    {0xa26da3999aef7749, 0xe3be5e330f38f09d},
 
    {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5},
 
    {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6},
 
    {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa},
 
    {0xc646d63501a1511d, 0xb281e1fd541501b8},
 
    {0xf7d88bc24209a565, 0x1f225a7ca91a4226},
 
    {0x9ae757596946075f, 0x3375788de9b06958},
 
    {0xc1a12d2fc3978937, 0x0052d6b1641c83ae},
 
    {0xf209787bb47d6b84, 0xc0678c5dbd23a49a},
 
    {0x9745eb4d50ce6332, 0xf840b7ba963646e0},
 
    {0xbd176620a501fbff, 0xb650e5a93bc3d898},
 
    {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe},
 
    {0x93ba47c980e98cdf, 0xc66f336c36b10137},
 
    {0xb8a8d9bbe123f017, 0xb80b0047445d4184},
 
    {0xe6d3102ad96cec1d, 0xa60dc059157491e5},
 
    {0x9043ea1ac7e41392, 0x87c89837ad68db2f},
 
    {0xb454e4a179dd1877, 0x29babe4598c311fb},
 
    {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a},
 
    {0x8ce2529e2734bb1d, 0x1899e4a65f58660c},
 
    {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f},
 
    {0xdc21a1171d42645d, 0x76707543f4fa1f73},
 
    {0x899504ae72497eba, 0x6a06494a791c53a8},
 
    {0xabfa45da0edbde69, 0x0487db9d17636892},
 
    {0xd6f8d7509292d603, 0x45a9d2845d3c42b6},
 
    {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
 
    {0xa7f26836f282b732, 0x8e6cac7768d7141e},
 
    {0xd1ef0244af2364ff, 0x3207d795430cd926},
 
    {0x8335616aed761f1f, 0x7f44e6bd49e807b8},
 
    {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6},
 
    {0xcd036837130890a1, 0x36dba887c37a8c0f},
 
    {0x802221226be55a64, 0xc2494954da2c9789},
 
    {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c},
 
    {0xc83553c5c8965d3d, 0x6f92829494e5acc7},
 
    {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9},
 
    {0x9c69a97284b578d7, 0xff2a760414536efb},
 
    {0xc38413cf25e2d70d, 0xfef5138519684aba},
 
    {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69},
 
    {0x98bf2f79d5993802, 0xef2f773ffbd97a61},
 
    {0xbeeefb584aff8603, 0xaafb550ffacfd8fa},
 
    {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38},
 
    {0x952ab45cfa97a0b2, 0xdd945a747bf26183},
 
    {0xba756174393d88df, 0x94f971119aeef9e4},
 
    {0xe912b9d1478ceb17, 0x7a37cd5601aab85d},
 
    {0x91abb422ccb812ee, 0xac62e055c10ab33a},
 
    {0xb616a12b7fe617aa, 0x577b986b314d6009},
 
    {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b},
 
    {0x8e41ade9fbebc27d, 0x14588f13be847307},
 
    {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8},
 
    {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb},
 
    {0x8aec23d680043bee, 0x25de7bb9480d5854},
 
    {0xada72ccc20054ae9, 0xaf561aa79a10ae6a},
 
    {0xd910f7ff28069da4, 0x1b2ba1518094da04},
 
    {0x87aa9aff79042286, 0x90fb44d2f05d0842},
 
    {0xa99541bf57452b28, 0x353a1607ac744a53},
 
    {0xd3fa922f2d1675f2, 0x42889b8997915ce8},
 
    {0x847c9b5d7c2e09b7, 0x69956135febada11},
 
    {0xa59bc234db398c25, 0x43fab9837e699095},
 
    {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb},
 
    {0x8161afb94b44f57d, 0x1d1be0eebac278f5},
 
    {0xa1ba1ba79e1632dc, 0x6462d92a69731732},
 
    {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe},
 
    {0xfcb2cb35e702af78, 0x5cda735244c3d43e},
 
    {0x9defbf01b061adab, 0x3a0888136afa64a7},
 
    {0xc56baec21c7a1916, 0x088aaa1845b8fdd0},
 
    {0xf6c69a72a3989f5b, 0x8aad549e57273d45},
 
    {0x9a3c2087a63f6399, 0x36ac54e2f678864b},
 
    {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd},
 
    {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5},
 
    {0x969eb7c47859e743, 0x9f644ae5a4b1b325},
 
    {0xbc4665b596706114, 0x873d5d9f0dde1fee},
 
    {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea},
 
    {0x9316ff75dd87cbd8, 0x09a7f12442d588f2},
 
    {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f},
 
    {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa},
 
    {0x8fa475791a569d10, 0xf96e017d694487bc},
 
    {0xb38d92d760ec4455, 0x37c981dcc395a9ac},
 
    {0xe070f78d3927556a, 0x85bbe253f47b1417},
 
    {0x8c469ab843b89562, 0x93956d7478ccec8e},
 
    {0xaf58416654a6babb, 0x387ac8d1970027b2},
 
    {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e},
 
    {0x88fcf317f22241e2, 0x441fece3bdf81f03},
 
    {0xab3c2fddeeaad25a, 0xd527e81cad7626c3},
 
    {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074},
 
    {0x85c7056562757456, 0xf6872d5667844e49},
 
    {0xa738c6bebb12d16c, 0xb428f8ac016561db},
 
    {0xd106f86e69d785c7, 0xe13336d701beba52},
 
    {0x82a45b450226b39c, 0xecc0024661173473},
 
    {0xa34d721642b06084, 0x27f002d7f95d0190},
 
    {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4},
 
    {0xff290242c83396ce, 0x7e67047175a15271},
 
    {0x9f79a169bd203e41, 0x0f0062c6e984d386},
 
    {0xc75809c42c684dd1, 0x52c07b78a3e60868},
 
    {0xf92e0c3537826145, 0xa7709a56ccdf8a82},
 
    {0x9bbcc7a142b17ccb, 0x88a66076400bb691},
 
    {0xc2abf989935ddbfe, 0x6acff893d00ea435},
 
    {0xf356f7ebf83552fe, 0x0583f6b8c4124d43},
 
    {0x98165af37b2153de, 0xc3727a337a8b704a},
 
    {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c},
 
    {0xeda2ee1c7064130c, 0x1162def06f79df73},
 
    {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8},
 
    {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692},
 
    {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437},
 
    {0x910ab1d4db9914a0, 0x1d9c9892400a22a2},
 
    {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b},
 
    {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d},
 
    {0x8da471a9de737e24, 0x5ceaecfed289e5d2},
 
    {0xb10d8e1456105dad, 0x7425a83e872c5f47},
 
    {0xdd50f1996b947518, 0xd12f124e28f77719},
 
    {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f},
 
    {0xace73cbfdc0bfb7b, 0x636cc64d1001550b},
 
    {0xd8210befd30efa5a, 0x3c47f7e05401aa4e},
 
    {0x8714a775e3e95c78, 0x65acfaec34810a71},
 
    {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d},
 
    {0xd31045a8341ca07c, 0x1ede48111209a050},
 
    {0x83ea2b892091e44d, 0x934aed0aab460432},
 
    {0xa4e4b66b68b65d60, 0xf81da84d5617853f},
 
    {0xce1de40642e3f4b9, 0x36251260ab9d668e},
 
    {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019},
 
    {0xa1075a24e4421730, 0xb24cf65b8612f81f},
 
    {0xc94930ae1d529cfc, 0xdee033f26797b627},
 
    {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1},
 
    {0x9d412e0806e88aa5, 0x8e1f289560ee864e},
 
    {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2},
 
    {0xf5b5d7ec8acb58a2, 0xae10af696774b1db},
 
    {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29},
 
    {0xbff610b0cc6edd3f, 0x17fd090a58d32af3},
 
    {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0},
 
    {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e},
 
    {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1},
 
    {0xea53df5fd18d5513, 0x84c86189216dc5ed},
 
    {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4},
 
    {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1},
 
    {0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
 
    {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e},
 
    {0xb2c71d5bca9023f8, 0x743e20e9ef511012},
 
    {0xdf78e4b2bd342cf6, 0x914da9246b255416},
 
    {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e},
 
    {0xae9672aba3d0c320, 0xa184ac2473b529b1},
 
    {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e},
 
    {0x8865899617fb1871, 0x7e2fa67c7a658892},
 
    {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7},
 
    {0xd51ea6fa85785631, 0x552a74227f3ea565},
 
    {0x8533285c936b35de, 0xd53a88958f87275f},
 
    {0xa67ff273b8460356, 0x8a892abaf368f137},
 
    {0xd01fef10a657842c, 0x2d2b7569b0432d85},
 
    {0x8213f56a67f6b29b, 0x9c3b29620e29fc73},
 
    {0xa298f2c501f45f42, 0x8349f3ba91b47b8f},
 
    {0xcb3f2f7642717713, 0x241c70a936219a73},
 
    {0xfe0efb53d30dd4d7, 0xed238cd383aa0110},
 
    {0x9ec95d1463e8a506, 0xf4363804324a40aa},
 
    {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5},
 
    {0xf81aa16fdc1b81da, 0xdd94b7868e94050a},
 
    {0x9b10a4e5e9913128, 0xca7cf2b4191c8326},
 
    {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0},
 
    {0xf24a01a73cf2dccf, 0xbc633b39673c8cec},
 
    {0x976e41088617ca01, 0xd5be0503e085d813},
 
    {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18},
 
    {0xec9c459d51852ba2, 0xddf8e7d60ed1219e},
 
    {0x93e1ab8252f33b45, 0xcabb90e5c942b503},
 
    {0xb8da1662e7b00a17, 0x3d6a751f3b936243},
 
    {0xe7109bfba19c0c9d, 0x0cc512670a783ad4},
 
    {0x906a617d450187e2, 0x27fb2b80668b24c5},
 
    {0xb484f9dc9641e9da, 0xb1f9f660802dedf6},
 
    {0xe1a63853bbd26451, 0x5e7873f8a0396973},
 
    {0x8d07e33455637eb2, 0xdb0b487b6423e1e8},
 
    {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62},
 
    {0xdc5c5301c56b75f7, 0x7641a140cc7810fb},
 
    {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d},
 
    {0xac2820d9623bf429, 0x546345fa9fbdcd44},
 
    {0xd732290fbacaf133, 0xa97c177947ad4095},
 
    {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d},
 
    {0xa81f301449ee8c70, 0x5c68f256bfff5a74},
 
    {0xd226fc195c6a2f8c, 0x73832eec6fff3111},
 
    {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab},
 
    {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55},
 
    {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb},
 
    {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3},
 
    {0xa0555e361951c366, 0xd7e105bcc332621f},
 
    {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7},
 
    {0xfa856334878fc150, 0xb14f98f6f0feb951},
 
    {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3},
 
    {0xc3b8358109e84f07, 0x0a862f80ec4700c8},
 
    {0xf4a642e14c6262c8, 0xcd27bb612758c0fa},
 
    {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c},
 
    {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3},
 
    {0xeeea5d5004981478, 0x1858ccfce06cac74},
 
    {0x95527a5202df0ccb, 0x0f37801e0c43ebc8},
 
    {0xbaa718e68396cffd, 0xd30560258f54e6ba},
 
    {0xe950df20247c83fd, 0x47c6b82ef32a2069},
 
    {0x91d28b7416cdd27e, 0x4cdc331d57fa5441},
 
    {0xb6472e511c81471d, 0xe0133fe4adf8e952},
 
    {0xe3d8f9e563a198e5, 0x58180fddd97723a6},
 
    {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648},
 
    {0xb201833b35d63f73, 0x2cd2cc6551e513da},
 
    {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1},
 
    {0x8b112e86420f6191, 0xfb04afaf27faf782},
 
    {0xadd57a27d29339f6, 0x79c5db9af1f9b563},
 
    {0xd94ad8b1c7380874, 0x18375281ae7822bc},
 
    {0x87cec76f1c830548, 0x8f2293910d0b15b5},
 
    {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22},
 
    {0xd433179d9c8cb841, 0x5fa60692a46151eb},
 
    {0x849feec281d7f328, 0xdbc7c41ba6bcd333},
 
    {0xa5c7ea73224deff3, 0x12b9b522906c0800},
 
    {0xcf39e50feae16bef, 0xd768226b34870a00},
 
    {0x81842f29f2cce375, 0xe6a1158300d46640},
 
    {0xa1e53af46f801c53, 0x60495ae3c1097fd0},
 
    {0xca5e89b18b602368, 0x385bb19cb14bdfc4},
 
    {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5},
 
    {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1},
 
    {0xc5a05277621be293, 0xc7098b7305241885},
 
    {0xf70867153aa2db38, 0xb8cbee4fc66d1ea7}
 
      {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
 
      {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
 
      {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
 
      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
 
      {0x9f4f2726179a2245, 0x01d762422c946591},
 
      {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
 
      {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
 
      {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
 
      {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
 
      {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
 
      {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
 
      {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
 
      {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
 
      {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
 
      {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
 
      {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
 
      {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
 
      {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
 
      {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
 
      {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
 
      {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
 
      {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
 
      {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
 
      {0xacb92ed9397bf996, 0x49c2c37f07965405},
 
      {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
 
      {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
 
      {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
 
      {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
 
      {0x83c7088e1aab65db, 0x792667c6da79e0fb},
 
      {0xa4b8cab1a1563f52, 0x577001b891185939},
 
      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
 
      {0x80b05e5ac60b6178, 0x544f8158315b05b5},
 
      {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
 
      {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
 
      {0xfb5878494ace3a5f, 0x04ab48a04065c724},
 
      {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
 
      {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
 
      {0xf5746577930d6500, 0xca8f44ec7ee3647a},
 
      {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
 
      {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
 
      {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
 
      {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
 
      {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
 
      {0xea1575143cf97226, 0xf52d09d71a3293be},
 
      {0x924d692ca61be758, 0x593c2626705f9c57},
 
      {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
 
      {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
 
      {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
 
      {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
 
      {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
 
      {0x8b865b215899f46c, 0xbd79e0d20082ee75},
 
      {0xae67f1e9aec07187, 0xecd8590680a3aa12},
 
      {0xda01ee641a708de9, 0xe80e6f4820cc9496},
 
      {0x884134fe908658b2, 0x3109058d147fdcde},
 
      {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
 
      {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
 
      {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
 
      {0xa6539930bf6bff45, 0x84db8346b786151d},
 
      {0xcfe87f7cef46ff16, 0xe612641865679a64},
 
      {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
 
      {0xa26da3999aef7749, 0xe3be5e330f38f09e},
 
      {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
 
      {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
 
      {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
 
      {0xc646d63501a1511d, 0xb281e1fd541501b9},
 
      {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
 
      {0x9ae757596946075f, 0x3375788de9b06959},
 
      {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
 
      {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
 
      {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
 
      {0xbd176620a501fbff, 0xb650e5a93bc3d899},
 
      {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
 
      {0x93ba47c980e98cdf, 0xc66f336c36b10138},
 
      {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
 
      {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
 
      {0x9043ea1ac7e41392, 0x87c89837ad68db30},
 
      {0xb454e4a179dd1877, 0x29babe4598c311fc},
 
      {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
 
      {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
 
      {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
 
      {0xdc21a1171d42645d, 0x76707543f4fa1f74},
 
      {0x899504ae72497eba, 0x6a06494a791c53a9},
 
      {0xabfa45da0edbde69, 0x0487db9d17636893},
 
      {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
 
      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
 
      {0xa7f26836f282b732, 0x8e6cac7768d7141f},
 
      {0xd1ef0244af2364ff, 0x3207d795430cd927},
 
      {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
 
      {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
 
      {0xcd036837130890a1, 0x36dba887c37a8c10},
 
      {0x802221226be55a64, 0xc2494954da2c978a},
 
      {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
 
      {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
 
      {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
 
      {0x9c69a97284b578d7, 0xff2a760414536efc},
 
      {0xc38413cf25e2d70d, 0xfef5138519684abb},
 
      {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
 
      {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
 
      {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
 
      {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
 
      {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
 
      {0xba756174393d88df, 0x94f971119aeef9e5},
 
      {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
 
      {0x91abb422ccb812ee, 0xac62e055c10ab33b},
 
      {0xb616a12b7fe617aa, 0x577b986b314d600a},
 
      {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
 
      {0x8e41ade9fbebc27d, 0x14588f13be847308},
 
      {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
 
      {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
 
      {0x8aec23d680043bee, 0x25de7bb9480d5855},
 
      {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
 
      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
 
      {0x87aa9aff79042286, 0x90fb44d2f05d0843},
 
      {0xa99541bf57452b28, 0x353a1607ac744a54},
 
      {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
 
      {0x847c9b5d7c2e09b7, 0x69956135febada12},
 
      {0xa59bc234db398c25, 0x43fab9837e699096},
 
      {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
 
      {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
 
      {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
 
      {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
 
      {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
 
      {0x9defbf01b061adab, 0x3a0888136afa64a8},
 
      {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
 
      {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
 
      {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
 
      {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
 
      {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
 
      {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
 
      {0xbc4665b596706114, 0x873d5d9f0dde1fef},
 
      {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
 
      {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
 
      {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
 
      {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
 
      {0x8fa475791a569d10, 0xf96e017d694487bd},
 
      {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
 
      {0xe070f78d3927556a, 0x85bbe253f47b1418},
 
      {0x8c469ab843b89562, 0x93956d7478ccec8f},
 
      {0xaf58416654a6babb, 0x387ac8d1970027b3},
 
      {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
 
      {0x88fcf317f22241e2, 0x441fece3bdf81f04},
 
      {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
 
      {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
 
      {0x85c7056562757456, 0xf6872d5667844e4a},
 
      {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
 
      {0xd106f86e69d785c7, 0xe13336d701beba53},
 
      {0x82a45b450226b39c, 0xecc0024661173474},
 
      {0xa34d721642b06084, 0x27f002d7f95d0191},
 
      {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
 
      {0xff290242c83396ce, 0x7e67047175a15272},
 
      {0x9f79a169bd203e41, 0x0f0062c6e984d387},
 
      {0xc75809c42c684dd1, 0x52c07b78a3e60869},
 
      {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
 
      {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
 
      {0xc2abf989935ddbfe, 0x6acff893d00ea436},
 
      {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
 
      {0x98165af37b2153de, 0xc3727a337a8b704b},
 
      {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
 
      {0xeda2ee1c7064130c, 0x1162def06f79df74},
 
      {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
 
      {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
 
      {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
 
      {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
 
      {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
 
      {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
 
      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
 
      {0xb10d8e1456105dad, 0x7425a83e872c5f48},
 
      {0xdd50f1996b947518, 0xd12f124e28f7771a},
 
      {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
 
      {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
 
      {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
 
      {0x8714a775e3e95c78, 0x65acfaec34810a72},
 
      {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
 
      {0xd31045a8341ca07c, 0x1ede48111209a051},
 
      {0x83ea2b892091e44d, 0x934aed0aab460433},
 
      {0xa4e4b66b68b65d60, 0xf81da84d56178540},
 
      {0xce1de40642e3f4b9, 0x36251260ab9d668f},
 
      {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
 
      {0xa1075a24e4421730, 0xb24cf65b8612f820},
 
      {0xc94930ae1d529cfc, 0xdee033f26797b628},
 
      {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
 
      {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
 
      {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
 
      {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
 
      {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
 
      {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
 
      {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
 
      {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
 
      {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
 
      {0xea53df5fd18d5513, 0x84c86189216dc5ee},
 
      {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
 
      {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
 
      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
 
      {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
 
      {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
 
      {0xdf78e4b2bd342cf6, 0x914da9246b255417},
 
      {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
 
      {0xae9672aba3d0c320, 0xa184ac2473b529b2},
 
      {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
 
      {0x8865899617fb1871, 0x7e2fa67c7a658893},
 
      {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
 
      {0xd51ea6fa85785631, 0x552a74227f3ea566},
 
      {0x8533285c936b35de, 0xd53a88958f872760},
 
      {0xa67ff273b8460356, 0x8a892abaf368f138},
 
      {0xd01fef10a657842c, 0x2d2b7569b0432d86},
 
      {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
 
      {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
 
      {0xcb3f2f7642717713, 0x241c70a936219a74},
 
      {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
 
      {0x9ec95d1463e8a506, 0xf4363804324a40ab},
 
      {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
 
      {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
 
      {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
 
      {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
 
      {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
 
      {0x976e41088617ca01, 0xd5be0503e085d814},
 
      {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
 
      {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
 
      {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
 
      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
 
      {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
 
      {0x906a617d450187e2, 0x27fb2b80668b24c6},
 
      {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
 
      {0xe1a63853bbd26451, 0x5e7873f8a0396974},
 
      {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
 
      {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
 
      {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
 
      {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
 
      {0xac2820d9623bf429, 0x546345fa9fbdcd45},
 
      {0xd732290fbacaf133, 0xa97c177947ad4096},
 
      {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
 
      {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
 
      {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
 
      {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
 
      {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
 
      {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
 
      {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
 
      {0xa0555e361951c366, 0xd7e105bcc3326220},
 
      {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
 
      {0xfa856334878fc150, 0xb14f98f6f0feb952},
 
      {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
 
      {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
 
      {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
 
      {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
 
      {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
 
      {0xeeea5d5004981478, 0x1858ccfce06cac75},
 
      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
 
      {0xbaa718e68396cffd, 0xd30560258f54e6bb},
 
      {0xe950df20247c83fd, 0x47c6b82ef32a206a},
 
      {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
 
      {0xb6472e511c81471d, 0xe0133fe4adf8e953},
 
      {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
 
      {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
 
      {0xb201833b35d63f73, 0x2cd2cc6551e513db},
 
      {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
 
      {0x8b112e86420f6191, 0xfb04afaf27faf783},
 
      {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
 
      {0xd94ad8b1c7380874, 0x18375281ae7822bd},
 
      {0x87cec76f1c830548, 0x8f2293910d0b15b6},
 
      {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
 
      {0xd433179d9c8cb841, 0x5fa60692a46151ec},
 
      {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
 
      {0xa5c7ea73224deff3, 0x12b9b522906c0801},
 
      {0xcf39e50feae16bef, 0xd768226b34870a01},
 
      {0x81842f29f2cce375, 0xe6a1158300d46641},
 
      {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
 
      {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
 
      {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
 
      {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
 
      {0xc5a05277621be293, 0xc7098b7305241886},
 
      {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
 
      {0x9a65406d44a5c903, 0x737f74f1dc043329},
 
      {0xc0fe908895cf3b44, 0x505f522e53053ff3},
 
      {0xf13e34aabb430a15, 0x647726b9e7c68ff0},
 
      {0x96c6e0eab509e64d, 0x5eca783430dc19f6},
 
      {0xbc789925624c5fe0, 0xb67d16413d132073},
 
      {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
 
      {0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
 
      {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
 
      {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
 
      {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
 
      {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
 
      {0xe0accfa875af45a7, 0x93eb1b80a33b8606},
 
      {0x8c6c01c9498d8b88, 0xbc72f130660533c4},
 
      {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
 
      { 0xdb68c2ca82ed2a05,
 
        0xa67398db9f6820e2 }
 
#else
 
    {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
 
    {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
 
@@ -1026,22 +1001,24 @@ const uint128_wrapper basic_data<T>::dra
 
    {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
 
    {0xc350000000000000, 0x0000000000000000},
 
    {0x9dc5ada82b70b59d, 0xf020000000000000},
 
    {0xfee50b7025c36a08, 0x02f236d04753d5b4},
 
    {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86},
 
    {0xa6539930bf6bff45, 0x84db8346b786151c},
 
    {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2},
 
    {0xd910f7ff28069da4, 0x1b2ba1518094da04},
 
    {0xaf58416654a6babb, 0x387ac8d1970027b2},
 
    {0x8da471a9de737e24, 0x5ceaecfed289e5d2},
 
    {0xe4d5e82392a40515, 0x0fabaf3feaa5334a},
 
    {0xb8da1662e7b00a17, 0x3d6a751f3b936243},
 
    {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}
 
      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
 
      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
 
      {0xa6539930bf6bff45, 0x84db8346b786151d},
 
      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
 
      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
 
      {0xaf58416654a6babb, 0x387ac8d1970027b3},
 
      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
 
      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
 
      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
 
      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
 
      {0xf13e34aabb430a15, 0x647726b9e7c68ff0}
 
#endif
 
};
 

	
 
#if !FMT_USE_FULL_CACHE_DRAGONBOX
 
template <typename T>
 
const uint64_t basic_data<T>::powers_of_5_64[] = {
 
#if FMT_USE_FULL_CACHE_DRAGONBOX
 
    return pow10_significands[k - float_info<double>::min_k];
 
#else
 
    static constexpr const uint64_t powers_of_5_64[] = {
 
    0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
 
    0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
 
    0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
 
@@ -1052,843 +1029,6 @@ const uint64_t basic_data<T>::powers_of_
 
    0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
 
    0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
 

	
 
template <typename T>
 
const uint32_t basic_data<T>::dragonbox_pow10_recovery_errors[] = {
 
    0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001,
 
    0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555,
 
    0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015,
 
    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110,
 
    0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454,
 
    0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014,
 
    0x69514555, 0x05151109, 0x00155555};
 
#endif
 

	
 
template <typename T>
 
const char basic_data<T>::foreground_color[] = "\x1b[38;2;";
 
template <typename T>
 
const char basic_data<T>::background_color[] = "\x1b[48;2;";
 
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
 
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
 
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
 
template <typename T>
 
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
 
template <typename T>
 
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
 

	
 
template <typename T> struct bits {
 
  static FMT_CONSTEXPR_DECL const int value =
 
      static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
 
};
 

	
 
class fp;
 
template <int SHIFT = 0> fp normalize(fp value);
 

	
 
// Lower (upper) boundary is a value half way between a floating-point value
 
// and its predecessor (successor). Boundaries have the same exponent as the
 
// value so only significands are stored.
 
struct boundaries {
 
  uint64_t lower;
 
  uint64_t upper;
 
};
 

	
 
// A handmade floating-point number f * pow(2, e).
 
class fp {
 
 private:
 
  using significand_type = uint64_t;
 

	
 
  template <typename Float>
 
  using is_supported_float = bool_constant<sizeof(Float) == sizeof(uint64_t) ||
 
                                           sizeof(Float) == sizeof(uint32_t)>;
 

	
 
 public:
 
  significand_type f;
 
  int e;
 

	
 
  // All sizes are in bits.
 
  // Subtract 1 to account for an implicit most significant bit in the
 
  // normalized form.
 
  static FMT_CONSTEXPR_DECL const int double_significand_size =
 
      std::numeric_limits<double>::digits - 1;
 
  static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
 
      1ULL << double_significand_size;
 
  static FMT_CONSTEXPR_DECL const int significand_size =
 
      bits<significand_type>::value;
 

	
 
  fp() : f(0), e(0) {}
 
  fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
 

	
 
  // Constructs fp from an IEEE754 double. It is a template to prevent compile
 
  // errors on platforms where double is not IEEE754.
 
  template <typename Double> explicit fp(Double d) { assign(d); }
 

	
 
  // Assigns d to this and return true iff predecessor is closer than successor.
 
  template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
 
  bool assign(Float d) {
 
    // Assume float is in the format [sign][exponent][significand].
 
    using limits = std::numeric_limits<Float>;
 
    const int float_significand_size = limits::digits - 1;
 
    const int exponent_size =
 
        bits<Float>::value - float_significand_size - 1;  // -1 for sign
 
    const uint64_t float_implicit_bit = 1ULL << float_significand_size;
 
    const uint64_t significand_mask = float_implicit_bit - 1;
 
    const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
 
    const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
 
    constexpr bool is_double = sizeof(Float) == sizeof(uint64_t);
 
    auto u = bit_cast<conditional_t<is_double, uint64_t, uint32_t>>(d);
 
    f = u & significand_mask;
 
    int biased_e =
 
        static_cast<int>((u & exponent_mask) >> float_significand_size);
 
    // Predecessor is closer if d is a normalized power of 2 (f == 0) other than
 
    // the smallest normalized number (biased_e > 1).
 
    bool is_predecessor_closer = f == 0 && biased_e > 1;
 
    if (biased_e != 0)
 
      f += float_implicit_bit;
 
    else
 
      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
 
    e = biased_e - exponent_bias - float_significand_size;
 
    return is_predecessor_closer;
 
  }
 

	
 
  template <typename Float, FMT_ENABLE_IF(!is_supported_float<Float>::value)>
 
  bool assign(Float) {
 
    *this = fp();
 
    return false;
 
  }
 
};
 

	
 
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
 
template <int SHIFT> fp normalize(fp value) {
 
  // Handle subnormals.
 
  const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
 
  while ((value.f & shifted_implicit_bit) == 0) {
 
    value.f <<= 1;
 
    --value.e;
 
  }
 
  // Subtract 1 to account for hidden bit.
 
  const auto offset =
 
      fp::significand_size - fp::double_significand_size - SHIFT - 1;
 
  value.f <<= offset;
 
  value.e -= offset;
 
  return value;
 
}
 

	
 
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
 

	
 
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
 
inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
 
#if FMT_USE_INT128
 
  auto product = static_cast<__uint128_t>(lhs) * rhs;
 
  auto f = static_cast<uint64_t>(product >> 64);
 
  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
 
#else
 
  // Multiply 32-bit parts of significands.
 
  uint64_t mask = (1ULL << 32) - 1;
 
  uint64_t a = lhs >> 32, b = lhs & mask;
 
  uint64_t c = rhs >> 32, d = rhs & mask;
 
  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
 
  // Compute mid 64-bit of result and round.
 
  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
 
  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
 
#endif
 
}
 

	
 
inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
 

	
 
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
 
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
 
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
 
  const int shift = 32;
 
  const auto significand = static_cast<int64_t>(data::log10_2_significand);
 
  int index = static_cast<int>(
 
      ((min_exponent + fp::significand_size - 1) * (significand >> shift) +
 
       ((int64_t(1) << shift) - 1))  // ceil
 
      >> 32                          // arithmetic shift
 
  );
 
  // Decimal exponent of the first (smallest) cached power of 10.
 
  const int first_dec_exp = -348;
 
  // Difference between 2 consecutive decimal exponents in cached powers of 10.
 
  const int dec_exp_step = 8;
 
  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
 
  pow10_exponent = first_dec_exp + index * dec_exp_step;
 
  return {data::grisu_pow10_significands[index],
 
          data::grisu_pow10_exponents[index]};
 
}
 

	
 
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
 
// is not available.
 
struct accumulator {
 
  uint64_t lower;
 
  uint64_t upper;
 

	
 
  accumulator() : lower(0), upper(0) {}
 
  explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
 

	
 
  void operator+=(uint64_t n) {
 
    lower += n;
 
    if (lower < n) ++upper;
 
  }
 
  void operator>>=(int shift) {
 
    assert(shift == 32);
 
    (void)shift;
 
    lower = (upper << 32) | (lower >> 32);
 
    upper >>= 32;
 
  }
 
};
 

	
 
class bigint {
 
 private:
 
  // A bigint is stored as an array of bigits (big digits), with bigit at index
 
  // 0 being the least significant one.
 
  using bigit = uint32_t;
 
  using double_bigit = uint64_t;
 
  enum { bigits_capacity = 32 };
 
  basic_memory_buffer<bigit, bigits_capacity> bigits_;
 
  int exp_;
 

	
 
  bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
 
  bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
 

	
 
  static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
 

	
 
  friend struct formatter<bigint>;
 

	
 
  void subtract_bigits(int index, bigit other, bigit& borrow) {
 
    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
 
    (*this)[index] = static_cast<bigit>(result);
 
    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
 
  }
 

	
 
  void remove_leading_zeros() {
 
    int num_bigits = static_cast<int>(bigits_.size()) - 1;
 
    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
 
    bigits_.resize(to_unsigned(num_bigits + 1));
 
  }
 

	
 
  // Computes *this -= other assuming aligned bigints and *this >= other.
 
  void subtract_aligned(const bigint& other) {
 
    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
 
    FMT_ASSERT(compare(*this, other) >= 0, "");
 
    bigit borrow = 0;
 
    int i = other.exp_ - exp_;
 
    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
 
      subtract_bigits(i, other.bigits_[j], borrow);
 
    while (borrow > 0) subtract_bigits(i, 0, borrow);
 
    remove_leading_zeros();
 
  }
 

	
 
  void multiply(uint32_t value) {
 
    const double_bigit wide_value = value;
 
    bigit carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      double_bigit result = bigits_[i] * wide_value + carry;
 
      bigits_[i] = static_cast<bigit>(result);
 
      carry = static_cast<bigit>(result >> bigit_bits);
 
    }
 
    if (carry != 0) bigits_.push_back(carry);
 
  }
 

	
 
  void multiply(uint64_t value) {
 
    const bigit mask = ~bigit(0);
 
    const double_bigit lower = value & mask;
 
    const double_bigit upper = value >> bigit_bits;
 
    double_bigit carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      double_bigit result = bigits_[i] * lower + (carry & mask);
 
      carry =
 
          bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
 
      bigits_[i] = static_cast<bigit>(result);
 
    }
 
    while (carry != 0) {
 
      bigits_.push_back(carry & mask);
 
      carry >>= bigit_bits;
 
    }
 
  }
 

	
 
 public:
 
  bigint() : exp_(0) {}
 
  explicit bigint(uint64_t n) { assign(n); }
 
  ~bigint() { assert(bigits_.capacity() <= bigits_capacity); }
 

	
 
  bigint(const bigint&) = delete;
 
  void operator=(const bigint&) = delete;
 

	
 
  void assign(const bigint& other) {
 
    auto size = other.bigits_.size();
 
    bigits_.resize(size);
 
    auto data = other.bigits_.data();
 
    std::copy(data, data + size, make_checked(bigits_.data(), size));
 
    exp_ = other.exp_;
 
  }
 

	
 
  void assign(uint64_t n) {
 
    size_t num_bigits = 0;
 
    do {
 
      bigits_[num_bigits++] = n & ~bigit(0);
 
      n >>= bigit_bits;
 
    } while (n != 0);
 
    bigits_.resize(num_bigits);
 
    exp_ = 0;
 
  }
 

	
 
  int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
 

	
 
  FMT_NOINLINE bigint& operator<<=(int shift) {
 
    assert(shift >= 0);
 
    exp_ += shift / bigit_bits;
 
    shift %= bigit_bits;
 
    if (shift == 0) return *this;
 
    bigit carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      bigit c = bigits_[i] >> (bigit_bits - shift);
 
      bigits_[i] = (bigits_[i] << shift) + carry;
 
      carry = c;
 
    }
 
    if (carry != 0) bigits_.push_back(carry);
 
    return *this;
 
  }
 

	
 
  template <typename Int> bigint& operator*=(Int value) {
 
    FMT_ASSERT(value > 0, "");
 
    multiply(uint32_or_64_or_128_t<Int>(value));
 
    return *this;
 
  }
 

	
 
  friend int compare(const bigint& lhs, const bigint& rhs) {
 
    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
 
    if (num_lhs_bigits != num_rhs_bigits)
 
      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
 
    int i = static_cast<int>(lhs.bigits_.size()) - 1;
 
    int j = static_cast<int>(rhs.bigits_.size()) - 1;
 
    int end = i - j;
 
    if (end < 0) end = 0;
 
    for (; i >= end; --i, --j) {
 
      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
 
      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
 
    }
 
    if (i != j) return i > j ? 1 : -1;
 
    return 0;
 
  }
 

	
 
  // Returns compare(lhs1 + lhs2, rhs).
 
  friend int add_compare(const bigint& lhs1, const bigint& lhs2,
 
                         const bigint& rhs) {
 
    int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
 
    int num_rhs_bigits = rhs.num_bigits();
 
    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
 
    if (max_lhs_bigits > num_rhs_bigits) return 1;
 
    auto get_bigit = [](const bigint& n, int i) -> bigit {
 
      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
 
    };
 
    double_bigit borrow = 0;
 
    int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
 
    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
 
      double_bigit sum =
 
          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
 
      bigit rhs_bigit = get_bigit(rhs, i);
 
      if (sum > rhs_bigit + borrow) return 1;
 
      borrow = rhs_bigit + borrow - sum;
 
      if (borrow > 1) return -1;
 
      borrow <<= bigit_bits;
 
    }
 
    return borrow != 0 ? -1 : 0;
 
  }
 

	
 
  // Assigns pow(10, exp) to this bigint.
 
  void assign_pow10(int exp) {
 
    assert(exp >= 0);
 
    if (exp == 0) return assign(1);
 
    // Find the top bit.
 
    int bitmask = 1;
 
    while (exp >= bitmask) bitmask <<= 1;
 
    bitmask >>= 1;
 
    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
 
    // repeated squaring and multiplication.
 
    assign(5);
 
    bitmask >>= 1;
 
    while (bitmask != 0) {
 
      square();
 
      if ((exp & bitmask) != 0) *this *= 5;
 
      bitmask >>= 1;
 
    }
 
    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
 
  }
 

	
 
  void square() {
 
    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
 
    int num_bigits = static_cast<int>(bigits_.size());
 
    int num_result_bigits = 2 * num_bigits;
 
    bigits_.resize(to_unsigned(num_result_bigits));
 
    using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
 
    auto sum = accumulator_t();
 
    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
 
      // Compute bigit at position bigit_index of the result by adding
 
      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
 
      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
 
        // Most terms are multiplied twice which can be optimized in the future.
 
        sum += static_cast<double_bigit>(n[i]) * n[j];
 
      }
 
      (*this)[bigit_index] = static_cast<bigit>(sum);
 
      sum >>= bits<bigit>::value;  // Compute the carry.
 
    }
 
    // Do the same for the top half.
 
    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
 
         ++bigit_index) {
 
      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
 
        sum += static_cast<double_bigit>(n[i++]) * n[j--];
 
      (*this)[bigit_index] = static_cast<bigit>(sum);
 
      sum >>= bits<bigit>::value;
 
    }
 
    --num_result_bigits;
 
    remove_leading_zeros();
 
    exp_ *= 2;
 
  }
 

	
 
  // If this bigint has a bigger exponent than other, adds trailing zero to make
 
  // exponents equal. This simplifies some operations such as subtraction.
 
  void align(const bigint& other) {
 
    int exp_difference = exp_ - other.exp_;
 
    if (exp_difference <= 0) return;
 
    int num_bigits = static_cast<int>(bigits_.size());
 
    bigits_.resize(to_unsigned(num_bigits + exp_difference));
 
    for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
 
      bigits_[j] = bigits_[i];
 
    std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
 
    exp_ -= exp_difference;
 
  }
 

	
 
  // Divides this bignum by divisor, assigning the remainder to this and
 
  // returning the quotient.
 
  int divmod_assign(const bigint& divisor) {
 
    FMT_ASSERT(this != &divisor, "");
 
    if (compare(*this, divisor) < 0) return 0;
 
    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
 
    align(divisor);
 
    int quotient = 0;
 
    do {
 
      subtract_aligned(divisor);
 
      ++quotient;
 
    } while (compare(*this, divisor) >= 0);
 
    return quotient;
 
  }
 
};
 

	
 
enum class round_direction { unknown, up, down };
 

	
 
// Given the divisor (normally a power of 10), the remainder = v % divisor for
 
// some number v and the error, returns whether v should be rounded up, down, or
 
// whether the rounding direction can't be determined due to error.
 
// error should be less than divisor / 2.
 
inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
 
                                           uint64_t error) {
 
  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
 
  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
 
  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
 
  // Round down if (remainder + error) * 2 <= divisor.
 
  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
 
    return round_direction::down;
 
  // Round up if (remainder - error) * 2 >= divisor.
 
  if (remainder >= error &&
 
      remainder - error >= divisor - (remainder - error)) {
 
    return round_direction::up;
 
  }
 
  return round_direction::unknown;
 
}
 

	
 
namespace digits {
 
enum result {
 
  more,  // Generate more digits.
 
  done,  // Done generating digits.
 
  error  // Digit generation cancelled due to an error.
 
};
 
}
 

	
 
// Generates output using the Grisu digit-gen algorithm.
 
// error: the size of the region (lower, upper) outside of which numbers
 
// definitely do not round to value (Delta in Grisu3).
 
template <typename Handler>
 
FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
 
                                                  int& exp, Handler& handler) {
 
  const fp one(1ULL << -value.e, value.e);
 
  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
 
  // zero because it contains a product of two 64-bit numbers with MSB set (due
 
  // to normalization) - 1, shifted right by at most 60 bits.
 
  auto integral = static_cast<uint32_t>(value.f >> -one.e);
 
  FMT_ASSERT(integral != 0, "");
 
  FMT_ASSERT(integral == value.f >> -one.e, "");
 
  // The fractional part of scaled value (p2 in Grisu) c = value % one.
 
  uint64_t fractional = value.f & (one.f - 1);
 
  exp = count_digits(integral);  // kappa in Grisu.
 
  // Divide by 10 to prevent overflow.
 
  auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
 
                                 value.f / 10, error * 10, exp);
 
  if (result != digits::more) return result;
 
  // Generate digits for the integral part. This can produce up to 10 digits.
 
  do {
 
    uint32_t digit = 0;
 
    auto divmod_integral = [&](uint32_t divisor) {
 
      digit = integral / divisor;
 
      integral %= divisor;
 
    };
 
    // This optimization by Milo Yip reduces the number of integer divisions by
 
    // one per iteration.
 
    switch (exp) {
 
    case 10:
 
      divmod_integral(1000000000);
 
      break;
 
    case 9:
 
      divmod_integral(100000000);
 
      break;
 
    case 8:
 
      divmod_integral(10000000);
 
      break;
 
    case 7:
 
      divmod_integral(1000000);
 
      break;
 
    case 6:
 
      divmod_integral(100000);
 
      break;
 
    case 5:
 
      divmod_integral(10000);
 
      break;
 
    case 4:
 
      divmod_integral(1000);
 
      break;
 
    case 3:
 
      divmod_integral(100);
 
      break;
 
    case 2:
 
      divmod_integral(10);
 
      break;
 
    case 1:
 
      digit = integral;
 
      integral = 0;
 
      break;
 
    default:
 
      FMT_ASSERT(false, "invalid number of digits");
 
    }
 
    --exp;
 
    auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
 
    result = handler.on_digit(static_cast<char>('0' + digit),
 
                              data::powers_of_10_64[exp] << -one.e, remainder,
 
                              error, exp, true);
 
    if (result != digits::more) return result;
 
  } while (exp > 0);
 
  // Generate digits for the fractional part.
 
  for (;;) {
 
    fractional *= 10;
 
    error *= 10;
 
    char digit = static_cast<char>('0' + (fractional >> -one.e));
 
    fractional &= one.f - 1;
 
    --exp;
 
    result = handler.on_digit(digit, one.f, fractional, error, exp, false);
 
    if (result != digits::more) return result;
 
  }
 
}
 

	
 
// The fixed precision digit handler.
 
struct fixed_handler {
 
  char* buf;
 
  int size;
 
  int precision;
 
  int exp10;
 
  bool fixed;
 

	
 
  digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
 
                          int& exp) {
 
    // Non-fixed formats require at least one digit and no precision adjustment.
 
    if (!fixed) return digits::more;
 
    // Adjust fixed precision by exponent because it is relative to decimal
 
    // point.
 
    precision += exp + exp10;
 
    // Check if precision is satisfied just by leading zeros, e.g.
 
    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
 
    if (precision > 0) return digits::more;
 
    if (precision < 0) return digits::done;
 
    auto dir = get_round_direction(divisor, remainder, error);
 
    if (dir == round_direction::unknown) return digits::error;
 
    buf[size++] = dir == round_direction::up ? '1' : '0';
 
    return digits::done;
 
  }
 

	
 
  digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
 
                          uint64_t error, int, bool integral) {
 
    FMT_ASSERT(remainder < divisor, "");
 
    buf[size++] = digit;
 
    if (!integral && error >= remainder) return digits::error;
 
    if (size < precision) return digits::more;
 
    if (!integral) {
 
      // Check if error * 2 < divisor with overflow prevention.
 
      // The check is not needed for the integral part because error = 1
 
      // and divisor > (1 << 32) there.
 
      if (error >= divisor || error >= divisor - error) return digits::error;
 
    } else {
 
      FMT_ASSERT(error == 1 && divisor > 2, "");
 
    }
 
    auto dir = get_round_direction(divisor, remainder, error);
 
    if (dir != round_direction::up)
 
      return dir == round_direction::down ? digits::done : digits::error;
 
    ++buf[size - 1];
 
    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
 
      buf[i] = '0';
 
      ++buf[i - 1];
 
    }
 
    if (buf[0] > '9') {
 
      buf[0] = '1';
 
      if (fixed)
 
        buf[size++] = '0';
 
      else
 
        ++exp10;
 
    }
 
    return digits::done;
 
  }
 
};
 

	
 
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
 
namespace dragonbox {
 
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
 
FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x,
 
                                               uint64_t y) FMT_NOEXCEPT {
 
#if FMT_USE_INT128
 
  return static_cast<uint128_t>(x) * static_cast<uint128_t>(y);
 
#elif defined(_MSC_VER) && defined(_M_X64)
 
  uint128_wrapper result;
 
  result.low_ = _umul128(x, y, &result.high_);
 
  return result;
 
#else
 
  const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1);
 

	
 
  uint64_t a = x >> 32;
 
  uint64_t b = x & mask;
 
  uint64_t c = y >> 32;
 
  uint64_t d = y & mask;
 

	
 
  uint64_t ac = a * c;
 
  uint64_t bc = b * c;
 
  uint64_t ad = a * d;
 
  uint64_t bd = b * d;
 

	
 
  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
 

	
 
  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
 
          (intermediate << 32) + (bd & mask)};
 
#endif
 
}
 

	
 
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
 
FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x,
 
                                                uint64_t y) FMT_NOEXCEPT {
 
#if FMT_USE_INT128
 
  auto p = static_cast<uint128_t>(x) * static_cast<uint128_t>(y);
 
  return static_cast<uint64_t>(p >> 64);
 
#elif defined(_MSC_VER) && defined(_M_X64)
 
  return __umulh(x, y);
 
#else
 
  return umul128(x, y).high();
 
#endif
 
}
 

	
 
// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a
 
// 128-bit unsigned integer.
 
FMT_SAFEBUFFERS inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y)
 
    FMT_NOEXCEPT {
 
  uint128_wrapper g0 = umul128(x, y.high());
 
  g0 += umul128_upper64(x, y.low());
 
  return g0.high();
 
}
 

	
 
// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a
 
// 64-bit unsigned integer.
 
inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT {
 
  return static_cast<uint32_t>(umul128_upper64(x, y));
 
}
 

	
 
// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a
 
// 128-bit unsigned integer.
 
FMT_SAFEBUFFERS inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y)
 
    FMT_NOEXCEPT {
 
  uint64_t g01 = x * y.high();
 
  uint64_t g10 = umul128_upper64(x, y.low());
 
  return g01 + g10;
 
}
 

	
 
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
 
// 64-bit unsigned integer.
 
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT {
 
  return x * y;
 
}
 

	
 
// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from
 
// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4.
 
inline int floor_log10_pow2(int e) FMT_NOEXCEPT {
 
  FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
 
  const int shift = 22;
 
  return (e * static_cast<int>(data::log10_2_significand >> (64 - shift))) >>
 
         shift;
 
}
 

	
 
// Various fast log computations.
 
inline int floor_log2_pow10(int e) FMT_NOEXCEPT {
 
  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
 
  const uint64_t log2_10_integer_part = 3;
 
  const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9;
 
  const int shift_amount = 19;
 
  return (e * static_cast<int>(
 
                  (log2_10_integer_part << shift_amount) |
 
                  (log2_10_fractional_digits >> (64 - shift_amount)))) >>
 
         shift_amount;
 
}
 
inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT {
 
  FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent");
 
  const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375;
 
  const int shift_amount = 22;
 
  return (e * static_cast<int>(data::log10_2_significand >>
 
                               (64 - shift_amount)) -
 
          static_cast<int>(log10_4_over_3_fractional_digits >>
 
                           (64 - shift_amount))) >>
 
         shift_amount;
 
}
 

	
 
// Returns true iff x is divisible by pow(2, exp).
 
inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT {
 
  FMT_ASSERT(exp >= 1, "");
 
  FMT_ASSERT(x != 0, "");
 
#ifdef FMT_BUILTIN_CTZ
 
  return FMT_BUILTIN_CTZ(x) >= exp;
 
#else
 
  return exp < num_bits<uint32_t>() && x == ((x >> exp) << exp);
 
#endif
 
}
 
inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT {
 
  FMT_ASSERT(exp >= 1, "");
 
  FMT_ASSERT(x != 0, "");
 
#ifdef FMT_BUILTIN_CTZLL
 
  return FMT_BUILTIN_CTZLL(x) >= exp;
 
#else
 
  return exp < num_bits<uint64_t>() && x == ((x >> exp) << exp);
 
#endif
 
}
 

	
 
// Returns true iff x is divisible by pow(5, exp).
 
inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT {
 
  FMT_ASSERT(exp <= 10, "too large exponent");
 
  return x * data::divtest_table_for_pow5_32[exp].mod_inv <=
 
         data::divtest_table_for_pow5_32[exp].max_quotient;
 
}
 
inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT {
 
  FMT_ASSERT(exp <= 23, "too large exponent");
 
  return x * data::divtest_table_for_pow5_64[exp].mod_inv <=
 
         data::divtest_table_for_pow5_64[exp].max_quotient;
 
}
 

	
 
// Replaces n by floor(n / pow(5, N)) returning true if and only if n is
 
// divisible by pow(5, N).
 
// Precondition: n <= 2 * pow(5, N + 1).
 
template <int N>
 
bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT {
 
  static constexpr struct {
 
    uint32_t magic_number;
 
    int bits_for_comparison;
 
    uint32_t threshold;
 
    int shift_amount;
 
  } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}};
 
  constexpr auto info = infos[N - 1];
 
  n *= info.magic_number;
 
  const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1;
 
  bool result = (n & comparison_mask) <= info.threshold;
 
  n >>= info.shift_amount;
 
  return result;
 
}
 

	
 
// Computes floor(n / pow(10, N)) for small n and N.
 
// Precondition: n <= pow(10, N + 1).
 
template <int N> uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT {
 
  static constexpr struct {
 
    uint32_t magic_number;
 
    int shift_amount;
 
    uint32_t divisor_times_10;
 
  } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}};
 
  constexpr auto info = infos[N - 1];
 
  FMT_ASSERT(n <= info.divisor_times_10, "n is too large");
 
  return n * info.magic_number >> info.shift_amount;
 
}
 

	
 
// Computes floor(n / 10^(kappa + 1)) (float)
 
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT {
 
  return n / float_info<float>::big_divisor;
 
}
 
// Computes floor(n / 10^(kappa + 1)) (double)
 
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT {
 
  return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9;
 
}
 

	
 
// Various subroutines using pow10 cache
 
template <class T> struct cache_accessor;
 

	
 
template <> struct cache_accessor<float> {
 
  using carrier_uint = float_info<float>::carrier_uint;
 
  using cache_entry_type = uint64_t;
 

	
 
  static uint64_t get_cached_power(int k) FMT_NOEXCEPT {
 
    FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
 
               "k is out of range");
 
    return data::dragonbox_pow10_significands_64[k - float_info<float>::min_k];
 
  }
 

	
 
  static carrier_uint compute_mul(carrier_uint u,
 
                                  const cache_entry_type& cache) FMT_NOEXCEPT {
 
    return umul96_upper32(u, cache);
 
  }
 

	
 
  static uint32_t compute_delta(const cache_entry_type& cache,
 
                                int beta_minus_1) FMT_NOEXCEPT {
 
    return static_cast<uint32_t>(cache >> (64 - 1 - beta_minus_1));
 
  }
 

	
 
  static bool compute_mul_parity(carrier_uint two_f,
 
                                 const cache_entry_type& cache,
 
                                 int beta_minus_1) FMT_NOEXCEPT {
 
    FMT_ASSERT(beta_minus_1 >= 1, "");
 
    FMT_ASSERT(beta_minus_1 < 64, "");
 

	
 
    return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
 
  }
 

	
 
  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
    return static_cast<carrier_uint>(
 
        (cache - (cache >> (float_info<float>::significand_bits + 2))) >>
 
        (64 - float_info<float>::significand_bits - 1 - beta_minus_1));
 
  }
 

	
 
  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
    return static_cast<carrier_uint>(
 
        (cache + (cache >> (float_info<float>::significand_bits + 1))) >>
 
        (64 - float_info<float>::significand_bits - 1 - beta_minus_1));
 
  }
 

	
 
  static carrier_uint compute_round_up_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
    return (static_cast<carrier_uint>(
 
                cache >>
 
                (64 - float_info<float>::significand_bits - 2 - beta_minus_1)) +
 
            1) /
 
           2;
 
  }
 
};
 

	
 
template <> struct cache_accessor<double> {
 
  using carrier_uint = float_info<double>::carrier_uint;
 
  using cache_entry_type = uint128_wrapper;
 

	
 
  static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT {
 
    FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
 
               "k is out of range");
 

	
 
#if FMT_USE_FULL_CACHE_DRAGONBOX
 
    return data::dragonbox_pow10_significands_128[k -
 
                                                  float_info<double>::min_k];
 
#else
 
    static const int compression_ratio = 27;
 

	
 
    // Compute base index.
 
@@ -1897,8 +1037,7 @@ template <> struct cache_accessor<double
 
    int offset = k - kb;
 

	
 
    // Get base cache.
 
    uint128_wrapper base_cache =
 
        data::dragonbox_pow10_significands_128[cache_index];
 
    uint128_fallback base_cache = pow10_significands[cache_index];
 
    if (offset == 0) return base_cache;
 

	
 
    // Compute the required amount of bit-shift.
 
@@ -1906,10 +1045,9 @@ template <> struct cache_accessor<double
 
    FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
 

	
 
    // Try to recover the real cache.
 
    uint64_t pow5 = data::powers_of_5_64[offset];
 
    uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5);
 
    uint128_wrapper middle_low =
 
        umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5);
 
    uint64_t pow5 = powers_of_5_64[offset];
 
    uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
 
    uint128_fallback middle_low = umul128(base_cache.low(), pow5);
 

	
 
    recovered_cache += middle_low.high();
 

	
 
@@ -1917,228 +1055,172 @@ template <> struct cache_accessor<double
 
    uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
 

	
 
    recovered_cache =
 
        uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle,
 
        uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
 
                        ((middle_low.low() >> alpha) | middle_to_low)};
 

	
 
    if (kb < 0) recovered_cache += 1;
 

	
 
    // Get error.
 
    int error_idx = (k - float_info<double>::min_k) / 16;
 
    uint32_t error = (data::dragonbox_pow10_recovery_errors[error_idx] >>
 
                      ((k - float_info<double>::min_k) % 16) * 2) &
 
                     0x3;
 

	
 
    // Add the error back.
 
    FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), "");
 
    return {recovered_cache.high(), recovered_cache.low() + error};
 
    FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
 
    return {recovered_cache.high(), recovered_cache.low() + 1};
 
#endif
 
  }
 

	
 
  static carrier_uint compute_mul(carrier_uint u,
 
                                  const cache_entry_type& cache) FMT_NOEXCEPT {
 
    return umul192_upper64(u, cache);
 
  struct compute_mul_result {
 
    carrier_uint result;
 
    bool is_integer;
 
  };
 
  struct compute_mul_parity_result {
 
    bool parity;
 
    bool is_integer;
 
  };
 

	
 
  static compute_mul_result compute_mul(
 
      carrier_uint u, const cache_entry_type& cache) noexcept {
 
    auto r = umul192_upper128(u, cache);
 
    return {r.high(), r.low() == 0};
 
  }
 

	
 
  static uint32_t compute_delta(cache_entry_type const& cache,
 
                                int beta_minus_1) FMT_NOEXCEPT {
 
    return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta_minus_1));
 
                                int beta) noexcept {
 
    return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
 
  }
 

	
 
  static bool compute_mul_parity(carrier_uint two_f,
 
                                 const cache_entry_type& cache,
 
                                 int beta_minus_1) FMT_NOEXCEPT {
 
    FMT_ASSERT(beta_minus_1 >= 1, "");
 
    FMT_ASSERT(beta_minus_1 < 64, "");
 
  static compute_mul_parity_result compute_mul_parity(
 
      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
 
    FMT_ASSERT(beta >= 1, "");
 
    FMT_ASSERT(beta < 64, "");
 

	
 
    return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0;
 
    auto r = umul192_lower128(two_f, cache);
 
    return {((r.high() >> (64 - beta)) & 1) != 0,
 
            ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
 
  }
 

	
 
  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return (cache.high() -
 
            (cache.high() >> (float_info<double>::significand_bits + 2))) >>
 
           (64 - float_info<double>::significand_bits - 1 - beta_minus_1);
 
            (cache.high() >> (num_significand_bits<double>() + 2))) >>
 
           (64 - num_significand_bits<double>() - 1 - beta);
 
  }
 

	
 
  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return (cache.high() +
 
            (cache.high() >> (float_info<double>::significand_bits + 1))) >>
 
           (64 - float_info<double>::significand_bits - 1 - beta_minus_1);
 
            (cache.high() >> (num_significand_bits<double>() + 1))) >>
 
           (64 - num_significand_bits<double>() - 1 - beta);
 
  }
 

	
 
  static carrier_uint compute_round_up_for_shorter_interval_case(
 
      const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT {
 
    return ((cache.high() >>
 
             (64 - float_info<double>::significand_bits - 2 - beta_minus_1)) +
 
      const cache_entry_type& cache, int beta) noexcept {
 
    return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
 
            1) /
 
           2;
 
  }
 
};
 

	
 
// Various integer checks
 
template <class T>
 
bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT {
 
  return exponent >=
 
             float_info<
 
                 T>::case_shorter_interval_left_endpoint_lower_threshold &&
 
         exponent <=
 
             float_info<T>::case_shorter_interval_left_endpoint_upper_threshold;
 
}
 
template <class T>
 
bool is_endpoint_integer(typename float_info<T>::carrier_uint two_f,
 
                         int exponent, int minus_k) FMT_NOEXCEPT {
 
  if (exponent < float_info<T>::case_fc_pm_half_lower_threshold) return false;
 
  // For k >= 0.
 
  if (exponent <= float_info<T>::case_fc_pm_half_upper_threshold) return true;
 
  // For k < 0.
 
  if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
 
  return divisible_by_power_of_5(two_f, minus_k);
 
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
 
  return cache_accessor<double>::get_cached_power(k);
 
}
 

	
 
template <class T>
 
bool is_center_integer(typename float_info<T>::carrier_uint two_f, int exponent,
 
                       int minus_k) FMT_NOEXCEPT {
 
  // Exponent for 5 is negative.
 
  if (exponent > float_info<T>::divisibility_check_by_5_threshold) return false;
 
  if (exponent > float_info<T>::case_fc_upper_threshold)
 
    return divisible_by_power_of_5(two_f, minus_k);
 
  // Both exponents are nonnegative.
 
  if (exponent >= float_info<T>::case_fc_lower_threshold) return true;
 
  // Exponent for 2 is negative.
 
  return divisible_by_power_of_2(two_f, minus_k - exponent + 1);
 
// Various integer checks
 
template <typename T>
 
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
 
  const int case_shorter_interval_left_endpoint_lower_threshold = 2;
 
  const int case_shorter_interval_left_endpoint_upper_threshold = 3;
 
  return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
 
         exponent <= case_shorter_interval_left_endpoint_upper_threshold;
 
}
 

	
 
// Remove trailing zeros from n and return the number of zeros removed (float)
 
FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT {
 
#ifdef FMT_BUILTIN_CTZ
 
  int t = FMT_BUILTIN_CTZ(n);
 
#else
 
  int t = ctz(n);
 
#endif
 
  if (t > float_info<float>::max_trailing_zeros)
 
    t = float_info<float>::max_trailing_zeros;
 

	
 
  const uint32_t mod_inv1 = 0xcccccccd;
 
  const uint32_t max_quotient1 = 0x33333333;
 
  const uint32_t mod_inv2 = 0xc28f5c29;
 
  const uint32_t max_quotient2 = 0x0a3d70a3;
 
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
 
  FMT_ASSERT(n != 0, "");
 
  // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
 
  // See https://github.com/fmtlib/fmt/issues/3163 for more details.
 
  const uint32_t mod_inv_5 = 0xcccccccd;
 
  // Casts are needed to workaround a bug in MSVC 19.22 and older.
 
  const uint32_t mod_inv_25 =
 
      static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
 

	
 
  int s = 0;
 
  for (; s < t - 1; s += 2) {
 
    if (n * mod_inv2 > max_quotient2) break;
 
    n *= mod_inv2;
 
  while (true) {
 
    auto q = rotr(n * mod_inv_25, 2);
 
    if (q > max_value<uint32_t>() / 100) break;
 
    n = q;
 
    s += 2;
 
  }
 
  if (s < t && n * mod_inv1 <= max_quotient1) {
 
    n *= mod_inv1;
 
    ++s;
 
  auto q = rotr(n * mod_inv_5, 1);
 
  if (q <= max_value<uint32_t>() / 10) {
 
    n = q;
 
    s |= 1;
 
  }
 
  n >>= s;
 
  return s;
 
}
 

	
 
// Removes trailing zeros and returns the number of zeros removed (double)
 
FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT {
 
#ifdef FMT_BUILTIN_CTZLL
 
  int t = FMT_BUILTIN_CTZLL(n);
 
#else
 
  int t = ctzll(n);
 
#endif
 
  if (t > float_info<double>::max_trailing_zeros)
 
    t = float_info<double>::max_trailing_zeros;
 
  // Divide by 10^8 and reduce to 32-bits
 
  // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17,
 
  // both of the quotient and the r should fit in 32-bits
 
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
 
  FMT_ASSERT(n != 0, "");
 

	
 
  // This magic number is ceil(2^90 / 10^8).
 
  constexpr uint64_t magic_number = 12379400392853802749ull;
 
  auto nm = umul128(n, magic_number);
 

	
 
  const uint32_t mod_inv1 = 0xcccccccd;
 
  const uint32_t max_quotient1 = 0x33333333;
 
  const uint64_t mod_inv8 = 0xc767074b22e90e21;
 
  const uint64_t max_quotient8 = 0x00002af31dc46118;
 
  // Is n is divisible by 10^8?
 
  if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
 
    // If yes, work with the quotient.
 
    auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
 

	
 
  // If the number is divisible by 1'0000'0000, work with the quotient
 
  if (t >= 8) {
 
    auto quotient_candidate = n * mod_inv8;
 

	
 
    if (quotient_candidate <= max_quotient8) {
 
      auto quotient = static_cast<uint32_t>(quotient_candidate >> 8);
 
    const uint32_t mod_inv_5 = 0xcccccccd;
 
    const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
 

	
 
      int s = 8;
 
      for (; s < t; ++s) {
 
        if (quotient * mod_inv1 > max_quotient1) break;
 
        quotient *= mod_inv1;
 
    while (true) {
 
      auto q = rotr(n32 * mod_inv_25, 2);
 
      if (q > max_value<uint32_t>() / 100) break;
 
      n32 = q;
 
      s += 2;
 
      }
 
      quotient >>= (s - 8);
 
      n = quotient;
 
      return s;
 
    }
 
    auto q = rotr(n32 * mod_inv_5, 1);
 
    if (q <= max_value<uint32_t>() / 10) {
 
      n32 = q;
 
      s |= 1;
 
  }
 

	
 
  // Otherwise, work with the remainder
 
  auto quotient = static_cast<uint32_t>(n / 100000000);
 
  auto remainder = static_cast<uint32_t>(n - 100000000 * quotient);
 

	
 
  if (t == 0 || remainder * mod_inv1 > max_quotient1) {
 
    return 0;
 
    n = n32;
 
    return s;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  if (t == 1 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 1) + quotient * 10000000ull;
 
    return 1;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  if (t == 2 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 2) + quotient * 1000000ull;
 
    return 2;
 
  }
 
  remainder *= mod_inv1;
 
  // If n is not divisible by 10^8, work with n itself.
 
  const uint64_t mod_inv_5 = 0xcccccccccccccccd;
 
  const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
 

	
 
  if (t == 3 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 3) + quotient * 100000ull;
 
    return 3;
 
  int s = 0;
 
  while (true) {
 
    auto q = rotr(n * mod_inv_25, 2);
 
    if (q > max_value<uint64_t>() / 100) break;
 
    n = q;
 
    s += 2;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  if (t == 4 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 4) + quotient * 10000ull;
 
    return 4;
 
  auto q = rotr(n * mod_inv_5, 1);
 
  if (q <= max_value<uint64_t>() / 10) {
 
    n = q;
 
    s |= 1;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  if (t == 5 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 5) + quotient * 1000ull;
 
    return 5;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  if (t == 6 || remainder * mod_inv1 > max_quotient1) {
 
    n = (remainder >> 6) + quotient * 100ull;
 
    return 6;
 
  }
 
  remainder *= mod_inv1;
 

	
 
  n = (remainder >> 7) + quotient * 10ull;
 
  return 7;
 
  return s;
 
}
 

	
 
// The main algorithm for shorter interval case
 
template <class T>
 
FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp<T> shorter_interval_case(
 
    int exponent) FMT_NOEXCEPT {
 
template <typename T>
 
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
 
  decimal_fp<T> ret_value;
 
  // Compute k and beta
 
  const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
 
  const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
 
  const int beta = exponent + floor_log2_pow10(-minus_k);
 

	
 
  // Compute xi and zi
 
  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
 
  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
 

	
 
  auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
 
      cache, beta_minus_1);
 
      cache, beta);
 
  auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
 
      cache, beta_minus_1);
 
      cache, beta);
 

	
 
  // If the left endpoint is not an integer, increase it
 
  if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
 
@@ -2155,8 +1237,8 @@ FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decima
 

	
 
  // Otherwise, compute the round-up of y
 
  ret_value.significand =
 
      cache_accessor<T>::compute_round_up_for_shorter_interval_case(
 
          cache, beta_minus_1);
 
      cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache,
 
                                                                    beta);
 
  ret_value.exponent = minus_k;
 

	
 
  // When tie occurs, choose one of them according to the rule
 
@@ -2171,8 +1253,7 @@ FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decima
 
  return ret_value;
 
}
 

	
 
template <typename T>
 
FMT_SAFEBUFFERS decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT {
 
template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
 
  // Step 1: integer promotion & Schubfach multiplier calculation.
 

	
 
  using carrier_uint = typename float_info<T>::carrier_uint;
 
@@ -2181,23 +1262,25 @@ FMT_SAFEBUFFERS decimal_fp<T> to_decimal
 

	
 
  // Extract significand bits and exponent bits.
 
  const carrier_uint significand_mask =
 
      (static_cast<carrier_uint>(1) << float_info<T>::significand_bits) - 1;
 
      (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
 
  carrier_uint significand = (br & significand_mask);
 
  int exponent = static_cast<int>((br & exponent_mask<T>()) >>
 
                                  float_info<T>::significand_bits);
 
  int exponent =
 
      static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
 

	
 
  if (exponent != 0) {  // Check if normal.
 
    exponent += float_info<T>::exponent_bias - float_info<T>::significand_bits;
 
    exponent -= exponent_bias<T>() + num_significand_bits<T>();
 

	
 
    // Shorter interval case; proceed like Schubfach.
 
    // In fact, when exponent == 1 and significand == 0, the interval is
 
    // regular. However, it can be shown that the end-results are anyway same.
 
    if (significand == 0) return shorter_interval_case<T>(exponent);
 

	
 
    significand |=
 
        (static_cast<carrier_uint>(1) << float_info<T>::significand_bits);
 
    significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
 
  } else {
 
    // Subnormal case; the interval is always regular.
 
    if (significand == 0) return {0, 0};
 
    exponent = float_info<T>::min_exponent - float_info<T>::significand_bits;
 
    exponent =
 
        std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
 
  }
 

	
 
  const bool include_left_endpoint = (significand % 2 == 0);
 
@@ -2206,485 +1289,125 @@ FMT_SAFEBUFFERS decimal_fp<T> to_decimal
 
  // Compute k and beta.
 
  const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
 
  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
 
  const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k);
 
  const int beta = exponent + floor_log2_pow10(-minus_k);
 

	
 
  // Compute zi and deltai
 
  // Compute zi and deltai.
 
  // 10^kappa <= deltai < 10^(kappa + 1)
 
  const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta_minus_1);
 
  const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta);
 
  const carrier_uint two_fc = significand << 1;
 
  const carrier_uint two_fr = two_fc | 1;
 
  const carrier_uint zi =
 
      cache_accessor<T>::compute_mul(two_fr << beta_minus_1, cache);
 

	
 
  // Step 2: Try larger divisor; remove trailing zeros if necessary
 
  // For the case of binary32, the result of integer check is not correct for
 
  // 29711844 * 2^-82
 
  // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
 
  // and 29711844 * 2^-81
 
  // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
 
  // and they are the unique counterexamples. However, since 29711844 is even,
 
  // this does not cause any problem for the endpoints calculations; it can only
 
  // cause a problem when we need to perform integer check for the center.
 
  // Fortunately, with these inputs, that branch is never executed, so we are
 
  // fine.
 
  const typename cache_accessor<T>::compute_mul_result z_mul =
 
      cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
 

	
 
  // Step 2: Try larger divisor; remove trailing zeros if necessary.
 

	
 
  // Using an upper bound on zi, we might be able to optimize the division
 
  // better than the compiler; we are computing zi / big_divisor here
 
  // better than the compiler; we are computing zi / big_divisor here.
 
  decimal_fp<T> ret_value;
 
  ret_value.significand = divide_by_10_to_kappa_plus_1(zi);
 
  uint32_t r = static_cast<uint32_t>(zi - float_info<T>::big_divisor *
 
  ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
 
  uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor *
 
                                              ret_value.significand);
 

	
 
  if (r > deltai) {
 
    goto small_divisor_case_label;
 
  } else if (r < deltai) {
 
    // Exclude the right endpoint if necessary
 
    if (r == 0 && !include_right_endpoint &&
 
        is_endpoint_integer<T>(two_fr, exponent, minus_k)) {
 
  if (r < deltai) {
 
    // Exclude the right endpoint if necessary.
 
    if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
 
      --ret_value.significand;
 
      r = float_info<T>::big_divisor;
 
      goto small_divisor_case_label;
 
    }
 
  } else if (r > deltai) {
 
    goto small_divisor_case_label;
 
  } else {
 
    // r == deltai; compare fractional parts
 
    // Check conditions in the order different from the paper
 
    // to take advantage of short-circuiting
 
    const carrier_uint two_fl = two_fc - 1;
 
    if ((!include_left_endpoint ||
 
         !is_endpoint_integer<T>(two_fl, exponent, minus_k)) &&
 
        !cache_accessor<T>::compute_mul_parity(two_fl, cache, beta_minus_1)) {
 
    // r == deltai; compare fractional parts.
 
    const typename cache_accessor<T>::compute_mul_parity_result x_mul =
 
        cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
 

	
 
    if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
 
      goto small_divisor_case_label;
 
    }
 
  }
 
  ret_value.exponent = minus_k + float_info<T>::kappa + 1;
 

	
 
  // We may need to remove trailing zeros
 
  // We may need to remove trailing zeros.
 
  ret_value.exponent += remove_trailing_zeros(ret_value.significand);
 
  return ret_value;
 

	
 
  // Step 3: Find the significand with the smaller divisor
 
  // Step 3: Find the significand with the smaller divisor.
 

	
 
small_divisor_case_label:
 
  ret_value.significand *= 10;
 
  ret_value.exponent = minus_k + float_info<T>::kappa;
 

	
 
  const uint32_t mask = (1u << float_info<T>::kappa) - 1;
 
  auto dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
 

	
 
  // Is dist divisible by 2^kappa?
 
  if ((dist & mask) == 0) {
 
  uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
 
    const bool approx_y_parity =
 
        ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
 
    dist >>= float_info<T>::kappa;
 

	
 
    // Is dist divisible by 5^kappa?
 
    if (check_divisibility_and_divide_by_pow5<float_info<T>::kappa>(dist)) {
 
  // Is dist divisible by 10^kappa?
 
  const bool divisible_by_small_divisor =
 
      check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
 

	
 
  // Add dist / 10^kappa to the significand.
 
      ret_value.significand += dist;
 

	
 
      // Check z^(f) >= epsilon^(f)
 
  if (!divisible_by_small_divisor) return ret_value;
 

	
 
  // Check z^(f) >= epsilon^(f).
 
      // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
 
      // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f)
 
  // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
 
      // Since there are only 2 possibilities, we only need to care about the
 
      // parity. Also, zi and r should have the same parity since the divisor
 
      // is an even number
 
      if (cache_accessor<T>::compute_mul_parity(two_fc, cache, beta_minus_1) !=
 
          approx_y_parity) {
 
  // is an even number.
 
  const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
 

	
 
  // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
 
  // or equivalently, when y is an integer.
 
  if (y_mul.parity != approx_y_parity)
 
        --ret_value.significand;
 
      } else {
 
        // If z^(f) >= epsilon^(f), we might have a tie
 
        // when z^(f) == epsilon^(f), or equivalently, when y is an integer
 
        if (is_center_integer<T>(two_fc, exponent, minus_k)) {
 
          ret_value.significand = ret_value.significand % 2 == 0
 
                                      ? ret_value.significand
 
                                      : ret_value.significand - 1;
 
        }
 
      }
 
    }
 
    // Is dist not divisible by 5^kappa?
 
    else {
 
      ret_value.significand += dist;
 
    }
 
  }
 
  // Is dist not divisible by 2^kappa?
 
  else {
 
    // Since we know dist is small, we might be able to optimize the division
 
    // better than the compiler; we are computing dist / small_divisor here
 
    ret_value.significand +=
 
        small_division_by_pow10<float_info<T>::kappa>(dist);
 
  }
 
  else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
 
    --ret_value.significand;
 
  return ret_value;
 
}
 
}  // namespace dragonbox
 

	
 
// Formats value using a variation of the Fixed-Precision Positive
 
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
 
// https://fmt.dev/p372-steele.pdf.
 
template <typename Double>
 
void fallback_format(Double d, int num_digits, bool binary32, buffer<char>& buf,
 
                     int& exp10) {
 
  bigint numerator;    // 2 * R in (FPP)^2.
 
  bigint denominator;  // 2 * S in (FPP)^2.
 
  // lower and upper are differences between value and corresponding boundaries.
 
  bigint lower;             // (M^- in (FPP)^2).
 
  bigint upper_store;       // upper's value if different from lower.
 
  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
 
  fp value;
 
  // Shift numerator and denominator by an extra bit or two (if lower boundary
 
  // is closer) to make lower and upper integers. This eliminates multiplication
 
  // by 2 during later computations.
 
  const bool is_predecessor_closer =
 
      binary32 ? value.assign(static_cast<float>(d)) : value.assign(d);
 
  int shift = is_predecessor_closer ? 2 : 1;
 
  uint64_t significand = value.f << shift;
 
  if (value.e >= 0) {
 
    numerator.assign(significand);
 
    numerator <<= value.e;
 
    lower.assign(1);
 
    lower <<= value.e;
 
    if (shift != 1) {
 
      upper_store.assign(1);
 
      upper_store <<= value.e + 1;
 
      upper = &upper_store;
 
    }
 
    denominator.assign_pow10(exp10);
 
    denominator <<= shift;
 
  } else if (exp10 < 0) {
 
    numerator.assign_pow10(-exp10);
 
    lower.assign(numerator);
 
    if (shift != 1) {
 
      upper_store.assign(numerator);
 
      upper_store <<= 1;
 
      upper = &upper_store;
 
    }
 
    numerator *= significand;
 
    denominator.assign(1);
 
    denominator <<= shift - value.e;
 
  } else {
 
    numerator.assign(significand);
 
    denominator.assign_pow10(exp10);
 
    denominator <<= shift - value.e;
 
    lower.assign(1);
 
    if (shift != 1) {
 
      upper_store.assign(1ULL << 1);
 
      upper = &upper_store;
 
    }
 
  }
 
  // Invariant: value == (numerator / denominator) * pow(10, exp10).
 
  if (num_digits < 0) {
 
    // Generate the shortest representation.
 
    if (!upper) upper = &lower;
 
    bool even = (value.f & 1) == 0;
 
    num_digits = 0;
 
    char* data = buf.data();
 
    for (;;) {
 
      int digit = numerator.divmod_assign(denominator);
 
      bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
 
      // numerator + upper >[=] pow10:
 
      bool high = add_compare(numerator, *upper, denominator) + even > 0;
 
      data[num_digits++] = static_cast<char>('0' + digit);
 
      if (low || high) {
 
        if (!low) {
 
          ++data[num_digits - 1];
 
        } else if (high) {
 
          int result = add_compare(numerator, numerator, denominator);
 
          // Round half to even.
 
          if (result > 0 || (result == 0 && (digit % 2) != 0))
 
            ++data[num_digits - 1];
 
        }
 
        buf.try_resize(to_unsigned(num_digits));
 
        exp10 -= num_digits - 1;
 
        return;
 
      }
 
      numerator *= 10;
 
      lower *= 10;
 
      if (upper != &lower) *upper *= 10;
 
    }
 
  }
 
  // Generate the given number of digits.
 
  exp10 -= num_digits - 1;
 
  if (num_digits == 0) {
 
    buf.try_resize(1);
 
    denominator *= 10;
 
    buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
 
    return;
 
  }
 
  buf.try_resize(to_unsigned(num_digits));
 
  for (int i = 0; i < num_digits - 1; ++i) {
 
    int digit = numerator.divmod_assign(denominator);
 
    buf[i] = static_cast<char>('0' + digit);
 
    numerator *= 10;
 
  }
 
  int digit = numerator.divmod_assign(denominator);
 
  auto result = add_compare(numerator, numerator, denominator);
 
  if (result > 0 || (result == 0 && (digit % 2) != 0)) {
 
    if (digit == 9) {
 
      const auto overflow = '0' + 10;
 
      buf[num_digits - 1] = overflow;
 
      // Propagate the carry.
 
      for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
 
        buf[i] = '0';
 
        ++buf[i - 1];
 
      }
 
      if (buf[0] == overflow) {
 
        buf[0] = '1';
 
        ++exp10;
 
      }
 
      return;
 
    }
 
    ++digit;
 
  }
 
  buf[num_digits - 1] = static_cast<char>('0' + digit);
 
}
 

	
 
template <typename T>
 
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
 
  static_assert(!std::is_same<T, float>::value, "");
 
  FMT_ASSERT(value >= 0, "value is negative");
 

	
 
  const bool fixed = specs.format == float_format::fixed;
 
  if (value <= 0) {  // <= instead of == to silence a warning.
 
    if (precision <= 0 || !fixed) {
 
      buf.push_back('0');
 
      return 0;
 
    }
 
    buf.try_resize(to_unsigned(precision));
 
    std::uninitialized_fill_n(buf.data(), precision, '0');
 
    return -precision;
 
  }
 

	
 
  if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
 

	
 
  if (precision < 0) {
 
    // Use Dragonbox for the shortest format.
 
    if (specs.binary32) {
 
      auto dec = dragonbox::to_decimal(static_cast<float>(value));
 
      write<char>(buffer_appender<char>(buf), dec.significand);
 
      return dec.exponent;
 
    }
 
    auto dec = dragonbox::to_decimal(static_cast<double>(value));
 
    write<char>(buffer_appender<char>(buf), dec.significand);
 
    return dec.exponent;
 
  }
 

	
 
  // Use Grisu + Dragon4 for the given precision:
 
  // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
 
  int exp = 0;
 
  const int min_exp = -60;  // alpha in Grisu.
 
  int cached_exp10 = 0;     // K in Grisu.
 
  fp normalized = normalize(fp(value));
 
  const auto cached_pow = get_cached_power(
 
      min_exp - (normalized.e + fp::significand_size), cached_exp10);
 
  normalized = normalized * cached_pow;
 
  // Limit precision to the maximum possible number of significant digits in an
 
  // IEEE754 double because we don't need to generate zeros.
 
  const int max_double_digits = 767;
 
  if (precision > max_double_digits) precision = max_double_digits;
 
  fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
 
  if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
 
    exp += handler.size - cached_exp10 - 1;
 
    fallback_format(value, handler.precision, specs.binary32, buf, exp);
 
  } else {
 
    exp += handler.exp10;
 
    buf.try_resize(to_unsigned(handler.size));
 
  }
 
  if (!fixed && !specs.showpoint) {
 
    // Remove trailing zeros.
 
    auto num_digits = buf.size();
 
    while (num_digits > 0 && buf[num_digits - 1] == '0') {
 
      --num_digits;
 
      ++exp;
 
    }
 
    buf.try_resize(num_digits);
 
  }
 
  return exp;
 
}  // namespace detail
 

	
 
template <typename T>
 
int snprintf_float(T value, int precision, float_specs specs,
 
                   buffer<char>& buf) {
 
  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
 
  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
 
  static_assert(!std::is_same<T, float>::value, "");
 

	
 
  // Subtract 1 to account for the difference in precision since we use %e for
 
  // both general and exponent format.
 
  if (specs.format == float_format::general ||
 
      specs.format == float_format::exp)
 
    precision = (precision >= 0 ? precision : 6) - 1;
 

	
 
  // Build the format string.
 
  enum { max_format_size = 7 };  // The longest format is "%#.*Le".
 
  char format[max_format_size];
 
  char* format_ptr = format;
 
  *format_ptr++ = '%';
 
  if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
 
  if (precision >= 0) {
 
    *format_ptr++ = '.';
 
    *format_ptr++ = '*';
 
  }
 
  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
 
  *format_ptr++ = specs.format != float_format::hex
 
                      ? (specs.format == float_format::fixed ? 'f' : 'e')
 
                      : (specs.upper ? 'A' : 'a');
 
  *format_ptr = '\0';
 

	
 
  // Format using snprintf.
 
  auto offset = buf.size();
 
  for (;;) {
 
    auto begin = buf.data() + offset;
 
    auto capacity = buf.capacity() - offset;
 
#ifdef FMT_FUZZ
 
    if (precision > 100000)
 
      throw std::runtime_error(
 
          "fuzz mode - avoid large allocation inside snprintf");
 
#endif
 
    // Suppress the warning about a nonliteral format string.
 
    // Cannot use auto because of a bug in MinGW (#1532).
 
    int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
 
    int result = precision >= 0
 
                     ? snprintf_ptr(begin, capacity, format, precision, value)
 
                     : snprintf_ptr(begin, capacity, format, value);
 
    if (result < 0) {
 
      // The buffer will grow exponentially.
 
      buf.try_reserve(buf.capacity() + 1);
 
      continue;
 
    }
 
    auto size = to_unsigned(result);
 
    // Size equal to capacity means that the last character was truncated.
 
    if (size >= capacity) {
 
      buf.try_reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
 
      continue;
 
    }
 
    auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
 
    if (specs.format == float_format::fixed) {
 
      if (precision == 0) {
 
        buf.try_resize(size);
 
        return 0;
 
      }
 
      // Find and remove the decimal point.
 
      auto end = begin + size, p = end;
 
      do {
 
        --p;
 
      } while (is_digit(*p));
 
      int fraction_size = static_cast<int>(end - p - 1);
 
      std::memmove(p, p + 1, to_unsigned(fraction_size));
 
      buf.try_resize(size - 1);
 
      return -fraction_size;
 
    }
 
    if (specs.format == float_format::hex) {
 
      buf.try_resize(size + offset);
 
      return 0;
 
    }
 
    // Find and parse the exponent.
 
    auto end = begin + size, exp_pos = end;
 
    do {
 
      --exp_pos;
 
    } while (*exp_pos != 'e');
 
    char sign = exp_pos[1];
 
    assert(sign == '+' || sign == '-');
 
    int exp = 0;
 
    auto p = exp_pos + 2;  // Skip 'e' and sign.
 
    do {
 
      assert(is_digit(*p));
 
      exp = exp * 10 + (*p++ - '0');
 
    } while (p != end);
 
    if (sign == '-') exp = -exp;
 
    int fraction_size = 0;
 
    if (exp_pos != begin + 1) {
 
      // Remove trailing zeros.
 
      auto fraction_end = exp_pos - 1;
 
      while (*fraction_end == '0') --fraction_end;
 
      // Move the fractional part left to get rid of the decimal point.
 
      fraction_size = static_cast<int>(fraction_end - begin - 1);
 
      std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
 
    }
 
    buf.try_resize(to_unsigned(fraction_size) + offset + 1);
 
    return exp - fraction_size;
 
  }
 
}
 

	
 
// A public domain branchless UTF-8 decoder by Christopher Wellons:
 
// https://github.com/skeeto/branchless-utf8
 
/* Decode the next character, c, from buf, reporting errors in e.
 
 *
 
 * Since this is a branchless decoder, four bytes will be read from the
 
 * buffer regardless of the actual length of the next character. This
 
 * means the buffer _must_ have at least three bytes of zero padding
 
 * following the end of the data stream.
 
 *
 
 * Errors are reported in e, which will be non-zero if the parsed
 
 * character was somehow invalid: invalid byte sequence, non-canonical
 
 * encoding, or a surrogate half.
 
 *
 
 * The function returns a pointer to the next character. When an error
 
 * occurs, this pointer will be a guess that depends on the particular
 
 * error, but it will always advance at least one byte.
 
 */
 
inline const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
 
  static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
 
  static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
 
  static const int shiftc[] = {0, 18, 12, 6, 0};
 
  static const int shifte[] = {0, 6, 4, 2, 0};
 

	
 
  int len = code_point_length(buf);
 
  const char* next = buf + len;
 

	
 
  // Assume a four-byte character and load four bytes. Unused bits are
 
  // shifted out.
 
  auto s = reinterpret_cast<const unsigned char*>(buf);
 
  *c = uint32_t(s[0] & masks[len]) << 18;
 
  *c |= uint32_t(s[1] & 0x3f) << 12;
 
  *c |= uint32_t(s[2] & 0x3f) << 6;
 
  *c |= uint32_t(s[3] & 0x3f) << 0;
 
  *c >>= shiftc[len];
 

	
 
  // Accumulate the various error conditions.
 
  *e = (*c < mins[len]) << 6;       // non-canonical encoding
 
  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
 
  *e |= (*c > 0x10FFFF) << 8;       // out of range?
 
  *e |= (s[1] & 0xc0) >> 2;
 
  *e |= (s[2] & 0xc0) >> 4;
 
  *e |= (s[3]) >> 6;
 
  *e ^= 0x2a;  // top two bits of each tail byte correct?
 
  *e >>= shifte[len];
 

	
 
  return next;
 
}
 

	
 
struct stringifier {
 
  template <typename T> FMT_INLINE std::string operator()(T value) const {
 
    return to_string(value);
 
  }
 
  std::string operator()(basic_format_arg<format_context>::handle h) const {
 
    memory_buffer buf;
 
    format_parse_context parse_ctx({});
 
    format_context format_ctx(buffer_appender<char>(buf), {}, {});
 
    h.format(parse_ctx, format_ctx);
 
    return to_string(buf);
 
  }
 
};
 
}  // namespace detail
 

	
 
template <> struct formatter<detail::bigint> {
 
  format_parse_context::iterator parse(format_parse_context& ctx) {
 
  FMT_CONSTEXPR auto parse(format_parse_context& ctx)
 
      -> format_parse_context::iterator {
 
    return ctx.begin();
 
  }
 

	
 
  format_context::iterator format(const detail::bigint& n,
 
                                  format_context& ctx) {
 
  auto format(const detail::bigint& n, format_context& ctx) const
 
      -> format_context::iterator {
 
    auto out = ctx.out();
 
    bool first = true;
 
    for (auto i = n.bigits_.size(); i > 0; --i) {
 
      auto value = n.bigits_[i - 1u];
 
      if (first) {
 
        out = format_to(out, "{:x}", value);
 
        out = format_to(out, FMT_STRING("{:x}"), value);
 
        first = false;
 
        continue;
 
      }
 
      out = format_to(out, "{:08x}", value);
 
      out = format_to(out, FMT_STRING("{:08x}"), value);
 
    }
 
    if (n.exp_ > 0)
 
      out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
 
      out = format_to(out, FMT_STRING("p{}"),
 
                      n.exp_ * detail::bigint::bigit_bits);
 
    return out;
 
  }
 
};
 

	
 
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
 
  auto transcode = [this](const char* p) {
 
    auto cp = uint32_t();
 
    auto error = 0;
 
    p = utf8_decode(p, &cp, &error);
 
    if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
 
  for_each_codepoint(s, [this](uint32_t cp, string_view) {
 
    if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
 
    if (cp <= 0xFFFF) {
 
      buffer_.push_back(static_cast<wchar_t>(cp));
 
    } else {
 
@@ -2692,110 +1415,267 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_
 
      buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
 
      buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
 
    }
 
    return p;
 
  };
 
  auto p = s.data();
 
  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
 
  if (s.size() >= block_size) {
 
    for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
 
  }
 
  if (auto num_chars_left = s.data() + s.size() - p) {
 
    char buf[2 * block_size - 1] = {};
 
    memcpy(buf, p, to_unsigned(num_chars_left));
 
    p = buf;
 
    do {
 
      p = transcode(p);
 
    } while (p - buf < num_chars_left);
 
  }
 
    return true;
 
  });
 
  buffer_.push_back(0);
 
}
 

	
 
FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
 
                                  string_view message) FMT_NOEXCEPT {
 
                                  const char* message) noexcept {
 
  FMT_TRY {
 
    memory_buffer buf;
 
    buf.resize(inline_buffer_size);
 
    for (;;) {
 
      char* system_message = &buf[0];
 
      int result =
 
          detail::safe_strerror(error_code, system_message, buf.size());
 
      if (result == 0) {
 
        format_to(detail::buffer_appender<char>(out), "{}: {}", message,
 
                  system_message);
 
    auto ec = std::error_code(error_code, std::generic_category());
 
    write(std::back_inserter(out), std::system_error(ec, message).what());
 
        return;
 
      }
 
      if (result != ERANGE)
 
        break;  // Can't get error message, report error code instead.
 
      buf.resize(buf.size() * 2);
 
    }
 
  }
 
  FMT_CATCH(...) {}
 
  format_error_code(out, error_code, message);
 
}
 

	
 
FMT_FUNC void detail::error_handler::on_error(const char* message) {
 
  FMT_THROW(format_error(message));
 
}
 

	
 
FMT_FUNC void report_system_error(int error_code,
 
                                  fmt::string_view message) FMT_NOEXCEPT {
 
                                  const char* message) noexcept {
 
  report_error(format_system_error, error_code, message);
 
}
 

	
 
FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
 
  if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
 
    auto arg = args.get(0);
 
    if (!arg) error_handler().on_error("argument not found");
 
    return visit_format_arg(stringifier(), arg);
 
  }
 
  memory_buffer buffer;
 
  detail::vformat_to(buffer, format_str, args);
 
FMT_FUNC std::string vformat(string_view fmt, format_args args) {
 
  // Don't optimize the "{}" case to keep the binary size small and because it
 
  // can be better optimized in fmt::format anyway.
 
  auto buffer = memory_buffer();
 
  detail::vformat_to(buffer, fmt, args);
 
  return to_string(buffer);
 
}
 

	
 
#ifdef _WIN32
 
namespace detail {
 
#ifndef _WIN32
 
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
 
#else
 
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
 
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  //
 
    void*, const void*, dword, dword*, void*);
 
}  // namespace detail
 
#endif
 

	
 
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
 
  memory_buffer buffer;
 
  detail::vformat_to(buffer, format_str,
 
                     basic_format_args<buffer_context<char>>(args));
 
#ifdef _WIN32
 
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
 
  auto fd = _fileno(f);
 
  if (_isatty(fd)) {
 
    detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
 
    auto written = detail::dword();
 
    if (!detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
 
                               u16.c_str(), static_cast<uint32_t>(u16.size()),
 
                               &written, nullptr)) {
 
      FMT_THROW(format_error("failed to write to console"));
 
    }
 
    return;
 
  }
 
#endif
 
  detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
 
  if (!_isatty(fd)) return false;
 
  auto u16 = utf8_to_utf16(text);
 
  auto written = dword();
 
  return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
 
                       static_cast<uint32_t>(u16.size()), &written, nullptr);
 
}
 

	
 
#ifdef _WIN32
 
// Print assuming legacy (non-Unicode) encoding.
 
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
 
                                      format_args args) {
 
  memory_buffer buffer;
 
  detail::vformat_to(buffer, format_str,
 
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
 
  auto buffer = memory_buffer();
 
  detail::vformat_to(buffer, fmt,
 
                     basic_format_args<buffer_context<char>>(args));
 
  fwrite_fully(buffer.data(), 1, buffer.size(), f);
 
}
 
#endif
 

	
 
FMT_FUNC void vprint(string_view format_str, format_args args) {
 
  vprint(stdout, format_str, args);
 
FMT_FUNC void print(std::FILE* f, string_view text) {
 
  if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
 
}
 
}  // namespace detail
 

	
 
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
 
  auto buffer = memory_buffer();
 
  detail::vformat_to(buffer, fmt, args);
 
  detail::print(f, {buffer.data(), buffer.size()});
 
}
 

	
 
FMT_FUNC void vprint(string_view fmt, format_args args) {
 
  vprint(stdout, fmt, args);
 
}
 

	
 
namespace detail {
 

	
 
struct singleton {
 
  unsigned char upper;
 
  unsigned char lower_count;
 
};
 

	
 
inline auto is_printable(uint16_t x, const singleton* singletons,
 
                         size_t singletons_size,
 
                         const unsigned char* singleton_lowers,
 
                         const unsigned char* normal, size_t normal_size)
 
    -> bool {
 
  auto upper = x >> 8;
 
  auto lower_start = 0;
 
  for (size_t i = 0; i < singletons_size; ++i) {
 
    auto s = singletons[i];
 
    auto lower_end = lower_start + s.lower_count;
 
    if (upper < s.upper) break;
 
    if (upper == s.upper) {
 
      for (auto j = lower_start; j < lower_end; ++j) {
 
        if (singleton_lowers[j] == (x & 0xff)) return false;
 
      }
 
    }
 
    lower_start = lower_end;
 
  }
 

	
 
  auto xsigned = static_cast<int>(x);
 
  auto current = true;
 
  for (size_t i = 0; i < normal_size; ++i) {
 
    auto v = static_cast<int>(normal[i]);
 
    auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
 
    xsigned -= len;
 
    if (xsigned < 0) break;
 
    current = !current;
 
  }
 
  return current;
 
}
 

	
 
// This code is generated by support/printable.py.
 
FMT_FUNC auto is_printable(uint32_t cp) -> bool {
 
  static constexpr singleton singletons0[] = {
 
      {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3},  {0x07, 6},  {0x08, 8},
 
      {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
 
      {0x0f, 4},  {0x10, 3},  {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
 
      {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2},  {0x1d, 1},  {0x1f, 22},
 
      {0x20, 3},  {0x2b, 3},  {0x2c, 2},  {0x2d, 11}, {0x2e, 1},  {0x30, 3},
 
      {0x31, 2},  {0x32, 1},  {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
 
      {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3},  {0xff, 9},
 
  };
 
  static constexpr unsigned char singletons0_lower[] = {
 
      0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
 
      0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
 
      0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
 
      0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
 
      0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
 
      0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
 
      0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
 
      0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
 
      0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
 
      0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
 
      0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
 
      0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
 
      0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
 
      0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
 
      0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
 
      0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
 
      0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
 
      0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
 
      0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
 
      0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
 
      0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
 
      0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
 
      0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
 
      0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
 
      0xfe, 0xff,
 
  };
 
  static constexpr singleton singletons1[] = {
 
      {0x00, 6},  {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2},
 
      {0x0a, 5},  {0x0b, 2}, {0x0e, 4},  {0x10, 1}, {0x11, 2},  {0x12, 5},
 
      {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5},
 
      {0x1d, 8},  {0x24, 1}, {0x6a, 3},  {0x6b, 2}, {0xbc, 2},  {0xd1, 2},
 
      {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1},  {0xe0, 5},
 
      {0xe1, 2},  {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2},
 
      {0xfa, 2},  {0xfb, 1},
 
  };
 
  static constexpr unsigned char singletons1_lower[] = {
 
      0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
 
      0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
 
      0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
 
      0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
 
      0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
 
      0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
 
      0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
 
      0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
 
      0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
 
      0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
 
      0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
 
      0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
 
      0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
 
      0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
 
      0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
 
  };
 
  static constexpr unsigned char normal0[] = {
 
      0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
 
      0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
 
      0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
 
      0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
 
      0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
 
      0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
 
      0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
 
      0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
 
      0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
 
      0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
 
      0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
 
      0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
 
      0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
 
      0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
 
      0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
 
      0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
 
      0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
 
      0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
 
      0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
 
      0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
 
      0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
 
      0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
 
      0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
 
      0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
 
      0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
 
      0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
 
  };
 
  static constexpr unsigned char normal1[] = {
 
      0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
 
      0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
 
      0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
 
      0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
 
      0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
 
      0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
 
      0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
 
      0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
 
      0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
 
      0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
 
      0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
 
      0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
 
      0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
 
      0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
 
      0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
 
      0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
 
      0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
 
      0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
 
      0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
 
      0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
 
      0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
 
      0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
 
      0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
 
      0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
 
      0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
 
      0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
 
      0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
 
      0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
 
      0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
 
      0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
 
      0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
 
      0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
 
      0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
 
      0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
 
      0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
 
  };
 
  auto lower = static_cast<uint16_t>(cp);
 
  if (cp < 0x10000) {
 
    return is_printable(lower, singletons0,
 
                        sizeof(singletons0) / sizeof(*singletons0),
 
                        singletons0_lower, normal0, sizeof(normal0));
 
  }
 
  if (cp < 0x20000) {
 
    return is_printable(lower, singletons1,
 
                        sizeof(singletons1) / sizeof(*singletons1),
 
                        singletons1_lower, normal1, sizeof(normal1));
 
  }
 
  if (0x2a6de <= cp && cp < 0x2a700) return false;
 
  if (0x2b735 <= cp && cp < 0x2b740) return false;
 
  if (0x2b81e <= cp && cp < 0x2b820) return false;
 
  if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
 
  if (0x2ebe1 <= cp && cp < 0x2f800) return false;
 
  if (0x2fa1e <= cp && cp < 0x30000) return false;
 
  if (0x3134b <= cp && cp < 0xe0100) return false;
 
  if (0xe01f0 <= cp && cp < 0x110000) return false;
 
  return cp < 0x110000;
 
}
 

	
 
}  // namespace detail
 

	
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_FORMAT_INL_H_
src/3rdparty/fmt/format.h
Show inline comments
 
@@ -33,22 +33,55 @@
 
#ifndef FMT_FORMAT_H_
 
#define FMT_FORMAT_H_
 

	
 
#include <algorithm>
 
#include <cerrno>
 
#include <cmath>
 
#include <cstdint>
 
#include <limits>
 
#include <memory>
 
#include <stdexcept>
 
#include <cmath>             // std::signbit
 
#include <cstdint>           // uint32_t
 
#include <cstring>           // std::memcpy
 
#include <initializer_list>  // std::initializer_list
 
#include <limits>            // std::numeric_limits
 
#include <memory>            // std::uninitialized_copy
 
#include <stdexcept>         // std::runtime_error
 
#include <system_error>      // std::system_error
 

	
 
#ifdef __cpp_lib_bit_cast
 
#  include <bit>  // std::bitcast
 
#endif
 

	
 
#include "core.h"
 

	
 
#ifdef __INTEL_COMPILER
 
#  define FMT_ICC_VERSION __INTEL_COMPILER
 
#elif defined(__ICL)
 
#  define FMT_ICC_VERSION __ICL
 
#ifndef FMT_BEGIN_DETAIL_NAMESPACE
 
#  define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
 
#  define FMT_END_DETAIL_NAMESPACE }
 
#endif
 

	
 
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
 
#  define FMT_FALLTHROUGH [[fallthrough]]
 
#elif defined(__clang__)
 
#  define FMT_FALLTHROUGH [[clang::fallthrough]]
 
#elif FMT_GCC_VERSION >= 700 && \
 
    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
 
#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
 
#else
 
#  define FMT_ICC_VERSION 0
 
#  define FMT_FALLTHROUGH
 
#endif
 

	
 
#ifndef FMT_DEPRECATED
 
#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
 
#    define FMT_DEPRECATED [[deprecated]]
 
#  else
 
#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
 
#      define FMT_DEPRECATED __attribute__((deprecated))
 
#    elif FMT_MSC_VERSION
 
#      define FMT_DEPRECATED __declspec(deprecated)
 
#    else
 
#      define FMT_DEPRECATED /* deprecated */
 
#    endif
 
#  endif
 
#endif
 

	
 
#if FMT_GCC_VERSION
 
#  define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
 
#else
 
#  define FMT_GCC_VISIBILITY_HIDDEN
 
#endif
 

	
 
#ifdef __NVCC__
 
@@ -69,35 +102,9 @@
 
#  define FMT_NOINLINE
 
#endif
 

	
 
#if __cplusplus == 201103L || __cplusplus == 201402L
 
#  if defined(__INTEL_COMPILER) || defined(__PGI)
 
#    define FMT_FALLTHROUGH
 
#  elif defined(__clang__)
 
#    define FMT_FALLTHROUGH [[clang::fallthrough]]
 
#  elif FMT_GCC_VERSION >= 700 && \
 
      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
 
#    define FMT_FALLTHROUGH [[gnu::fallthrough]]
 
#  else
 
#    define FMT_FALLTHROUGH
 
#  endif
 
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
 
    (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
 
#  define FMT_FALLTHROUGH [[fallthrough]]
 
#else
 
#  define FMT_FALLTHROUGH
 
#endif
 

	
 
#ifndef FMT_MAYBE_UNUSED
 
#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
 
#    define FMT_MAYBE_UNUSED [[maybe_unused]]
 
#  else
 
#    define FMT_MAYBE_UNUSED
 
#  endif
 
#endif
 

	
 
#ifndef FMT_THROW
 
#  if FMT_EXCEPTIONS
 
#    if FMT_MSC_VER || FMT_NVCC
 
#    if FMT_MSC_VERSION || defined(__NVCC__)
 
FMT_BEGIN_NAMESPACE
 
namespace detail {
 
template <typename Exception> inline void do_throw(const Exception& x) {
 
@@ -115,8 +122,7 @@ FMT_END_NAMESPACE
 
#  else
 
#    define FMT_THROW(x)              \
 
      do {                            \
 
        static_cast<void>(sizeof(x)); \
 
        FMT_ASSERT(false, "");        \
 
        FMT_ASSERT(false, (x).what()); \
 
      } while (false)
 
#  endif
 
#endif
 
@@ -129,10 +135,18 @@ FMT_END_NAMESPACE
 
#  define FMT_CATCH(x) if (false)
 
#endif
 

	
 
#ifndef FMT_MAYBE_UNUSED
 
#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
 
#    define FMT_MAYBE_UNUSED [[maybe_unused]]
 
#  else
 
#    define FMT_MAYBE_UNUSED
 
#  endif
 
#endif
 

	
 
#ifndef FMT_USE_USER_DEFINED_LITERALS
 
// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
 
#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
 
       FMT_MSC_VER >= 1900) &&                                         \
 
       FMT_MSC_VERSION >= 1900) &&                                     \
 
      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
 
#    define FMT_USE_USER_DEFINED_LITERALS 1
 
#  else
 
@@ -140,117 +154,101 @@ FMT_END_NAMESPACE
 
#  endif
 
#endif
 

	
 
#ifndef FMT_USE_UDL_TEMPLATE
 
// EDG frontend based compilers (icc, nvcc, PGI, etc) and GCC < 6.4 do not
 
// properly support UDL templates and GCC >= 9 warns about them.
 
#  if FMT_USE_USER_DEFINED_LITERALS &&                         \
 
      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \
 
      ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) ||   \
 
       FMT_CLANG_VERSION >= 304) &&                            \
 
      !defined(__PGI) && !defined(__NVCC__)
 
#    define FMT_USE_UDL_TEMPLATE 1
 
#  else
 
#    define FMT_USE_UDL_TEMPLATE 0
 
#  endif
 
#endif
 

	
 
#ifndef FMT_USE_FLOAT
 
#  define FMT_USE_FLOAT 1
 
#endif
 

	
 
#ifndef FMT_USE_DOUBLE
 
#  define FMT_USE_DOUBLE 1
 
#endif
 

	
 
#ifndef FMT_USE_LONG_DOUBLE
 
#  define FMT_USE_LONG_DOUBLE 1
 
#endif
 

	
 
// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
 
// int_writer template instances to just one by only using the largest integer
 
// type. This results in a reduction in binary size but will cause a decrease in
 
// integer formatting performance.
 
// integer formatter template instantiations to just one by only using the
 
// largest integer type. This results in a reduction in binary size but will
 
// cause a decrease in integer formatting performance.
 
#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
 
#  define FMT_REDUCE_INT_INSTANTIATIONS 0
 
#endif
 

	
 
// __builtin_clz is broken in clang with Microsoft CodeGen:
 
// https://github.com/fmtlib/fmt/issues/519
 
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
 
// https://github.com/fmtlib/fmt/issues/519.
 
#if !FMT_MSC_VERSION
 
#  if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
 
#  define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
 
#endif
 
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER
 
#  if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
 
#  define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
 
#endif
 
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz))
 
#endif
 

	
 
// __builtin_ctz is broken in Intel Compiler Classic on Windows:
 
// https://github.com/fmtlib/fmt/issues/2510.
 
#ifndef __ICL
 
#  if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
 
      defined(__NVCOMPILER)
 
#  define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
 
#endif
 
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll))
 
#  if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
 
      FMT_ICC_VERSION || defined(__NVCOMPILER)
 
#  define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
 
#endif
 

	
 
#if FMT_MSC_VER
 
#endif
 

	
 
#if FMT_MSC_VERSION
 
#  include <intrin.h>  // _BitScanReverse[64], _BitScanForward[64], _umul128
 
#endif
 

	
 
// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
 
// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
 
// MSVC intrinsics if the clz and clzll builtins are not available.
 
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && \
 
    !defined(FMT_BUILTIN_CTZLL) && !defined(_MANAGED)
 
#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
 
    !defined(FMT_BUILTIN_CTZLL)
 
FMT_BEGIN_NAMESPACE
 
namespace detail {
 
// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
 
#  ifndef __clang__
 
#  if !defined(__clang__)
 
#    pragma intrinsic(_BitScanForward)
 
#    pragma intrinsic(_BitScanReverse)
 
#  endif
 
#  if defined(_WIN64) && !defined(__clang__)
 
#    if defined(_WIN64)
 
#    pragma intrinsic(_BitScanForward64)
 
#    pragma intrinsic(_BitScanReverse64)
 
#  endif
 

	
 
inline int clz(uint32_t x) {
 
#  endif
 

	
 
inline auto clz(uint32_t x) -> int {
 
  unsigned long r = 0;
 
  _BitScanReverse(&r, x);
 
  FMT_ASSERT(x != 0, "");
 
  // Static analysis complains about using uninitialized data
 
  // "r", but the only way that can happen is if "x" is 0,
 
  // which the callers guarantee to not happen.
 
  FMT_SUPPRESS_MSC_WARNING(6102)
 
  FMT_MSC_WARNING(suppress : 6102)
 
  return 31 ^ static_cast<int>(r);
 
}
 
#  define FMT_BUILTIN_CLZ(n) detail::clz(n)
 

	
 
inline int clzll(uint64_t x) {
 
inline auto clzll(uint64_t x) -> int {
 
  unsigned long r = 0;
 
#  ifdef _WIN64
 
  _BitScanReverse64(&r, x);
 
#  else
 
  // Scan the high 32 bits.
 
  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
 
  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
 
    return 63 ^ static_cast<int>(r + 32);
 
  // Scan the low 32 bits.
 
  _BitScanReverse(&r, static_cast<uint32_t>(x));
 
#  endif
 
  FMT_ASSERT(x != 0, "");
 
  FMT_SUPPRESS_MSC_WARNING(6102)  // Suppress a bogus static analysis warning.
 
  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
 
  return 63 ^ static_cast<int>(r);
 
}
 
#  define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
 

	
 
inline int ctz(uint32_t x) {
 
inline auto ctz(uint32_t x) -> int {
 
  unsigned long r = 0;
 
  _BitScanForward(&r, x);
 
  FMT_ASSERT(x != 0, "");
 
  FMT_SUPPRESS_MSC_WARNING(6102)  // Suppress a bogus static analysis warning.
 
  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
 
  return static_cast<int>(r);
 
}
 
#  define FMT_BUILTIN_CTZ(n) detail::ctz(n)
 

	
 
inline int ctzll(uint64_t x) {
 
inline auto ctzll(uint64_t x) -> int {
 
  unsigned long r = 0;
 
  FMT_ASSERT(x != 0, "");
 
  FMT_SUPPRESS_MSC_WARNING(6102)  // Suppress a bogus static analysis warning.
 
  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
 
#  ifdef _WIN64
 
  _BitScanForward64(&r, x);
 
#  else
 
@@ -267,75 +265,276 @@ inline int ctzll(uint64_t x) {
 
FMT_END_NAMESPACE
 
#endif
 

	
 
// Enable the deprecated numeric alignment.
 
#ifndef FMT_DEPRECATED_NUMERIC_ALIGN
 
#  define FMT_DEPRECATED_NUMERIC_ALIGN 0
 
#endif
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
template <typename...> struct disjunction : std::false_type {};
 
template <typename P> struct disjunction<P> : P {};
 
template <typename P1, typename... Pn>
 
struct disjunction<P1, Pn...>
 
    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
 

	
 
template <typename...> struct conjunction : std::true_type {};
 
template <typename P> struct conjunction<P> : P {};
 
template <typename P1, typename... Pn>
 
struct conjunction<P1, Pn...>
 
    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
 

	
 
namespace detail {
 

	
 
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
 
// undefined behavior (e.g. due to type aliasing).
 
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
 
template <typename Dest, typename Source>
 
inline Dest bit_cast(const Source& source) {
 
  static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
 
  Dest dest;
 
  std::memcpy(&dest, &source, sizeof(dest));
 
  return dest;
 
}
 

	
 
inline bool is_big_endian() {
 
  const auto u = 1u;
 
  struct bytes {
 
    char data[sizeof(u)];
 
FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
 
  ignore_unused(condition);
 
#ifdef FMT_FUZZ
 
  if (condition) throw std::runtime_error("fuzzing limit reached");
 
#endif
 
}
 

	
 
template <typename CharT, CharT... C> struct string_literal {
 
  static constexpr CharT value[sizeof...(C)] = {C...};
 
  constexpr operator basic_string_view<CharT>() const {
 
    return {value, sizeof...(C)};
 
  }
 
  };
 
  return bit_cast<bytes>(u).data[0] == 0;
 
}
 

	
 
// A fallback implementation of uintptr_t for systems that lack it.
 
struct fallback_uintptr {
 
  unsigned char value[sizeof(void*)];
 

	
 
  fallback_uintptr() = default;
 
  explicit fallback_uintptr(const void* p) {
 
    *this = bit_cast<fallback_uintptr>(p);
 
    if (is_big_endian()) {
 
      for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
 
        std::swap(value[i], value[j]);
 
    }
 

	
 
#if FMT_CPLUSPLUS < 201703L
 
template <typename CharT, CharT... C>
 
constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
 
#endif
 

	
 
template <typename Streambuf> class formatbuf : public Streambuf {
 
 private:
 
  using char_type = typename Streambuf::char_type;
 
  using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
 
  using int_type = typename Streambuf::int_type;
 
  using traits_type = typename Streambuf::traits_type;
 

	
 
  buffer<char_type>& buffer_;
 

	
 
 public:
 
  explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
 

	
 
 protected:
 
  // The put area is always empty. This makes the implementation simpler and has
 
  // the advantage that the streambuf and the buffer are always in sync and
 
  // sputc never writes into uninitialized memory. A disadvantage is that each
 
  // call to sputc always results in a (virtual) call to overflow. There is no
 
  // disadvantage here for sputn since this always results in a call to xsputn.
 

	
 
  auto overflow(int_type ch) -> int_type override {
 
    if (!traits_type::eq_int_type(ch, traits_type::eof()))
 
      buffer_.push_back(static_cast<char_type>(ch));
 
    return ch;
 
  }
 

	
 
  auto xsputn(const char_type* s, streamsize count) -> streamsize override {
 
    buffer_.append(s, s + count);
 
    return count;
 
  }
 
};
 

	
 
// Implementation of std::bit_cast for pre-C++20.
 
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
 
FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
 
#ifdef __cpp_lib_bit_cast
 
  if (is_constant_evaluated()) return std::bit_cast<To>(from);
 
#endif
 
  auto to = To();
 
  // The cast suppresses a bogus -Wclass-memaccess on GCC.
 
  std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
 
  return to;
 
}
 

	
 
inline auto is_big_endian() -> bool {
 
#ifdef _WIN32
 
  return false;
 
#elif defined(__BIG_ENDIAN__)
 
  return true;
 
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
 
  return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
 
#else
 
  struct bytes {
 
    char data[sizeof(int)];
 
  };
 
  return bit_cast<bytes>(1).data[0] == 0;
 
#endif
 
}
 

	
 
class uint128_fallback {
 
 private:
 
  uint64_t lo_, hi_;
 

	
 
  friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
 

	
 
 public:
 
  constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
 
  constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
 

	
 
  constexpr uint64_t high() const noexcept { return hi_; }
 
  constexpr uint64_t low() const noexcept { return lo_; }
 

	
 
  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
  constexpr explicit operator T() const {
 
    return static_cast<T>(lo_);
 
  }
 

	
 
  friend constexpr auto operator==(const uint128_fallback& lhs,
 
                                   const uint128_fallback& rhs) -> bool {
 
    return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
 
  }
 
  friend constexpr auto operator!=(const uint128_fallback& lhs,
 
                                   const uint128_fallback& rhs) -> bool {
 
    return !(lhs == rhs);
 
  }
 
  friend constexpr auto operator>(const uint128_fallback& lhs,
 
                                  const uint128_fallback& rhs) -> bool {
 
    return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
 
  }
 
  friend constexpr auto operator|(const uint128_fallback& lhs,
 
                                  const uint128_fallback& rhs)
 
      -> uint128_fallback {
 
    return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
 
  }
 
  friend constexpr auto operator&(const uint128_fallback& lhs,
 
                                  const uint128_fallback& rhs)
 
      -> uint128_fallback {
 
    return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
 
  }
 
  friend constexpr auto operator~(const uint128_fallback& n)
 
      -> uint128_fallback {
 
    return {~n.hi_, ~n.lo_};
 
  }
 
  friend auto operator+(const uint128_fallback& lhs,
 
                        const uint128_fallback& rhs) -> uint128_fallback {
 
    auto result = uint128_fallback(lhs);
 
    result += rhs;
 
    return result;
 
  }
 
  friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
 
      -> uint128_fallback {
 
    FMT_ASSERT(lhs.hi_ == 0, "");
 
    uint64_t hi = (lhs.lo_ >> 32) * rhs;
 
    uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
 
    uint64_t new_lo = (hi << 32) + lo;
 
    return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
 
  }
 
  friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
 
      -> uint128_fallback {
 
    return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
 
  }
 
  FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
 
    if (shift == 64) return {0, hi_};
 
    if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
 
    return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
 
  }
 
  FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
 
    if (shift == 64) return {lo_, 0};
 
    if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
 
    return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
 
  }
 
  FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
 
    return *this = *this >> shift;
 
  }
 
  FMT_CONSTEXPR void operator+=(uint128_fallback n) {
 
    uint64_t new_lo = lo_ + n.lo_;
 
    uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
 
    FMT_ASSERT(new_hi >= hi_, "");
 
    lo_ = new_lo;
 
    hi_ = new_hi;
 
  }
 
  FMT_CONSTEXPR void operator&=(uint128_fallback n) {
 
    lo_ &= n.lo_;
 
    hi_ &= n.hi_;
 
  }
 

	
 
  FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
 
    if (is_constant_evaluated()) {
 
      lo_ += n;
 
      hi_ += (lo_ < n ? 1 : 0);
 
      return *this;
 
    }
 
#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
 
    unsigned long long carry;
 
    lo_ = __builtin_addcll(lo_, n, 0, &carry);
 
    hi_ += carry;
 
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
 
    unsigned long long result;
 
    auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
 
    lo_ = result;
 
    hi_ += carry;
 
#elif defined(_MSC_VER) && defined(_M_X64)
 
    auto carry = _addcarry_u64(0, lo_, n, &lo_);
 
    _addcarry_u64(carry, hi_, 0, &hi_);
 
#else
 
    lo_ += n;
 
    hi_ += (lo_ < n ? 1 : 0);
 
#endif
 
    return *this;
 
  }
 
};
 

	
 
using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
 

	
 
#ifdef UINTPTR_MAX
 
using uintptr_t = ::uintptr_t;
 
inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
 
#else
 
using uintptr_t = fallback_uintptr;
 
inline fallback_uintptr to_uintptr(const void* p) {
 
  return fallback_uintptr(p);
 
}
 
using uintptr_t = uint128_t;
 
#endif
 

	
 
// Returns the largest possible value for type T. Same as
 
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
 
template <typename T> constexpr T max_value() {
 
template <typename T> constexpr auto max_value() -> T {
 
  return (std::numeric_limits<T>::max)();
 
}
 
template <typename T> constexpr int num_bits() {
 
template <typename T> constexpr auto num_bits() -> int {
 
  return std::numeric_limits<T>::digits;
 
}
 
// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
 
template <> constexpr int num_bits<int128_t>() { return 128; }
 
template <> constexpr int num_bits<uint128_t>() { return 128; }
 
template <> constexpr int num_bits<fallback_uintptr>() {
 
  return static_cast<int>(sizeof(void*) *
 
                          std::numeric_limits<unsigned char>::digits);
 
template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
 
template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
 

	
 
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
 
// and 128-bit pointers to uint128_fallback.
 
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
 
inline auto bit_cast(const From& from) -> To {
 
  constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
 
  struct data_t {
 
    unsigned value[static_cast<unsigned>(size)];
 
  } data = bit_cast<data_t>(from);
 
  auto result = To();
 
  if (const_check(is_big_endian())) {
 
    for (int i = 0; i < size; ++i)
 
      result = (result << num_bits<unsigned>()) | data.value[i];
 
  } else {
 
    for (int i = size - 1; i >= 0; --i)
 
      result = (result << num_bits<unsigned>()) | data.value[i];
 
  }
 
  return result;
 
}
 

	
 
template <typename UInt>
 
FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int {
 
  int lz = 0;
 
  constexpr UInt msb_mask = static_cast<UInt>(1) << (num_bits<UInt>() - 1);
 
  for (; (n & msb_mask) == 0; n <<= 1) lz++;
 
  return lz;
 
}
 

	
 
FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int {
 
#ifdef FMT_BUILTIN_CLZ
 
  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n);
 
#endif
 
  return countl_zero_fallback(n);
 
}
 

	
 
FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int {
 
#ifdef FMT_BUILTIN_CLZLL
 
  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n);
 
#endif
 
  return countl_zero_fallback(n);
 
}
 

	
 
FMT_INLINE void assume(bool condition) {
 
  (void)condition;
 
#if FMT_HAS_BUILTIN(__builtin_assume)
 
#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
 
  __builtin_assume(condition);
 
#endif
 
}
 
@@ -346,31 +545,38 @@ using iterator_t = decltype(std::begin(s
 
template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
 

	
 
// A workaround for std::string not having mutable data() until C++17.
 
template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
 
template <typename Char>
 
inline auto get_data(std::basic_string<Char>& s) -> Char* {
 
  return &s[0];
 
}
 
template <typename Container>
 
inline typename Container::value_type* get_data(Container& c) {
 
inline auto get_data(Container& c) -> typename Container::value_type* {
 
  return c.data();
 
}
 

	
 
#if defined(_SECURE_SCL) && _SECURE_SCL
 
// Make a checked iterator to avoid MSVC warnings.
 
template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
 
template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
 
template <typename T>
 
constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
 
  return {p, size};
 
}
 
#else
 
template <typename T> using checked_ptr = T*;
 
template <typename T> inline T* make_checked(T* p, size_t) { return p; }
 
template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
 
  return p;
 
}
 
#endif
 

	
 
// Attempts to reserve space for n extra characters in the output range.
 
// Returns a pointer to the reserved range or a reference to it.
 
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
 
#if FMT_CLANG_VERSION
 
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
 
__attribute__((no_sanitize("undefined")))
 
#endif
 
inline checked_ptr<typename Container::value_type>
 
reserve(std::back_insert_iterator<Container> it, size_t n) {
 
inline auto
 
reserve(std::back_insert_iterator<Container> it, size_t n)
 
    -> checked_ptr<typename Container::value_type> {
 
  Container& c = get_container(it);
 
  size_t size = c.size();
 
  c.resize(size + n);
 
@@ -378,21 +584,26 @@ reserve(std::back_insert_iterator<Contai
 
}
 

	
 
template <typename T>
 
inline buffer_appender<T> reserve(buffer_appender<T> it, size_t n) {
 
inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
 
  buffer<T>& buf = get_container(it);
 
  buf.try_reserve(buf.size() + n);
 
  return it;
 
}
 

	
 
template <typename Iterator> inline Iterator& reserve(Iterator& it, size_t) {
 
template <typename Iterator>
 
constexpr auto reserve(Iterator& it, size_t) -> Iterator& {
 
  return it;
 
}
 

	
 
template <typename OutputIt>
 
using reserve_iterator =
 
    remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
 

	
 
template <typename T, typename OutputIt>
 
constexpr T* to_pointer(OutputIt, size_t) {
 
constexpr auto to_pointer(OutputIt, size_t) -> T* {
 
  return nullptr;
 
}
 
template <typename T> T* to_pointer(buffer_appender<T> it, size_t n) {
 
template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
 
  buffer<T>& buf = get_container(it);
 
  auto size = buf.size();
 
  if (buf.capacity() < size + n) return nullptr;
 
@@ -401,195 +612,262 @@ template <typename T> T* to_pointer(buff
 
}
 

	
 
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
 
inline std::back_insert_iterator<Container> base_iterator(
 
    std::back_insert_iterator<Container>& it,
 
    checked_ptr<typename Container::value_type>) {
 
inline auto base_iterator(std::back_insert_iterator<Container>& it,
 
                          checked_ptr<typename Container::value_type>)
 
    -> std::back_insert_iterator<Container> {
 
  return it;
 
}
 

	
 
template <typename Iterator>
 
inline Iterator base_iterator(Iterator, Iterator it) {
 
constexpr auto base_iterator(Iterator, Iterator it) -> Iterator {
 
  return it;
 
}
 

	
 
// An output iterator that counts the number of objects written to it and
 
// discards them.
 
class counting_iterator {
 
 private:
 
  size_t count_;
 

	
 
 public:
 
  using iterator_category = std::output_iterator_tag;
 
  using difference_type = std::ptrdiff_t;
 
  using pointer = void;
 
  using reference = void;
 
  using _Unchecked_type = counting_iterator;  // Mark iterator as checked.
 

	
 
  struct value_type {
 
    template <typename T> void operator=(const T&) {}
 
  };
 

	
 
  counting_iterator() : count_(0) {}
 

	
 
  size_t count() const { return count_; }
 

	
 
  counting_iterator& operator++() {
 
    ++count_;
 
    return *this;
 
  }
 
  counting_iterator operator++(int) {
 
    auto it = *this;
 
    ++*this;
 
    return it;
 
  }
 

	
 
  friend counting_iterator operator+(counting_iterator it, difference_type n) {
 
    it.count_ += static_cast<size_t>(n);
 
    return it;
 
  }
 

	
 
  value_type operator*() const { return {}; }
 
};
 

	
 
template <typename OutputIt> class truncating_iterator_base {
 
 protected:
 
  OutputIt out_;
 
  size_t limit_;
 
  size_t count_;
 

	
 
  truncating_iterator_base(OutputIt out, size_t limit)
 
      : out_(out), limit_(limit), count_(0) {}
 

	
 
 public:
 
  using iterator_category = std::output_iterator_tag;
 
  using value_type = typename std::iterator_traits<OutputIt>::value_type;
 
  using difference_type = void;
 
  using pointer = void;
 
  using reference = void;
 
  using _Unchecked_type =
 
      truncating_iterator_base;  // Mark iterator as checked.
 

	
 
  OutputIt base() const { return out_; }
 
  size_t count() const { return count_; }
 
// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
 
// instead (#1998).
 
template <typename OutputIt, typename Size, typename T>
 
FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
 
    -> OutputIt {
 
  for (Size i = 0; i < count; ++i) *out++ = value;
 
  return out;
 
}
 
template <typename T, typename Size>
 
FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
 
  if (is_constant_evaluated()) {
 
    return fill_n<T*, Size, T>(out, count, value);
 
  }
 
  std::memset(out, value, to_unsigned(count));
 
  return out + count;
 
}
 

	
 
#ifdef __cpp_char8_t
 
using char8_type = char8_t;
 
#else
 
enum char8_type : unsigned char {};
 
#endif
 

	
 
template <typename OutChar, typename InputIt, typename OutputIt>
 
FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
 
                                                  OutputIt out) -> OutputIt {
 
  return copy_str<OutChar>(begin, end, out);
 
}
 

	
 
// A public domain branchless UTF-8 decoder by Christopher Wellons:
 
// https://github.com/skeeto/branchless-utf8
 
/* Decode the next character, c, from s, reporting errors in e.
 
 *
 
 * Since this is a branchless decoder, four bytes will be read from the
 
 * buffer regardless of the actual length of the next character. This
 
 * means the buffer _must_ have at least three bytes of zero padding
 
 * following the end of the data stream.
 
 *
 
 * Errors are reported in e, which will be non-zero if the parsed
 
 * character was somehow invalid: invalid byte sequence, non-canonical
 
 * encoding, or a surrogate half.
 
 *
 
 * The function returns a pointer to the next character. When an error
 
 * occurs, this pointer will be a guess that depends on the particular
 
 * error, but it will always advance at least one byte.
 
 */
 
FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
 
    -> const char* {
 
  constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
 
  constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
 
  constexpr const int shiftc[] = {0, 18, 12, 6, 0};
 
  constexpr const int shifte[] = {0, 6, 4, 2, 0};
 

	
 
  int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
 
      [static_cast<unsigned char>(*s) >> 3];
 
  // Compute the pointer to the next character early so that the next
 
  // iteration can start working on the next character. Neither Clang
 
  // nor GCC figure out this reordering on their own.
 
  const char* next = s + len + !len;
 

	
 
  using uchar = unsigned char;
 

	
 
  // Assume a four-byte character and load four bytes. Unused bits are
 
  // shifted out.
 
  *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
 
  *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
 
  *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
 
  *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
 
  *c >>= shiftc[len];
 

	
 
  // Accumulate the various error conditions.
 
  *e = (*c < mins[len]) << 6;       // non-canonical encoding
 
  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
 
  *e |= (*c > 0x10FFFF) << 8;       // out of range?
 
  *e |= (uchar(s[1]) & 0xc0) >> 2;
 
  *e |= (uchar(s[2]) & 0xc0) >> 4;
 
  *e |= uchar(s[3]) >> 6;
 
  *e ^= 0x2a;  // top two bits of each tail byte correct?
 
  *e >>= shifte[len];
 

	
 
  return next;
 
}
 

	
 
constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t();
 

	
 
// Invokes f(cp, sv) for every code point cp in s with sv being the string view
 
// corresponding to the code point. cp is invalid_code_point on error.
 
template <typename F>
 
FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
 
  auto decode = [f](const char* buf_ptr, const char* ptr) {
 
    auto cp = uint32_t();
 
    auto error = 0;
 
    auto end = utf8_decode(buf_ptr, &cp, &error);
 
    bool result = f(error ? invalid_code_point : cp,
 
                    string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
 
    return result ? (error ? buf_ptr + 1 : end) : nullptr;
 
};
 

	
 
// An output iterator that truncates the output and counts the number of objects
 
// written to it.
 
template <typename OutputIt,
 
          typename Enable = typename std::is_void<
 
              typename std::iterator_traits<OutputIt>::value_type>::type>
 
class truncating_iterator;
 

	
 
template <typename OutputIt>
 
class truncating_iterator<OutputIt, std::false_type>
 
    : public truncating_iterator_base<OutputIt> {
 
  mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
 

	
 
 public:
 
  using value_type = typename truncating_iterator_base<OutputIt>::value_type;
 

	
 
  truncating_iterator(OutputIt out, size_t limit)
 
      : truncating_iterator_base<OutputIt>(out, limit) {}
 

	
 
  truncating_iterator& operator++() {
 
    if (this->count_++ < this->limit_) ++this->out_;
 
    return *this;
 
  }
 

	
 
  truncating_iterator operator++(int) {
 
    auto it = *this;
 
    ++*this;
 
    return it;
 
  }
 

	
 
  value_type& operator*() const {
 
    return this->count_ < this->limit_ ? *this->out_ : blackhole_;
 
  }
 
};
 

	
 
template <typename OutputIt>
 
class truncating_iterator<OutputIt, std::true_type>
 
    : public truncating_iterator_base<OutputIt> {
 
 public:
 
  truncating_iterator(OutputIt out, size_t limit)
 
      : truncating_iterator_base<OutputIt>(out, limit) {}
 

	
 
  template <typename T> truncating_iterator& operator=(T val) {
 
    if (this->count_++ < this->limit_) *this->out_++ = val;
 
    return *this;
 
  }
 

	
 
  truncating_iterator& operator++() { return *this; }
 
  truncating_iterator& operator++(int) { return *this; }
 
  truncating_iterator& operator*() { return *this; }
 
};
 

	
 
template <typename Char>
 
inline size_t count_code_points(basic_string_view<Char> s) {
 
  return s.size();
 
}
 

	
 
// Counts the number of code points in a UTF-8 string.
 
inline size_t count_code_points(basic_string_view<char> s) {
 
  const char* data = s.data();
 
  size_t num_code_points = 0;
 
  for (size_t i = 0, size = s.size(); i != size; ++i) {
 
    if ((data[i] & 0xc0) != 0x80) ++num_code_points;
 
  }
 
  return num_code_points;
 
}
 

	
 
inline size_t count_code_points(basic_string_view<char8_type> s) {
 
  return count_code_points(basic_string_view<char>(
 
      reinterpret_cast<const char*>(s.data()), s.size()));
 
  auto p = s.data();
 
  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
 
  if (s.size() >= block_size) {
 
    for (auto end = p + s.size() - block_size + 1; p < end;) {
 
      p = decode(p, p);
 
      if (!p) return;
 
    }
 
  }
 
  if (auto num_chars_left = s.data() + s.size() - p) {
 
    char buf[2 * block_size - 1] = {};
 
    copy_str<char>(p, p + num_chars_left, buf);
 
    const char* buf_ptr = buf;
 
    do {
 
      auto end = decode(buf_ptr, p);
 
      if (!end) return;
 
      p += end - buf_ptr;
 
      buf_ptr = end;
 
    } while (buf_ptr - buf < num_chars_left);
 
  }
 
}
 

	
 
template <typename Char>
 
inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
 
inline auto compute_width(basic_string_view<Char> s) -> size_t {
 
  return s.size();
 
}
 

	
 
// Computes approximate display width of a UTF-8 string.
 
FMT_CONSTEXPR inline size_t compute_width(string_view s) {
 
  size_t num_code_points = 0;
 
  // It is not a lambda for compatibility with C++14.
 
  struct count_code_points {
 
    size_t* count;
 
    FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
 
      *count += detail::to_unsigned(
 
          1 +
 
          (cp >= 0x1100 &&
 
           (cp <= 0x115f ||  // Hangul Jamo init. consonants
 
            cp == 0x2329 ||  // LEFT-POINTING ANGLE BRACKET
 
            cp == 0x232a ||  // RIGHT-POINTING ANGLE BRACKET
 
            // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
 
            (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
 
            (cp >= 0xac00 && cp <= 0xd7a3) ||    // Hangul Syllables
 
            (cp >= 0xf900 && cp <= 0xfaff) ||    // CJK Compatibility Ideographs
 
            (cp >= 0xfe10 && cp <= 0xfe19) ||    // Vertical Forms
 
            (cp >= 0xfe30 && cp <= 0xfe6f) ||    // CJK Compatibility Forms
 
            (cp >= 0xff00 && cp <= 0xff60) ||    // Fullwidth Forms
 
            (cp >= 0xffe0 && cp <= 0xffe6) ||    // Fullwidth Forms
 
            (cp >= 0x20000 && cp <= 0x2fffd) ||  // CJK
 
            (cp >= 0x30000 && cp <= 0x3fffd) ||
 
            // Miscellaneous Symbols and Pictographs + Emoticons:
 
            (cp >= 0x1f300 && cp <= 0x1f64f) ||
 
            // Supplemental Symbols and Pictographs:
 
            (cp >= 0x1f900 && cp <= 0x1f9ff))));
 
      return true;
 
    }
 
  };
 
  // We could avoid branches by using utf8_decode directly.
 
  for_each_codepoint(s, count_code_points{&num_code_points});
 
  return num_code_points;
 
}
 

	
 
inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
 
  return compute_width(
 
      string_view(reinterpret_cast<const char*>(s.data()), s.size()));
 
}
 

	
 
template <typename Char>
 
inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
 
  size_t size = s.size();
 
  return n < size ? n : size;
 
}
 

	
 
// Calculates the index of the nth code point in a UTF-8 string.
 
inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
 
  const char8_type* data = s.data();
 
inline auto code_point_index(string_view s, size_t n) -> size_t {
 
  const char* data = s.data();
 
  size_t num_code_points = 0;
 
  for (size_t i = 0, size = s.size(); i != size; ++i) {
 
    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
 
      return i;
 
    }
 
    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
 
  }
 
  return s.size();
 
}
 

	
 
template <typename InputIt, typename OutChar>
 
using needs_conversion = bool_constant<
 
    std::is_same<typename std::iterator_traits<InputIt>::value_type,
 
                 char>::value &&
 
    std::is_same<OutChar, char8_type>::value>;
 

	
 
template <typename OutChar, typename InputIt, typename OutputIt,
 
          FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
 
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
 
  return std::copy(begin, end, it);
 
}
 

	
 
template <typename OutChar, typename InputIt, typename OutputIt,
 
          FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
 
OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
 
  return std::transform(begin, end, it,
 
                        [](char c) { return static_cast<char8_type>(c); });
 
}
 

	
 
template <typename Char, typename InputIt>
 
inline counting_iterator copy_str(InputIt begin, InputIt end,
 
                                  counting_iterator it) {
 
  return it + (end - begin);
 
}
 
inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
 
    -> size_t {
 
  return code_point_index(
 
      string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
 
}
 

	
 
template <typename T> struct is_integral : std::is_integral<T> {};
 
template <> struct is_integral<int128_opt> : std::true_type {};
 
template <> struct is_integral<uint128_t> : std::true_type {};
 

	
 
template <typename T>
 
using is_signed =
 
    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
 
                                     std::is_same<T, int128_opt>::value>;
 

	
 
template <typename T>
 
using is_fast_float = bool_constant<std::numeric_limits<T>::is_iec559 &&
 
                                    sizeof(T) <= sizeof(double)>;
 
using is_integer =
 
    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
 
                  !std::is_same<T, char>::value &&
 
                  !std::is_same<T, wchar_t>::value>;
 

	
 
#ifndef FMT_USE_FLOAT
 
#  define FMT_USE_FLOAT 1
 
#endif
 
#ifndef FMT_USE_DOUBLE
 
#  define FMT_USE_DOUBLE 1
 
#endif
 
#ifndef FMT_USE_LONG_DOUBLE
 
#  define FMT_USE_LONG_DOUBLE 1
 
#endif
 

	
 
#ifndef FMT_USE_FLOAT128
 
#  ifdef __clang__
 
// Clang emulates GCC, so it has to appear early.
 
#    if FMT_HAS_INCLUDE(<quadmath.h>)
 
#      define FMT_USE_FLOAT128 1
 
#    endif
 
#  elif defined(__GNUC__)
 
// GNU C++:
 
#    if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__)
 
#      define FMT_USE_FLOAT128 1
 
#    endif
 
#  endif
 
#  ifndef FMT_USE_FLOAT128
 
#    define FMT_USE_FLOAT128 0
 
#  endif
 
#endif
 

	
 
#if FMT_USE_FLOAT128
 
using float128 = __float128;
 
#else
 
using float128 = void;
 
#endif
 
template <typename T> using is_float128 = std::is_same<T, float128>;
 

	
 
template <typename T>
 
using is_floating_point =
 
    bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
 

	
 
template <typename T, bool = std::is_floating_point<T>::value>
 
struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
 
                                     sizeof(T) <= sizeof(double)> {};
 
template <typename T> struct is_fast_float<T, false> : std::false_type {};
 

	
 
template <typename T>
 
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
 

	
 
#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
 
#  define FMT_USE_FULL_CACHE_DRAGONBOX 0
 
@@ -598,7 +876,7 @@ using is_fast_float = bool_constant<std:
 
template <typename T>
 
template <typename U>
 
void buffer<T>::append(const U* begin, const U* end) {
 
  do {
 
  while (begin != end) {
 
    auto count = to_unsigned(end - begin);
 
    try_reserve(size_ + count);
 
    auto free_cap = capacity_ - size_;
 
@@ -606,16 +884,17 @@ void buffer<T>::append(const U* begin, c
 
    std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
 
    size_ += count;
 
    begin += count;
 
  } while (begin != end);
 
}
 

	
 
template <typename OutputIt, typename T, typename Traits>
 
void iterator_buffer<OutputIt, T, Traits>::flush() {
 
  out_ = std::copy_n(data_, this->limit(this->size()), out_);
 
  this->clear();
 
}
 
  }
 
}
 

	
 
template <typename T, typename Enable = void>
 
struct is_locale : std::false_type {};
 
template <typename T>
 
struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
 
}  // namespace detail
 

	
 
FMT_BEGIN_EXPORT
 

	
 
// The number of characters to store in the basic_memory_buffer object itself
 
// to avoid dynamic memory allocation.
 
enum { inline_buffer_size = 500 };
 
@@ -625,20 +904,12 @@ enum { inline_buffer_size = 500 };
 
  A dynamically growing memory buffer for trivially copyable/constructible types
 
  with the first ``SIZE`` elements stored in the object itself.
 

	
 
  You can use one of the following type aliases for common character types:
 

	
 
  +----------------+------------------------------+
 
  | Type           | Definition                   |
 
  +================+==============================+
 
  | memory_buffer  | basic_memory_buffer<char>    |
 
  +----------------+------------------------------+
 
  | wmemory_buffer | basic_memory_buffer<wchar_t> |
 
  +----------------+------------------------------+
 
  You can use the ``memory_buffer`` type alias for ``char`` instead.
 

	
 
  **Example**::
 

	
 
     fmt::memory_buffer out;
 
     format_to(out, "The answer is {}.", 42);
 
     auto out = fmt::memory_buffer();
 
     format_to(std::back_inserter(out), "The answer is {}.", 42);
 

	
 
  This will append the following output to the ``out`` object:
 

	
 
@@ -659,92 +930,21 @@ class basic_memory_buffer final : public
 
  Allocator alloc_;
 

	
 
  // Deallocate memory allocated by the buffer.
 
  void deallocate() {
 
  FMT_CONSTEXPR20 void deallocate() {
 
    T* data = this->data();
 
    if (data != store_) alloc_.deallocate(data, this->capacity());
 
  }
 

	
 
 protected:
 
  void grow(size_t size) final FMT_OVERRIDE;
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  explicit basic_memory_buffer(const Allocator& alloc = Allocator())
 
      : alloc_(alloc) {
 
    this->set(store_, SIZE);
 
  }
 
  ~basic_memory_buffer() { deallocate(); }
 

	
 
 private:
 
  // Move data from other to this buffer.
 
  void move(basic_memory_buffer& other) {
 
    alloc_ = std::move(other.alloc_);
 
    T* data = other.data();
 
    size_t size = other.size(), capacity = other.capacity();
 
    if (data == other.store_) {
 
      this->set(store_, capacity);
 
      std::uninitialized_copy(other.store_, other.store_ + size,
 
                              detail::make_checked(store_, capacity));
 
    } else {
 
      this->set(data, capacity);
 
      // Set pointer to the inline array so that delete is not called
 
      // when deallocating.
 
      other.set(other.store_, 0);
 
    }
 
    this->resize(size);
 
  }
 

	
 
 public:
 
  /**
 
    \rst
 
    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
 
    of the other object to it.
 
    \endrst
 
   */
 
  basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
 

	
 
  /**
 
    \rst
 
    Moves the content of the other ``basic_memory_buffer`` object to this one.
 
    \endrst
 
   */
 
  basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
 
    FMT_ASSERT(this != &other, "");
 
    deallocate();
 
    move(other);
 
    return *this;
 
  }
 

	
 
  // Returns a copy of the allocator associated with this buffer.
 
  Allocator get_allocator() const { return alloc_; }
 

	
 
  /**
 
    Resizes the buffer to contain *count* elements. If T is a POD type new
 
    elements may not be initialized.
 
   */
 
  void resize(size_t count) { this->try_resize(count); }
 

	
 
  /** Increases the buffer capacity to *new_capacity*. */
 
  void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
 

	
 
  // Directly append data into the buffer
 
  using detail::buffer<T>::append;
 
  template <typename ContiguousRange>
 
  void append(const ContiguousRange& range) {
 
    append(range.data(), range.data() + range.size());
 
  }
 
};
 

	
 
template <typename T, size_t SIZE, typename Allocator>
 
void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size) {
 
#ifdef FMT_FUZZ
 
  if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much");
 
#endif
 
  FMT_CONSTEXPR20 void grow(size_t size) override {
 
    detail::abort_fuzzing_if(size > 5000);
 
    const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
 
  size_t old_capacity = this->capacity();
 
  size_t new_capacity = old_capacity + old_capacity / 2;
 
  if (size > new_capacity) new_capacity = size;
 
    if (size > new_capacity)
 
      new_capacity = size;
 
    else if (new_capacity > max_size)
 
      new_capacity = size > max_size ? size : max_size;
 
  T* old_data = this->data();
 
  T* new_data =
 
      std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
 
@@ -758,50 +958,195 @@ void basic_memory_buffer<T, SIZE, Alloca
 
  if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
 
}
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  FMT_CONSTEXPR20 explicit basic_memory_buffer(
 
      const Allocator& alloc = Allocator())
 
      : alloc_(alloc) {
 
    this->set(store_, SIZE);
 
    if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
 
  }
 
  FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
 

	
 
 private:
 
  // Move data from other to this buffer.
 
  FMT_CONSTEXPR20 void move(basic_memory_buffer& other) {
 
    alloc_ = std::move(other.alloc_);
 
    T* data = other.data();
 
    size_t size = other.size(), capacity = other.capacity();
 
    if (data == other.store_) {
 
      this->set(store_, capacity);
 
      detail::copy_str<T>(other.store_, other.store_ + size,
 
                          detail::make_checked(store_, capacity));
 
    } else {
 
      this->set(data, capacity);
 
      // Set pointer to the inline array so that delete is not called
 
      // when deallocating.
 
      other.set(other.store_, 0);
 
      other.clear();
 
    }
 
    this->resize(size);
 
  }
 

	
 
 public:
 
  /**
 
    \rst
 
    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
 
    of the other object to it.
 
    \endrst
 
   */
 
  FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
 
    move(other);
 
  }
 

	
 
  /**
 
    \rst
 
    Moves the content of the other ``basic_memory_buffer`` object to this one.
 
    \endrst
 
   */
 
  auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
 
    FMT_ASSERT(this != &other, "");
 
    deallocate();
 
    move(other);
 
    return *this;
 
  }
 

	
 
  // Returns a copy of the allocator associated with this buffer.
 
  auto get_allocator() const -> Allocator { return alloc_; }
 

	
 
  /**
 
    Resizes the buffer to contain *count* elements. If T is a POD type new
 
    elements may not be initialized.
 
   */
 
  FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
 

	
 
  /** Increases the buffer capacity to *new_capacity*. */
 
  void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
 

	
 
  // Directly append data into the buffer
 
  using detail::buffer<T>::append;
 
  template <typename ContiguousRange>
 
  void append(const ContiguousRange& range) {
 
    append(range.data(), range.data() + range.size());
 
  }
 
};
 

	
 
using memory_buffer = basic_memory_buffer<char>;
 
using wmemory_buffer = basic_memory_buffer<wchar_t>;
 

	
 
template <typename T, size_t SIZE, typename Allocator>
 
struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
 
};
 

	
 
/** A formatting error such as invalid format string. */
 
FMT_CLASS_API
 
FMT_END_EXPORT
 
namespace detail {
 
FMT_API bool write_console(std::FILE* f, string_view text);
 
FMT_API void print(std::FILE*, string_view);
 
}  // namespace detail
 
FMT_BEGIN_EXPORT
 

	
 
// Suppress a misleading warning in older versions of clang.
 
#if FMT_CLANG_VERSION
 
#  pragma clang diagnostic ignored "-Wweak-vtables"
 
#endif
 

	
 
/** An error reported from a formatting function. */
 
class FMT_API format_error : public std::runtime_error {
 
 public:
 
  explicit format_error(const char* message) : std::runtime_error(message) {}
 
  explicit format_error(const std::string& message)
 
      : std::runtime_error(message) {}
 
  format_error(const format_error&) = default;
 
  format_error& operator=(const format_error&) = default;
 
  format_error(format_error&&) = default;
 
  format_error& operator=(format_error&&) = default;
 
  ~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
 
  using std::runtime_error::runtime_error;
 
};
 

	
 
namespace detail_exported {
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
template <typename Char, size_t N> struct fixed_string {
 
  constexpr fixed_string(const Char (&str)[N]) {
 
    detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
 
                                               str + N, data);
 
  }
 
  Char data[N] = {};
 
};
 

	
 
namespace detail {
 

	
 
template <typename T>
 
using is_signed =
 
    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
 
                                     std::is_same<T, int128_t>::value>;
 
#endif
 

	
 
// Converts a compile-time string to basic_string_view.
 
template <typename Char, size_t N>
 
constexpr auto compile_string_to_view(const Char (&s)[N])
 
    -> basic_string_view<Char> {
 
  // Remove trailing NUL character if needed. Won't be present if this is used
 
  // with a raw character array (i.e. not defined as a string).
 
  return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
 
}
 
template <typename Char>
 
constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
 
    -> basic_string_view<Char> {
 
  return {s.data(), s.size()};
 
}
 
}  // namespace detail_exported
 

	
 
class loc_value {
 
 private:
 
  basic_format_arg<format_context> value_;
 

	
 
 public:
 
  template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
 
  loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
 

	
 
  template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
 
  loc_value(T) {}
 

	
 
  template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
 
    return visit_format_arg(vis, value_);
 
  }
 
};
 

	
 
// A locale facet that formats values in UTF-8.
 
// It is parameterized on the locale to avoid the heavy <locale> include.
 
template <typename Locale> class format_facet : public Locale::facet {
 
 private:
 
  std::string separator_;
 
  std::string grouping_;
 
  std::string decimal_point_;
 

	
 
 protected:
 
  virtual auto do_put(appender out, loc_value val,
 
                      const format_specs<>& specs) const -> bool;
 

	
 
 public:
 
  static FMT_API typename Locale::id id;
 

	
 
  explicit format_facet(Locale& loc);
 
  explicit format_facet(string_view sep = "",
 
                        std::initializer_list<unsigned char> g = {3},
 
                        std::string decimal_point = ".")
 
      : separator_(sep.data(), sep.size()),
 
        grouping_(g.begin(), g.end()),
 
        decimal_point_(decimal_point) {}
 

	
 
  auto put(appender out, loc_value val, const format_specs<>& specs) const
 
      -> bool {
 
    return do_put(out, val, specs);
 
  }
 
};
 

	
 
FMT_BEGIN_DETAIL_NAMESPACE
 

	
 
// Returns true if value is negative, false otherwise.
 
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
 
template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
 
FMT_CONSTEXPR bool is_negative(T value) {
 
constexpr auto is_negative(T value) -> bool {
 
  return value < 0;
 
}
 
template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
 
FMT_CONSTEXPR bool is_negative(T) {
 
constexpr auto is_negative(T) -> bool {
 
  return false;
 
}
 

	
 
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
 
FMT_CONSTEXPR bool is_supported_floating_point(T) {
 
  return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
 
         (std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
 
         (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
 
template <typename T>
 
FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
 
  if (std::is_same<T, float>()) return FMT_USE_FLOAT;
 
  if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
 
  if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
 
  return true;
 
}
 

	
 
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
 
@@ -811,121 +1156,33 @@ using uint32_or_64_or_128_t =
 
    conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
 
                  uint32_t,
 
                  conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
 

	
 
// 128-bit integer type used internally
 
struct FMT_EXTERN_TEMPLATE_API uint128_wrapper {
 
  uint128_wrapper() = default;
 

	
 
#if FMT_USE_INT128
 
  uint128_t internal_;
 

	
 
  uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT
 
      : internal_{static_cast<uint128_t>(low) |
 
                  (static_cast<uint128_t>(high) << 64)} {}
 

	
 
  uint128_wrapper(uint128_t u) : internal_{u} {}
 

	
 
  uint64_t high() const FMT_NOEXCEPT { return uint64_t(internal_ >> 64); }
 
  uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); }
 

	
 
  uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
 
    internal_ += n;
 
    return *this;
 
  }
 
#else
 
  uint64_t high_;
 
  uint64_t low_;
 

	
 
  uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT : high_{high},
 
                                                              low_{low} {}
 

	
 
  uint64_t high() const FMT_NOEXCEPT { return high_; }
 
  uint64_t low() const FMT_NOEXCEPT { return low_; }
 

	
 
  uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT {
 
#  if defined(_MSC_VER) && defined(_M_X64)
 
    unsigned char carry = _addcarry_u64(0, low_, n, &low_);
 
    _addcarry_u64(carry, high_, 0, &high_);
 
    return *this;
 
#  else
 
    uint64_t sum = low_ + n;
 
    high_ += (sum < low_ ? 1 : 0);
 
    low_ = sum;
 
    return *this;
 
#  endif
 
  }
 
template <typename T>
 
using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
 

	
 
#define FMT_POWERS_OF_10(factor)                                             \
 
  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
 
      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
 
      (factor)*1000000000
 

	
 
// Converts value in the range [0, 100) to a string.
 
constexpr const char* digits2(size_t value) {
 
  // GCC generates slightly better code when value is pointer-size.
 
  return &"0001020304050607080910111213141516171819"
 
         "2021222324252627282930313233343536373839"
 
         "4041424344454647484950515253545556575859"
 
         "6061626364656667686970717273747576777879"
 
         "8081828384858687888990919293949596979899"[value * 2];
 
}
 

	
 
// Sign is a template parameter to workaround a bug in gcc 4.8.
 
template <typename Char, typename Sign> constexpr Char sign(Sign s) {
 
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
 
  static_assert(std::is_same<Sign, sign_t>::value, "");
 
#endif
 
};
 

	
 
// Table entry type for divisibility test used internally
 
template <typename T> struct FMT_EXTERN_TEMPLATE_API divtest_table_entry {
 
  T mod_inv;
 
  T max_quotient;
 
};
 

	
 
// Static data is placed in this class template for the header-only config.
 
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
 
  static const uint64_t powers_of_10_64[];
 
  static const uint32_t zero_or_powers_of_10_32_new[];
 
  static const uint64_t zero_or_powers_of_10_64_new[];
 
  static const uint64_t grisu_pow10_significands[];
 
  static const int16_t grisu_pow10_exponents[];
 
  static const divtest_table_entry<uint32_t> divtest_table_for_pow5_32[];
 
  static const divtest_table_entry<uint64_t> divtest_table_for_pow5_64[];
 
  static const uint64_t dragonbox_pow10_significands_64[];
 
  static const uint128_wrapper dragonbox_pow10_significands_128[];
 
  // log10(2) = 0x0.4d104d427de7fbcc...
 
  static const uint64_t log10_2_significand = 0x4d104d427de7fbcc;
 
#if !FMT_USE_FULL_CACHE_DRAGONBOX
 
  static const uint64_t powers_of_5_64[];
 
  static const uint32_t dragonbox_pow10_recovery_errors[];
 
#endif
 
  // GCC generates slightly better code for pairs than chars.
 
  using digit_pair = char[2];
 
  static const digit_pair digits[];
 
  static const char hex_digits[];
 
  static const char foreground_color[];
 
  static const char background_color[];
 
  static const char reset_color[5];
 
  static const wchar_t wreset_color[5];
 
  static const char signs[];
 
  static const char left_padding_shifts[5];
 
  static const char right_padding_shifts[5];
 

	
 
  // DEPRECATED! These are for ABI compatibility.
 
  static const uint32_t zero_or_powers_of_10_32[];
 
  static const uint64_t zero_or_powers_of_10_64[];
 
};
 

	
 
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
 
// This is a function instead of an array to workaround a bug in GCC10 (#1810).
 
FMT_INLINE uint16_t bsr2log10(int bsr) {
 
  static constexpr uint16_t data[] = {
 
      1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
 
      6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
 
      10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
 
      15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
 
  return data[bsr];
 
}
 

	
 
#ifndef FMT_EXPORTED
 
FMT_EXTERN template struct basic_data<void>;
 
#endif
 

	
 
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
 
struct data : basic_data<> {};
 

	
 
#ifdef FMT_BUILTIN_CLZLL
 
// Returns the number of decimal digits in n. Leading zeros are not counted
 
// except for n == 0 in which case count_digits returns 1.
 
inline int count_digits(uint64_t n) {
 
  // https://github.com/fmtlib/format-benchmark/blob/master/digits10
 
  auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63);
 
  return t - (n < data::zero_or_powers_of_10_64_new[t]);
 
}
 
#else
 
// Fallback version of count_digits used when __builtin_clz is not available.
 
inline int count_digits(uint64_t n) {
 
  return static_cast<Char>("\0-+ "[s]);
 
}
 

	
 
template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
 
  int count = 1;
 
  for (;;) {
 
    // Integer division is slow so do it for a group of four digits instead
 
@@ -939,103 +1196,146 @@ inline int count_digits(uint64_t n) {
 
    count += 4;
 
  }
 
}
 
#endif
 

	
 
#if FMT_USE_INT128
 
inline int count_digits(uint128_t n) {
 
  int count = 1;
 
  for (;;) {
 
    // Integer division is slow so do it for a group of four digits instead
 
    // of for every digit. The idea comes from the talk by Alexandrescu
 
    // "Three Optimization Tips for C++". See speed-test for a comparison.
 
    if (n < 10) return count;
 
    if (n < 100) return count + 1;
 
    if (n < 1000) return count + 2;
 
    if (n < 10000) return count + 3;
 
    n /= 10000U;
 
    count += 4;
 
  }
 
FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
 
  return count_digits_fallback(n);
 
}
 
#endif
 

	
 
#ifdef FMT_BUILTIN_CLZLL
 
// It is a separate function rather than a part of count_digits to workaround
 
// the lack of static constexpr in constexpr functions.
 
inline auto do_count_digits(uint64_t n) -> int {
 
  // This has comparable performance to the version by Kendall Willets
 
  // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
 
  // but uses smaller tables.
 
  // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
 
  static constexpr uint8_t bsr2log10[] = {
 
      1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
 
      6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
 
      10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
 
      15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
 
  auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
 
  static constexpr const uint64_t zero_or_powers_of_10[] = {
 
      0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
 
      10000000000000000000ULL};
 
  return t - (n < zero_or_powers_of_10[t]);
 
}
 
#endif
 

	
 
// Returns the number of decimal digits in n. Leading zeros are not counted
 
// except for n == 0 in which case count_digits returns 1.
 
FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
 
#ifdef FMT_BUILTIN_CLZLL
 
  if (!is_constant_evaluated()) {
 
    return do_count_digits(n);
 
  }
 
#endif
 
  return count_digits_fallback(n);
 
}
 

	
 
// Counts the number of digits in n. BITS = log2(radix).
 
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
 
template <int BITS, typename UInt>
 
FMT_CONSTEXPR auto count_digits(UInt n) -> int {
 
#ifdef FMT_BUILTIN_CLZ
 
  if (!is_constant_evaluated() && num_bits<UInt>() == 32)
 
    return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
 
#endif
 
  // Lambda avoids unreachable code warnings from NVHPC.
 
  return [](UInt m) {
 
  int num_digits = 0;
 
  do {
 
    ++num_digits;
 
  } while ((n >>= BITS) != 0);
 
    } while ((m >>= BITS) != 0);
 
  return num_digits;
 
}
 

	
 
template <> int count_digits<4>(detail::fallback_uintptr n);
 

	
 
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
 
#  define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
 
#elif FMT_MSC_VER
 
#  define FMT_ALWAYS_INLINE __forceinline
 
#else
 
#  define FMT_ALWAYS_INLINE inline
 
#endif
 

	
 
// To suppress unnecessary security cookie checks
 
#if FMT_MSC_VER && !FMT_CLANG_VERSION
 
#  define FMT_SAFEBUFFERS __declspec(safebuffers)
 
#else
 
#  define FMT_SAFEBUFFERS
 
#endif
 
  }(n);
 
}
 

	
 
#ifdef FMT_BUILTIN_CLZ
 
// Optional version of count_digits for better performance on 32-bit platforms.
 
inline int count_digits(uint32_t n) {
 
  auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31);
 
  return t - (n < data::zero_or_powers_of_10_32_new[t]);
 
// It is a separate function rather than a part of count_digits to workaround
 
// the lack of static constexpr in constexpr functions.
 
FMT_INLINE auto do_count_digits(uint32_t n) -> int {
 
// An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
 
// This increments the upper 32 bits (log10(T) - 1) when >= T is added.
 
#  define FMT_INC(T) (((sizeof(#  T) - 1ull) << 32) - T)
 
  static constexpr uint64_t table[] = {
 
      FMT_INC(0),          FMT_INC(0),          FMT_INC(0),           // 8
 
      FMT_INC(10),         FMT_INC(10),         FMT_INC(10),          // 64
 
      FMT_INC(100),        FMT_INC(100),        FMT_INC(100),         // 512
 
      FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),        // 4096
 
      FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),       // 32k
 
      FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),      // 256k
 
      FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),     // 2048k
 
      FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),    // 16M
 
      FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),   // 128M
 
      FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000),  // 1024M
 
      FMT_INC(1000000000), FMT_INC(1000000000)                        // 4B
 
  };
 
  auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
 
  return static_cast<int>((n + inc) >> 32);
 
}
 
#endif
 

	
 
template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
 
// Optional version of count_digits for better performance on 32-bit platforms.
 
FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
 
#ifdef FMT_BUILTIN_CLZ
 
  if (!is_constant_evaluated()) {
 
    return do_count_digits(n);
 
  }
 
#endif
 
  return count_digits_fallback(n);
 
}
 

	
 
template <typename Int> constexpr auto digits10() noexcept -> int {
 
  return std::numeric_limits<Int>::digits10;
 
}
 
template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
 
template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
 

	
 
template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
 
template <typename Char> inline std::string grouping(locale_ref loc) {
 
  return grouping_impl<char>(loc);
 
}
 
template <> inline std::string grouping<wchar_t>(locale_ref loc) {
 
  return grouping_impl<wchar_t>(loc);
 
}
 

	
 
template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
 
template <typename Char> inline Char thousands_sep(locale_ref loc) {
 
  return Char(thousands_sep_impl<char>(loc));
 
}
 
template <> inline wchar_t thousands_sep(locale_ref loc) {
 
template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
 
template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
 

	
 
template <typename Char> struct thousands_sep_result {
 
  std::string grouping;
 
  Char thousands_sep;
 
};
 

	
 
template <typename Char>
 
FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
 
template <typename Char>
 
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
 
  auto result = thousands_sep_impl<char>(loc);
 
  return {result.grouping, Char(result.thousands_sep)};
 
}
 
template <>
 
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
 
  return thousands_sep_impl<wchar_t>(loc);
 
}
 

	
 
template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
 
template <typename Char> inline Char decimal_point(locale_ref loc) {
 
template <typename Char>
 
FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
 
template <typename Char> inline auto decimal_point(locale_ref loc) -> Char {
 
  return Char(decimal_point_impl<char>(loc));
 
}
 
template <> inline wchar_t decimal_point(locale_ref loc) {
 
template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
 
  return decimal_point_impl<wchar_t>(loc);
 
}
 

	
 
// Compares two characters for equality.
 
template <typename Char> bool equal2(const Char* lhs, const char* rhs) {
 
  return lhs[0] == rhs[0] && lhs[1] == rhs[1];
 
}
 
inline bool equal2(const char* lhs, const char* rhs) {
 
template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
 
  return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
 
}
 
inline auto equal2(const char* lhs, const char* rhs) -> bool {
 
  return memcmp(lhs, rhs, 2) == 0;
 
}
 

	
 
// Copies two characters from src to dst.
 
template <typename Char> void copy2(Char* dst, const char* src) {
 
template <typename Char>
 
FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
 
  if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
 
    memcpy(dst, src, 2);
 
    return;
 
  }
 
  *dst++ = static_cast<Char>(*src++);
 
  *dst = static_cast<Char>(*src);
 
}
 
FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
 

	
 
template <typename Iterator> struct format_decimal_result {
 
  Iterator begin;
 
@@ -1046,8 +1346,8 @@ template <typename Iterator> struct form
 
// buffer of specified size. The caller must ensure that the buffer is large
 
// enough.
 
template <typename Char, typename UInt>
 
inline format_decimal_result<Char*> format_decimal(Char* out, UInt value,
 
                                                   int size) {
 
FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
 
    -> format_decimal_result<Char*> {
 
  FMT_ASSERT(size >= count_digits(value), "invalid digit count");
 
  out += size;
 
  Char* end = out;
 
@@ -1056,7 +1356,7 @@ inline format_decimal_result<Char*> form
 
    // of for every digit. The idea comes from the talk by Alexandrescu
 
    // "Three Optimization Tips for C++". See speed-test for a comparison.
 
    out -= 2;
 
    copy2(out, data::digits[value % 100]);
 
    copy2(out, digits2(static_cast<size_t>(value % 100)));
 
    value /= 100;
 
  }
 
  if (value < 10) {
 
@@ -1064,58 +1364,37 @@ inline format_decimal_result<Char*> form
 
    return {out, end};
 
  }
 
  out -= 2;
 
  copy2(out, data::digits[value]);
 
  copy2(out, digits2(static_cast<size_t>(value)));
 
  return {out, end};
 
}
 

	
 
template <typename Char, typename UInt, typename Iterator,
 
          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
 
inline format_decimal_result<Iterator> format_decimal(Iterator out, UInt value,
 
                                                      int size) {
 
FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
 
    -> format_decimal_result<Iterator> {
 
  // Buffer is large enough to hold all digits (digits10 + 1).
 
  Char buffer[digits10<UInt>() + 1];
 
  Char buffer[digits10<UInt>() + 1] = {};
 
  auto end = format_decimal(buffer, value, size).end;
 
  return {out, detail::copy_str<Char>(buffer, end, out)};
 
  return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
 
}
 

	
 
template <unsigned BASE_BITS, typename Char, typename UInt>
 
inline Char* format_uint(Char* buffer, UInt value, int num_digits,
 
                         bool upper = false) {
 
FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
 
                               bool upper = false) -> Char* {
 
  buffer += num_digits;
 
  Char* end = buffer;
 
  do {
 
    const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits;
 
    unsigned digit = (value & ((1 << BASE_BITS) - 1));
 
    const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
 
    unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
 
    *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
 
                                                : digits[digit]);
 
  } while ((value >>= BASE_BITS) != 0);
 
  return end;
 
}
 

	
 
template <unsigned BASE_BITS, typename Char>
 
Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
 
                  bool = false) {
 
  auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
 
  int start = (num_digits + char_digits - 1) / char_digits - 1;
 
  if (int start_digits = num_digits % char_digits) {
 
    unsigned value = n.value[start--];
 
    buffer = format_uint<BASE_BITS>(buffer, value, start_digits);
 
  }
 
  for (; start >= 0; --start) {
 
    unsigned value = n.value[start];
 
    buffer += char_digits;
 
    auto p = buffer;
 
    for (int i = 0; i < char_digits; ++i) {
 
      unsigned digit = (value & ((1 << BASE_BITS) - 1));
 
      *--p = static_cast<Char>(data::hex_digits[digit]);
 
      value >>= BASE_BITS;
 
    }
 
  }
 
  return buffer;
 
}
 

	
 
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
 
inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
 
inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
 
    -> It {
 
  if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
 
    format_uint<BASE_BITS>(ptr, value, num_digits, upper);
 
    return out;
 
@@ -1123,141 +1402,190 @@ inline It format_uint(It out, UInt value
 
  // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
 
  char buffer[num_bits<UInt>() / BASE_BITS + 1];
 
  format_uint<BASE_BITS>(buffer, value, num_digits, upper);
 
  return detail::copy_str<Char>(buffer, buffer + num_digits, out);
 
  return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
 
}
 

	
 
// A converter from UTF-8 to UTF-16.
 
class utf8_to_utf16 {
 
 private:
 
  wmemory_buffer buffer_;
 
  basic_memory_buffer<wchar_t> buffer_;
 

	
 
 public:
 
  FMT_API explicit utf8_to_utf16(string_view s);
 
  operator wstring_view() const { return {&buffer_[0], size()}; }
 
  size_t size() const { return buffer_.size() - 1; }
 
  const wchar_t* c_str() const { return &buffer_[0]; }
 
  std::wstring str() const { return {&buffer_[0], size()}; }
 
  operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
 
  auto size() const -> size_t { return buffer_.size() - 1; }
 
  auto c_str() const -> const wchar_t* { return &buffer_[0]; }
 
  auto str() const -> std::wstring { return {&buffer_[0], size()}; }
 
};
 

	
 
template <typename T = void> struct null {};
 

	
 
// Workaround an array initialization issue in gcc 4.8.
 
template <typename Char> struct fill_t {
 
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
 
template <typename WChar, typename Buffer = memory_buffer>
 
class unicode_to_utf8 {
 
 private:
 
  enum { max_size = 4 };
 
  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
 
  unsigned char size_ = 1;
 
  Buffer buffer_;
 

	
 
 public:
 
  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
 
    auto size = s.size();
 
    if (size > max_size) {
 
      FMT_THROW(format_error("invalid fill"));
 
      return;
 
    }
 
    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
 
    size_ = static_cast<unsigned char>(size);
 
  }
 

	
 
  size_t size() const { return size_; }
 
  const Char* data() const { return data_; }
 

	
 
  FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
 
  FMT_CONSTEXPR const Char& operator[](size_t index) const {
 
    return data_[index];
 
  unicode_to_utf8() {}
 
  explicit unicode_to_utf8(basic_string_view<WChar> s) {
 
    static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
 
                  "Expect utf16 or utf32");
 

	
 
    if (!convert(s))
 
      FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
 
                                                      : "invalid utf32"));
 
  }
 
  operator string_view() const { return string_view(&buffer_[0], size()); }
 
  size_t size() const { return buffer_.size() - 1; }
 
  const char* c_str() const { return &buffer_[0]; }
 
  std::string str() const { return std::string(&buffer_[0], size()); }
 

	
 
  // Performs conversion returning a bool instead of throwing exception on
 
  // conversion error. This method may still throw in case of memory allocation
 
  // error.
 
  bool convert(basic_string_view<WChar> s) {
 
    if (!convert(buffer_, s)) return false;
 
    buffer_.push_back(0);
 
    return true;
 
  }
 
  static bool convert(Buffer& buf, basic_string_view<WChar> s) {
 
    for (auto p = s.begin(); p != s.end(); ++p) {
 
      uint32_t c = static_cast<uint32_t>(*p);
 
      if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
 
        // surrogate pair
 
        ++p;
 
        if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
 
          return false;
 
        }
 
        c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
 
      }
 
      if (c < 0x80) {
 
        buf.push_back(static_cast<char>(c));
 
      } else if (c < 0x800) {
 
        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
 
        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
 
      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
 
        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
 
        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
 
        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
 
      } else if (c >= 0x10000 && c <= 0x10ffff) {
 
        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
 
        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
 
        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
 
        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
 
      } else {
 
        return false;
 
      }
 
    }
 
    return true;
 
  }
 
};
 
}  // namespace detail
 

	
 
// We cannot use enum classes as bit fields because of a gcc bug
 
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
 
namespace align {
 
enum type { none, left, right, center, numeric };
 
}
 
using align_t = align::type;
 

	
 
namespace sign {
 
enum type { none, minus, plus, space };
 
}
 
using sign_t = sign::type;
 

	
 
// Format specifiers for built-in and string types.
 
template <typename Char> struct basic_format_specs {
 
  int width;
 
  int precision;
 
  char type;
 
  align_t align : 4;
 
  sign_t sign : 3;
 
  bool alt : 1;  // Alternate form ('#').
 
  detail::fill_t<Char> fill;
 

	
 
  constexpr basic_format_specs()
 
      : width(0),
 
        precision(-1),
 
        type(0),
 
        align(align::none),
 
        sign(sign::none),
 
        alt(false) {}
 
};
 

	
 
using format_specs = basic_format_specs<char>;
 

	
 
namespace detail {
 

	
 
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
 
inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
 
#if FMT_USE_INT128
 
  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
 
  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
 
#elif defined(_MSC_VER) && defined(_M_X64)
 
  auto result = uint128_fallback();
 
  result.lo_ = _umul128(x, y, &result.hi_);
 
  return result;
 
#else
 
  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
 

	
 
  uint64_t a = x >> 32;
 
  uint64_t b = x & mask;
 
  uint64_t c = y >> 32;
 
  uint64_t d = y & mask;
 

	
 
  uint64_t ac = a * c;
 
  uint64_t bc = b * c;
 
  uint64_t ad = a * d;
 
  uint64_t bd = b * d;
 

	
 
  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
 

	
 
  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
 
          (intermediate << 32) + (bd & mask)};
 
#endif
 
}
 

	
 
namespace dragonbox {
 
// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
 
// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
 
inline int floor_log10_pow2(int e) noexcept {
 
  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
 
  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
 
  return (e * 315653) >> 20;
 
}
 

	
 
inline int floor_log2_pow10(int e) noexcept {
 
  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
 
  return (e * 1741647) >> 19;
 
}
 

	
 
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
 
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
 
#if FMT_USE_INT128
 
  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
 
  return static_cast<uint64_t>(p >> 64);
 
#elif defined(_MSC_VER) && defined(_M_X64)
 
  return __umulh(x, y);
 
#else
 
  return umul128(x, y).high();
 
#endif
 
}
 

	
 
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
 
// 128-bit unsigned integer.
 
inline uint128_fallback umul192_upper128(uint64_t x,
 
                                         uint128_fallback y) noexcept {
 
  uint128_fallback r = umul128(x, y.high());
 
  r += umul128_upper64(x, y.low());
 
  return r;
 
}
 

	
 
FMT_API uint128_fallback get_cached_power(int k) noexcept;
 

	
 
// Type-specific information that Dragonbox uses.
 
template <class T> struct float_info;
 
template <typename T, typename Enable = void> struct float_info;
 

	
 
template <> struct float_info<float> {
 
  using carrier_uint = uint32_t;
 
  static const int significand_bits = 23;
 
  static const int exponent_bits = 8;
 
  static const int min_exponent = -126;
 
  static const int max_exponent = 127;
 
  static const int exponent_bias = -127;
 
  static const int decimal_digits = 9;
 
  static const int kappa = 1;
 
  static const int big_divisor = 100;
 
  static const int small_divisor = 10;
 
  static const int min_k = -31;
 
  static const int max_k = 46;
 
  static const int cache_bits = 64;
 
  static const int divisibility_check_by_5_threshold = 39;
 
  static const int case_fc_pm_half_lower_threshold = -1;
 
  static const int case_fc_pm_half_upper_threshold = 6;
 
  static const int case_fc_lower_threshold = -2;
 
  static const int case_fc_upper_threshold = 6;
 
  static const int case_shorter_interval_left_endpoint_lower_threshold = 2;
 
  static const int case_shorter_interval_left_endpoint_upper_threshold = 3;
 
  static const int shorter_interval_tie_lower_threshold = -35;
 
  static const int shorter_interval_tie_upper_threshold = -35;
 
  static const int max_trailing_zeros = 7;
 
};
 

	
 
template <> struct float_info<double> {
 
  using carrier_uint = uint64_t;
 
  static const int significand_bits = 52;
 
  static const int exponent_bits = 11;
 
  static const int min_exponent = -1022;
 
  static const int max_exponent = 1023;
 
  static const int exponent_bias = -1023;
 
  static const int decimal_digits = 17;
 
  static const int kappa = 2;
 
  static const int big_divisor = 1000;
 
  static const int small_divisor = 100;
 
  static const int min_k = -292;
 
  static const int max_k = 326;
 
  static const int cache_bits = 128;
 
  static const int divisibility_check_by_5_threshold = 86;
 
  static const int case_fc_pm_half_lower_threshold = -2;
 
  static const int case_fc_pm_half_upper_threshold = 9;
 
  static const int case_fc_lower_threshold = -4;
 
  static const int case_fc_upper_threshold = 9;
 
  static const int case_shorter_interval_left_endpoint_lower_threshold = 2;
 
  static const int case_shorter_interval_left_endpoint_upper_threshold = 3;
 
  static const int max_k = 341;
 
  static const int shorter_interval_tie_lower_threshold = -77;
 
  static const int shorter_interval_tie_upper_threshold = -77;
 
  static const int max_trailing_zeros = 16;
 
};
 

	
 
// An 80- or 128-bit floating point number.
 
template <typename T>
 
struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 ||
 
                                 std::numeric_limits<T>::digits == 113 ||
 
                                 is_float128<T>::value>> {
 
  using carrier_uint = detail::uint128_t;
 
  static const int exponent_bits = 15;
 
};
 

	
 
// A double-double floating point number.
 
template <typename T>
 
struct float_info<T, enable_if_t<is_double_double<T>::value>> {
 
  using carrier_uint = detail::uint128_t;
 
};
 

	
 
template <typename T> struct decimal_fp {
 
@@ -1266,14 +1594,885 @@ template <typename T> struct decimal_fp 
 
  int exponent;
 
};
 

	
 
template <typename T> FMT_API decimal_fp<T> to_decimal(T x) FMT_NOEXCEPT;
 
template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
 
}  // namespace dragonbox
 

	
 
// Returns true iff Float has the implicit bit which is not stored.
 
template <typename Float> constexpr bool has_implicit_bit() {
 
  // An 80-bit FP number has a 64-bit significand an no implicit bit.
 
  return std::numeric_limits<Float>::digits != 64;
 
}
 

	
 
// Returns the number of significand bits stored in Float. The implicit bit is
 
// not counted since it is not stored.
 
template <typename Float> constexpr int num_significand_bits() {
 
  // std::numeric_limits may not support __float128.
 
  return is_float128<Float>() ? 112
 
                              : (std::numeric_limits<Float>::digits -
 
                                 (has_implicit_bit<Float>() ? 1 : 0));
 
}
 

	
 
template <typename Float>
 
constexpr auto exponent_mask() ->
 
    typename dragonbox::float_info<Float>::carrier_uint {
 
  using float_uint = typename dragonbox::float_info<Float>::carrier_uint;
 
  return ((float_uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
 
         << num_significand_bits<Float>();
 
}
 
template <typename Float> constexpr auto exponent_bias() -> int {
 
  // std::numeric_limits may not support __float128.
 
  return is_float128<Float>() ? 16383
 
                              : std::numeric_limits<Float>::max_exponent - 1;
 
}
 

	
 
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
 
template <typename Char, typename It>
 
FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
 
  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
 
  if (exp < 0) {
 
    *it++ = static_cast<Char>('-');
 
    exp = -exp;
 
  } else {
 
    *it++ = static_cast<Char>('+');
 
  }
 
  if (exp >= 100) {
 
    const char* top = digits2(to_unsigned(exp / 100));
 
    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
 
    *it++ = static_cast<Char>(top[1]);
 
    exp %= 100;
 
  }
 
  const char* d = digits2(to_unsigned(exp));
 
  *it++ = static_cast<Char>(d[0]);
 
  *it++ = static_cast<Char>(d[1]);
 
  return it;
 
}
 

	
 
// A floating-point number f * pow(2, e) where F is an unsigned type.
 
template <typename F> struct basic_fp {
 
  F f;
 
  int e;
 

	
 
  static constexpr const int num_significand_bits =
 
      static_cast<int>(sizeof(F) * num_bits<unsigned char>());
 

	
 
  constexpr basic_fp() : f(0), e(0) {}
 
  constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
 

	
 
  // Constructs fp from an IEEE754 floating-point number.
 
  template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
 

	
 
  // Assigns n to this and return true iff predecessor is closer than successor.
 
  template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
 
  FMT_CONSTEXPR auto assign(Float n) -> bool {
 
    static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
 
    // Assume Float is in the format [sign][exponent][significand].
 
    using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint;
 
    const auto num_float_significand_bits =
 
        detail::num_significand_bits<Float>();
 
    const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
 
    const auto significand_mask = implicit_bit - 1;
 
    auto u = bit_cast<carrier_uint>(n);
 
    f = static_cast<F>(u & significand_mask);
 
    auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
 
                                     num_float_significand_bits);
 
    // The predecessor is closer if n is a normalized power of 2 (f == 0)
 
    // other than the smallest normalized number (biased_e > 1).
 
    auto is_predecessor_closer = f == 0 && biased_e > 1;
 
    if (biased_e == 0)
 
      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
 
    else if (has_implicit_bit<Float>())
 
      f += static_cast<F>(implicit_bit);
 
    e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
 
    if (!has_implicit_bit<Float>()) ++e;
 
    return is_predecessor_closer;
 
  }
 

	
 
  template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
 
  FMT_CONSTEXPR auto assign(Float n) -> bool {
 
    static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
 
    return assign(static_cast<double>(n));
 
  }
 
};
 

	
 
using fp = basic_fp<unsigned long long>;
 

	
 
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
 
template <int SHIFT = 0, typename F>
 
FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
 
  // Handle subnormals.
 
  const auto implicit_bit = F(1) << num_significand_bits<double>();
 
  const auto shifted_implicit_bit = implicit_bit << SHIFT;
 
  while ((value.f & shifted_implicit_bit) == 0) {
 
    value.f <<= 1;
 
    --value.e;
 
  }
 
  // Subtract 1 to account for hidden bit.
 
  const auto offset = basic_fp<F>::num_significand_bits -
 
                      num_significand_bits<double>() - SHIFT - 1;
 
  value.f <<= offset;
 
  value.e -= offset;
 
  return value;
 
}
 

	
 
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
 
FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
 
#if FMT_USE_INT128
 
  auto product = static_cast<__uint128_t>(lhs) * rhs;
 
  auto f = static_cast<uint64_t>(product >> 64);
 
  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
 
#else
 
  // Multiply 32-bit parts of significands.
 
  uint64_t mask = (1ULL << 32) - 1;
 
  uint64_t a = lhs >> 32, b = lhs & mask;
 
  uint64_t c = rhs >> 32, d = rhs & mask;
 
  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
 
  // Compute mid 64-bit of result and round.
 
  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
 
  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
 
#endif
 
}
 

	
 
FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
 
  return {multiply(x.f, y.f), x.e + y.e + 64};
 
}
 

	
 
template <typename T = void> struct basic_data {
 
  // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
 
  // These are generated by support/compute-powers.py.
 
  static constexpr uint64_t pow10_significands[87] = {
 
      0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
 
      0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
 
      0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
 
      0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
 
      0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
 
      0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
 
      0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
 
      0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
 
      0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
 
      0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
 
      0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
 
      0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
 
      0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
 
      0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
 
      0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
 
      0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
 
      0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
 
      0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
 
      0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
 
      0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
 
      0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
 
      0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
 
      0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
 
      0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
 
      0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
 
      0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
 
      0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
 
      0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
 
      0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
 
  };
 

	
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
#  pragma GCC diagnostic push
 
#  pragma GCC diagnostic ignored "-Wnarrowing"
 
#endif
 
  // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
 
  // to significands above.
 
  static constexpr int16_t pow10_exponents[87] = {
 
      -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
 
      -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
 
      -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
 
      -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
 
      -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
 
      242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
 
      534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
 
      827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
#  pragma GCC diagnostic pop
 
#endif
 

	
 
  static constexpr uint64_t power_of_10_64[20] = {
 
      1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
 
      10000000000000000000ULL};
 

	
 
  // For checking rounding thresholds.
 
  // The kth entry is chosen to be the smallest integer such that the
 
  // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
 
  static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
 
      2576980378,  // ceil(2^31 + 2^32/10^1)
 
      2190433321,  // ceil(2^31 + 2^32/10^2)
 
      2151778616,  // ceil(2^31 + 2^32/10^3)
 
      2147913145,  // ceil(2^31 + 2^32/10^4)
 
      2147526598,  // ceil(2^31 + 2^32/10^5)
 
      2147487943,  // ceil(2^31 + 2^32/10^6)
 
      2147484078,  // ceil(2^31 + 2^32/10^7)
 
      2147483691   // ceil(2^31 + 2^32/10^8)
 
  };
 
};
 

	
 
#if FMT_CPLUSPLUS < 201703L
 
template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
 
template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
 
template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
 
template <typename T>
 
constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
 
#endif
 

	
 
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
 
struct data : basic_data<> {};
 

	
 
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
 
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
 
FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
 
                                         int& pow10_exponent) {
 
  const int shift = 32;
 
  // log10(2) = 0x0.4d104d427de7fbcc...
 
  const int64_t significand = 0x4d104d427de7fbcc;
 
  int index = static_cast<int>(
 
      ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
 
       ((int64_t(1) << shift) - 1))  // ceil
 
      >> 32                          // arithmetic shift
 
  );
 
  // Decimal exponent of the first (smallest) cached power of 10.
 
  const int first_dec_exp = -348;
 
  // Difference between 2 consecutive decimal exponents in cached powers of 10.
 
  const int dec_exp_step = 8;
 
  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
 
  pow10_exponent = first_dec_exp + index * dec_exp_step;
 
  // Using *(x + index) instead of x[index] avoids an issue with some compilers
 
  // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
 
  return {*(data::pow10_significands + index),
 
          *(data::pow10_exponents + index)};
 
}
 

	
 
template <typename T>
 
using convert_float_result =
 
    conditional_t<std::is_same<T, float>::value ||
 
                      std::numeric_limits<T>::digits ==
 
                          std::numeric_limits<double>::digits,
 
                  double, T>;
 

	
 
template <typename T>
 
constexpr typename dragonbox::float_info<T>::carrier_uint exponent_mask() {
 
  using uint = typename dragonbox::float_info<T>::carrier_uint;
 
  return ((uint(1) << dragonbox::float_info<T>::exponent_bits) - 1)
 
         << dragonbox::float_info<T>::significand_bits;
 
constexpr auto convert_float(T value) -> convert_float_result<T> {
 
  return static_cast<convert_float_result<T>>(value);
 
}
 

	
 
template <typename OutputIt, typename Char>
 
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
 
                                     const fill_t<Char>& fill) -> OutputIt {
 
  auto fill_size = fill.size();
 
  if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
 
  auto data = fill.data();
 
  for (size_t i = 0; i < n; ++i)
 
    it = copy_str<Char>(data, data + fill_size, it);
 
  return it;
 
}
 

	
 
// Writes the output of f, padded according to format specifications in specs.
 
// size: output size in code units.
 
// width: output display width in (terminal) column positions.
 
template <align::type align = align::left, typename OutputIt, typename Char,
 
          typename F>
 
FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
 
                                size_t size, size_t width, F&& f) -> OutputIt {
 
  static_assert(align == align::left || align == align::right, "");
 
  unsigned spec_width = to_unsigned(specs.width);
 
  size_t padding = spec_width > width ? spec_width - width : 0;
 
  // Shifts are encoded as string literals because static constexpr is not
 
  // supported in constexpr functions.
 
  auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
 
  size_t left_padding = padding >> shifts[specs.align];
 
  size_t right_padding = padding - left_padding;
 
  auto it = reserve(out, size + padding * specs.fill.size());
 
  if (left_padding != 0) it = fill(it, left_padding, specs.fill);
 
  it = f(it);
 
  if (right_padding != 0) it = fill(it, right_padding, specs.fill);
 
  return base_iterator(out, it);
 
}
 

	
 
template <align::type align = align::left, typename OutputIt, typename Char,
 
          typename F>
 
constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs,
 
                            size_t size, F&& f) -> OutputIt {
 
  return write_padded<align>(out, specs, size, size, f);
 
}
 

	
 
template <align::type align = align::left, typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
 
                               const format_specs<Char>& specs) -> OutputIt {
 
  return write_padded<align>(
 
      out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
 
        const char* data = bytes.data();
 
        return copy_str<Char>(data, data + bytes.size(), it);
 
      });
 
}
 

	
 
template <typename Char, typename OutputIt, typename UIntPtr>
 
auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
 
    -> OutputIt {
 
  int num_digits = count_digits<4>(value);
 
  auto size = to_unsigned(num_digits) + size_t(2);
 
  auto write = [=](reserve_iterator<OutputIt> it) {
 
    *it++ = static_cast<Char>('0');
 
    *it++ = static_cast<Char>('x');
 
    return format_uint<4, Char>(it, value, num_digits);
 
  };
 
  return specs ? write_padded<align::right>(out, *specs, size, write)
 
               : base_iterator(out, write(reserve(out, size)));
 
}
 

	
 
// Returns true iff the code point cp is printable.
 
FMT_API auto is_printable(uint32_t cp) -> bool;
 

	
 
inline auto needs_escape(uint32_t cp) -> bool {
 
  return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
 
         !is_printable(cp);
 
}
 

	
 
template <typename Char> struct find_escape_result {
 
  const Char* begin;
 
  const Char* end;
 
  uint32_t cp;
 
};
 

	
 
template <typename Char>
 
using make_unsigned_char =
 
    typename conditional_t<std::is_integral<Char>::value,
 
                           std::make_unsigned<Char>,
 
                           type_identity<uint32_t>>::type;
 

	
 
template <typename Char>
 
auto find_escape(const Char* begin, const Char* end)
 
    -> find_escape_result<Char> {
 
  for (; begin != end; ++begin) {
 
    uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
 
    if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
 
    if (needs_escape(cp)) return {begin, begin + 1, cp};
 
  }
 
  return {begin, nullptr, 0};
 
}
 

	
 
inline auto find_escape(const char* begin, const char* end)
 
    -> find_escape_result<char> {
 
  if (!is_utf8()) return find_escape<char>(begin, end);
 
  auto result = find_escape_result<char>{end, nullptr, 0};
 
  for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
 
                     [&](uint32_t cp, string_view sv) {
 
                       if (needs_escape(cp)) {
 
                         result = {sv.begin(), sv.end(), cp};
 
                         return false;
 
                       }
 
                       return true;
 
                     });
 
  return result;
 
}
 

	
 
#define FMT_STRING_IMPL(s, base, explicit)                                    \
 
  [] {                                                                        \
 
    /* Use the hidden visibility as a workaround for a GCC bug (#1973). */    \
 
    /* Use a macro-like name to avoid shadowing warnings. */                  \
 
    struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base {              \
 
      using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
 
      FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit                                 \
 
      operator fmt::basic_string_view<char_type>() const {                    \
 
        return fmt::detail_exported::compile_string_to_view<char_type>(s);    \
 
      }                                                                       \
 
    };                                                                        \
 
    return FMT_COMPILE_STRING();                                              \
 
  }()
 

	
 
/**
 
  \rst
 
  Constructs a compile-time format string from a string literal *s*.
 

	
 
  **Example**::
 

	
 
    // A compile-time error because 'd' is an invalid specifier for strings.
 
    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
 
  \endrst
 
 */
 
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
 

	
 
template <size_t width, typename Char, typename OutputIt>
 
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
 
  *out++ = static_cast<Char>('\\');
 
  *out++ = static_cast<Char>(prefix);
 
  Char buf[width];
 
  fill_n(buf, width, static_cast<Char>('0'));
 
  format_uint<4>(buf, cp, width);
 
  return copy_str<Char>(buf, buf + width, out);
 
}
 

	
 
template <typename OutputIt, typename Char>
 
auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
 
    -> OutputIt {
 
  auto c = static_cast<Char>(escape.cp);
 
  switch (escape.cp) {
 
  case '\n':
 
    *out++ = static_cast<Char>('\\');
 
    c = static_cast<Char>('n');
 
    break;
 
  case '\r':
 
    *out++ = static_cast<Char>('\\');
 
    c = static_cast<Char>('r');
 
    break;
 
  case '\t':
 
    *out++ = static_cast<Char>('\\');
 
    c = static_cast<Char>('t');
 
    break;
 
  case '"':
 
    FMT_FALLTHROUGH;
 
  case '\'':
 
    FMT_FALLTHROUGH;
 
  case '\\':
 
    *out++ = static_cast<Char>('\\');
 
    break;
 
  default:
 
    if (escape.cp < 0x100) {
 
      return write_codepoint<2, Char>(out, 'x', escape.cp);
 
    }
 
    if (escape.cp < 0x10000) {
 
      return write_codepoint<4, Char>(out, 'u', escape.cp);
 
    }
 
    if (escape.cp < 0x110000) {
 
      return write_codepoint<8, Char>(out, 'U', escape.cp);
 
    }
 
    for (Char escape_char : basic_string_view<Char>(
 
             escape.begin, to_unsigned(escape.end - escape.begin))) {
 
      out = write_codepoint<2, Char>(out, 'x',
 
                                     static_cast<uint32_t>(escape_char) & 0xFF);
 
    }
 
    return out;
 
  }
 
  *out++ = c;
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt>
 
auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
 
    -> OutputIt {
 
  *out++ = static_cast<Char>('"');
 
  auto begin = str.begin(), end = str.end();
 
  do {
 
    auto escape = find_escape(begin, end);
 
    out = copy_str<Char>(begin, escape.begin, out);
 
    begin = escape.end;
 
    if (!begin) break;
 
    out = write_escaped_cp<OutputIt, Char>(out, escape);
 
  } while (begin != end);
 
  *out++ = static_cast<Char>('"');
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt>
 
auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
 
  *out++ = static_cast<Char>('\'');
 
  if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
 
      v == static_cast<Char>('\'')) {
 
    out = write_escaped_cp(
 
        out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
 
  } else {
 
    *out++ = v;
 
  }
 
  *out++ = static_cast<Char>('\'');
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
 
                              const format_specs<Char>& specs) -> OutputIt {
 
  bool is_debug = specs.type == presentation_type::debug;
 
  return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
 
    if (is_debug) return write_escaped_char(it, value);
 
    *it++ = value;
 
    return it;
 
  });
 
}
 
template <typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write(OutputIt out, Char value,
 
                         const format_specs<Char>& specs, locale_ref loc = {})
 
    -> OutputIt {
 
  // char is formatted as unsigned char for consistency across platforms.
 
  using unsigned_type =
 
      conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
 
  return check_char_specs(specs)
 
             ? write_char(out, value, specs)
 
             : write(out, static_cast<unsigned_type>(value), specs, loc);
 
}
 

	
 
// Data for write_int that doesn't depend on output iterator type. It is used to
 
// avoid template code bloat.
 
template <typename Char> struct write_int_data {
 
  size_t size;
 
  size_t padding;
 

	
 
  FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
 
                               const format_specs<Char>& specs)
 
      : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
 
    if (specs.align == align::numeric) {
 
      auto width = to_unsigned(specs.width);
 
      if (width > size) {
 
        padding = width - size;
 
        size = width;
 
      }
 
    } else if (specs.precision > num_digits) {
 
      size = (prefix >> 24) + to_unsigned(specs.precision);
 
      padding = to_unsigned(specs.precision - num_digits);
 
    }
 
  }
 
};
 

	
 
// Writes an integer in the format
 
//   <left-padding><prefix><numeric-padding><digits><right-padding>
 
// where <digits> are written by write_digits(it).
 
// prefix contains chars in three lower bytes and the size in the fourth byte.
 
template <typename OutputIt, typename Char, typename W>
 
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
 
                                        unsigned prefix,
 
                                        const format_specs<Char>& specs,
 
                                        W write_digits) -> OutputIt {
 
  // Slightly faster check for specs.width == 0 && specs.precision == -1.
 
  if ((specs.width | (specs.precision + 1)) == 0) {
 
    auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
 
    if (prefix != 0) {
 
      for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
 
        *it++ = static_cast<Char>(p & 0xff);
 
    }
 
    return base_iterator(out, write_digits(it));
 
  }
 
  auto data = write_int_data<Char>(num_digits, prefix, specs);
 
  return write_padded<align::right>(
 
      out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
 
        for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
 
          *it++ = static_cast<Char>(p & 0xff);
 
        it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
 
        return write_digits(it);
 
      });
 
}
 

	
 
template <typename Char> class digit_grouping {
 
 private:
 
  std::string grouping_;
 
  std::basic_string<Char> thousands_sep_;
 

	
 
  struct next_state {
 
    std::string::const_iterator group;
 
    int pos;
 
  };
 
  next_state initial_state() const { return {grouping_.begin(), 0}; }
 

	
 
  // Returns the next digit group separator position.
 
  int next(next_state& state) const {
 
    if (thousands_sep_.empty()) return max_value<int>();
 
    if (state.group == grouping_.end()) return state.pos += grouping_.back();
 
    if (*state.group <= 0 || *state.group == max_value<char>())
 
      return max_value<int>();
 
    state.pos += *state.group++;
 
    return state.pos;
 
  }
 

	
 
 public:
 
  explicit digit_grouping(locale_ref loc, bool localized = true) {
 
    if (!localized) return;
 
    auto sep = thousands_sep<Char>(loc);
 
    grouping_ = sep.grouping;
 
    if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
 
  }
 
  digit_grouping(std::string grouping, std::basic_string<Char> sep)
 
      : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
 

	
 
  bool has_separator() const { return !thousands_sep_.empty(); }
 

	
 
  int count_separators(int num_digits) const {
 
    int count = 0;
 
    auto state = initial_state();
 
    while (num_digits > next(state)) ++count;
 
    return count;
 
  }
 

	
 
  // Applies grouping to digits and write the output to out.
 
  template <typename Out, typename C>
 
  Out apply(Out out, basic_string_view<C> digits) const {
 
    auto num_digits = static_cast<int>(digits.size());
 
    auto separators = basic_memory_buffer<int>();
 
    separators.push_back(0);
 
    auto state = initial_state();
 
    while (int i = next(state)) {
 
      if (i >= num_digits) break;
 
      separators.push_back(i);
 
    }
 
    for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
 
         i < num_digits; ++i) {
 
      if (num_digits - i == separators[sep_index]) {
 
        out =
 
            copy_str<Char>(thousands_sep_.data(),
 
                           thousands_sep_.data() + thousands_sep_.size(), out);
 
        --sep_index;
 
      }
 
      *out++ = static_cast<Char>(digits[to_unsigned(i)]);
 
    }
 
    return out;
 
  }
 
};
 

	
 
// Writes a decimal integer with digit grouping.
 
template <typename OutputIt, typename UInt, typename Char>
 
auto write_int(OutputIt out, UInt value, unsigned prefix,
 
               const format_specs<Char>& specs,
 
               const digit_grouping<Char>& grouping) -> OutputIt {
 
  static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
 
  int num_digits = count_digits(value);
 
  char digits[40];
 
  format_decimal(digits, value, num_digits);
 
  unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
 
                              grouping.count_separators(num_digits));
 
  return write_padded<align::right>(
 
      out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
 
        if (prefix != 0) {
 
          char sign = static_cast<char>(prefix);
 
          *it++ = static_cast<Char>(sign);
 
        }
 
        return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
 
      });
 
}
 

	
 
// Writes a localized value.
 
FMT_API auto write_loc(appender out, loc_value value,
 
                       const format_specs<>& specs, locale_ref loc) -> bool;
 
template <typename OutputIt, typename Char>
 
inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
 
                      locale_ref) -> bool {
 
  return false;
 
}
 

	
 
FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
 
  prefix |= prefix != 0 ? value << 8 : value;
 
  prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
 
}
 

	
 
template <typename UInt> struct write_int_arg {
 
  UInt abs_value;
 
  unsigned prefix;
 
};
 

	
 
template <typename T>
 
FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
 
    -> write_int_arg<uint32_or_64_or_128_t<T>> {
 
  auto prefix = 0u;
 
  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
 
  if (is_negative(value)) {
 
    prefix = 0x01000000 | '-';
 
    abs_value = 0 - abs_value;
 
  } else {
 
    constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
 
                                            0x1000000u | ' '};
 
    prefix = prefixes[sign];
 
  }
 
  return {abs_value, prefix};
 
}
 

	
 
template <typename Char = char> struct loc_writer {
 
  buffer_appender<Char> out;
 
  const format_specs<Char>& specs;
 
  std::basic_string<Char> sep;
 
  std::string grouping;
 
  std::basic_string<Char> decimal_point;
 

	
 
  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
 
  auto operator()(T value) -> bool {
 
    auto arg = make_write_int_arg(value, specs.sign);
 
    write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
 
              specs, digit_grouping<Char>(grouping, sep));
 
    return true;
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
 
  auto operator()(T) -> bool {
 
    return false;
 
  }
 
};
 

	
 
template <typename Char, typename OutputIt, typename T>
 
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
 
                                        const format_specs<Char>& specs,
 
                                        locale_ref) -> OutputIt {
 
  static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
 
  auto abs_value = arg.abs_value;
 
  auto prefix = arg.prefix;
 
  switch (specs.type) {
 
  case presentation_type::none:
 
  case presentation_type::dec: {
 
    auto num_digits = count_digits(abs_value);
 
    return write_int(
 
        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
 
          return format_decimal<Char>(it, abs_value, num_digits).end;
 
        });
 
  }
 
  case presentation_type::hex_lower:
 
  case presentation_type::hex_upper: {
 
    bool upper = specs.type == presentation_type::hex_upper;
 
    if (specs.alt)
 
      prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
 
    int num_digits = count_digits<4>(abs_value);
 
    return write_int(
 
        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
 
          return format_uint<4, Char>(it, abs_value, num_digits, upper);
 
        });
 
  }
 
  case presentation_type::bin_lower:
 
  case presentation_type::bin_upper: {
 
    bool upper = specs.type == presentation_type::bin_upper;
 
    if (specs.alt)
 
      prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
 
    int num_digits = count_digits<1>(abs_value);
 
    return write_int(out, num_digits, prefix, specs,
 
                     [=](reserve_iterator<OutputIt> it) {
 
                       return format_uint<1, Char>(it, abs_value, num_digits);
 
                     });
 
  }
 
  case presentation_type::oct: {
 
    int num_digits = count_digits<3>(abs_value);
 
    // Octal prefix '0' is counted as a digit, so only add it if precision
 
    // is not greater than the number of digits.
 
    if (specs.alt && specs.precision <= num_digits && abs_value != 0)
 
      prefix_append(prefix, '0');
 
    return write_int(out, num_digits, prefix, specs,
 
                     [=](reserve_iterator<OutputIt> it) {
 
                       return format_uint<3, Char>(it, abs_value, num_digits);
 
                     });
 
  }
 
  case presentation_type::chr:
 
    return write_char(out, static_cast<Char>(abs_value), specs);
 
  default:
 
    throw_format_error("invalid format specifier");
 
  }
 
  return out;
 
}
 
template <typename Char, typename OutputIt, typename T>
 
FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
 
    OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs,
 
    locale_ref loc) -> OutputIt {
 
  return write_int(out, arg, specs, loc);
 
}
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_integral<T>::value &&
 
                        !std::is_same<T, bool>::value &&
 
                        std::is_same<OutputIt, buffer_appender<Char>>::value)>
 
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
 
                                    const format_specs<Char>& specs,
 
                                    locale_ref loc) -> OutputIt {
 
  if (specs.localized && write_loc(out, value, specs, loc)) return out;
 
  return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
 
                            loc);
 
}
 
// An inlined version of write used in format string compilation.
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_integral<T>::value &&
 
                        !std::is_same<T, bool>::value &&
 
                        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
 
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
 
                                    const format_specs<Char>& specs,
 
                                    locale_ref loc) -> OutputIt {
 
  if (specs.localized && write_loc(out, value, specs, loc)) return out;
 
  return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
 
}
 

	
 
// An output iterator that counts the number of objects written to it and
 
// discards them.
 
class counting_iterator {
 
 private:
 
  size_t count_;
 

	
 
 public:
 
  using iterator_category = std::output_iterator_tag;
 
  using difference_type = std::ptrdiff_t;
 
  using pointer = void;
 
  using reference = void;
 
  FMT_UNCHECKED_ITERATOR(counting_iterator);
 

	
 
  struct value_type {
 
    template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
 
  };
 

	
 
  FMT_CONSTEXPR counting_iterator() : count_(0) {}
 

	
 
  FMT_CONSTEXPR size_t count() const { return count_; }
 

	
 
  FMT_CONSTEXPR counting_iterator& operator++() {
 
    ++count_;
 
    return *this;
 
  }
 
  FMT_CONSTEXPR counting_iterator operator++(int) {
 
    auto it = *this;
 
    ++*this;
 
    return it;
 
  }
 

	
 
  FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
 
                                                   difference_type n) {
 
    it.count_ += static_cast<size_t>(n);
 
    return it;
 
  }
 

	
 
  FMT_CONSTEXPR value_type operator*() const { return {}; }
 
};
 

	
 
template <typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
 
                         const format_specs<Char>& specs) -> OutputIt {
 
  auto data = s.data();
 
  auto size = s.size();
 
  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
 
    size = code_point_index(s, to_unsigned(specs.precision));
 
  bool is_debug = specs.type == presentation_type::debug;
 
  size_t width = 0;
 
  if (specs.width != 0) {
 
    if (is_debug)
 
      width = write_escaped_string(counting_iterator{}, s).count();
 
    else
 
      width = compute_width(basic_string_view<Char>(data, size));
 
  }
 
  return write_padded(out, specs, size, width,
 
                      [=](reserve_iterator<OutputIt> it) {
 
                        if (is_debug) return write_escaped_string(it, s);
 
                        return copy_str<Char>(data, data + size, it);
 
                      });
 
}
 
template <typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write(OutputIt out,
 
                         basic_string_view<type_identity_t<Char>> s,
 
                         const format_specs<Char>& specs, locale_ref)
 
    -> OutputIt {
 
  return write(out, s, specs);
 
}
 
template <typename Char, typename OutputIt>
 
FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
 
                         const format_specs<Char>& specs, locale_ref)
 
    -> OutputIt {
 
  return specs.type != presentation_type::pointer
 
             ? write(out, basic_string_view<Char>(s), specs, {})
 
             : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_integral<T>::value &&
 
                        !std::is_same<T, bool>::value &&
 
                        !std::is_same<T, Char>::value)>
 
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
 
  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
 
  bool negative = is_negative(value);
 
  // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
 
  if (negative) abs_value = ~abs_value + 1;
 
  int num_digits = count_digits(abs_value);
 
  auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
 
  auto it = reserve(out, size);
 
  if (auto ptr = to_pointer<Char>(it, size)) {
 
    if (negative) *ptr++ = static_cast<Char>('-');
 
    format_decimal<Char>(ptr, abs_value, num_digits);
 
    return out;
 
  }
 
  if (negative) *it++ = static_cast<Char>('-');
 
  it = format_decimal<Char>(it, abs_value, num_digits).end;
 
  return base_iterator(out, it);
 
}
 

	
 
// A floating-point presentation format.
 
@@ -1291,442 +2490,68 @@ struct float_specs {
 
  bool upper : 1;
 
  bool locale : 1;
 
  bool binary32 : 1;
 
  bool use_grisu : 1;
 
  bool showpoint : 1;
 
};
 

	
 
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
 
template <typename Char, typename It> It write_exponent(int exp, It it) {
 
  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
 
  if (exp < 0) {
 
    *it++ = static_cast<Char>('-');
 
    exp = -exp;
 
  } else {
 
    *it++ = static_cast<Char>('+');
 
  }
 
  if (exp >= 100) {
 
    const char* top = data::digits[exp / 100];
 
    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
 
    *it++ = static_cast<Char>(top[1]);
 
    exp %= 100;
 
  }
 
  const char* d = data::digits[exp];
 
  *it++ = static_cast<Char>(d[0]);
 
  *it++ = static_cast<Char>(d[1]);
 
  return it;
 
}
 

	
 
template <typename T>
 
int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
 

	
 
// Formats a floating-point number with snprintf.
 
template <typename T>
 
int snprintf_float(T value, int precision, float_specs specs,
 
                   buffer<char>& buf);
 

	
 
template <typename T> T promote_float(T value) { return value; }
 
inline double promote_float(float value) { return static_cast<double>(value); }
 

	
 
template <typename Handler>
 
FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
 
  switch (spec) {
 
  case 0:
 
  case 'd':
 
    handler.on_dec();
 
    break;
 
  case 'x':
 
  case 'X':
 
    handler.on_hex();
 
    break;
 
  case 'b':
 
  case 'B':
 
    handler.on_bin();
 
    break;
 
  case 'o':
 
    handler.on_oct();
 
    break;
 
#ifdef FMT_DEPRECATED_N_SPECIFIER
 
  case 'n':
 
#endif
 
  case 'L':
 
    handler.on_num();
 
    break;
 
  case 'c':
 
    handler.on_chr();
 
    break;
 
  default:
 
    handler.on_error();
 
  }
 
}
 

	
 
template <typename ErrorHandler = error_handler, typename Char>
 
FMT_CONSTEXPR float_specs parse_float_type_spec(
 
    const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
 
FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
 
                                         ErrorHandler&& eh = {})
 
    -> float_specs {
 
  auto result = float_specs();
 
  result.showpoint = specs.alt;
 
  result.locale = specs.localized;
 
  switch (specs.type) {
 
  case 0:
 
  case presentation_type::none:
 
    result.format = float_format::general;
 
    result.showpoint |= specs.precision > 0;
 
    break;
 
  case 'G':
 
  case presentation_type::general_upper:
 
    result.upper = true;
 
    FMT_FALLTHROUGH;
 
  case 'g':
 
  case presentation_type::general_lower:
 
    result.format = float_format::general;
 
    break;
 
  case 'E':
 
  case presentation_type::exp_upper:
 
    result.upper = true;
 
    FMT_FALLTHROUGH;
 
  case 'e':
 
  case presentation_type::exp_lower:
 
    result.format = float_format::exp;
 
    result.showpoint |= specs.precision != 0;
 
    break;
 
  case 'F':
 
  case presentation_type::fixed_upper:
 
    result.upper = true;
 
    FMT_FALLTHROUGH;
 
  case 'f':
 
  case presentation_type::fixed_lower:
 
    result.format = float_format::fixed;
 
    result.showpoint |= specs.precision != 0;
 
    break;
 
  case 'A':
 
  case presentation_type::hexfloat_upper:
 
    result.upper = true;
 
    FMT_FALLTHROUGH;
 
  case 'a':
 
  case presentation_type::hexfloat_lower:
 
    result.format = float_format::hex;
 
    break;
 
#ifdef FMT_DEPRECATED_N_SPECIFIER
 
  case 'n':
 
#endif
 
  case 'L':
 
    result.locale = true;
 
    break;
 
  default:
 
    eh.on_error("invalid type specifier");
 
    eh.on_error("invalid format specifier");
 
    break;
 
  }
 
  return result;
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs,
 
                                     Handler&& handler) {
 
  if (!specs) return handler.on_char();
 
  if (specs->type && specs->type != 'c') return handler.on_int();
 
  if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
 
    handler.on_error("invalid format specifier for char");
 
  handler.on_char();
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) {
 
  if (spec == 0 || spec == 's')
 
    handler.on_string();
 
  else if (spec == 'p')
 
    handler.on_pointer();
 
  else
 
    handler.on_error("invalid type specifier");
 
}
 

	
 
template <typename Char, typename ErrorHandler>
 
FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
 
  if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
 
}
 

	
 
template <typename Char, typename ErrorHandler>
 
FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
 
  if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
 
}
 

	
 
template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
 
 public:
 
  FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {}
 

	
 
  FMT_CONSTEXPR void on_dec() {}
 
  FMT_CONSTEXPR void on_hex() {}
 
  FMT_CONSTEXPR void on_bin() {}
 
  FMT_CONSTEXPR void on_oct() {}
 
  FMT_CONSTEXPR void on_num() {}
 
  FMT_CONSTEXPR void on_chr() {}
 

	
 
  FMT_CONSTEXPR void on_error() {
 
    ErrorHandler::on_error("invalid type specifier");
 
  }
 
};
 

	
 
template <typename ErrorHandler>
 
class char_specs_checker : public ErrorHandler {
 
 private:
 
  char type_;
 

	
 
 public:
 
  FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
 
      : ErrorHandler(eh), type_(type) {}
 

	
 
  FMT_CONSTEXPR void on_int() {
 
    handle_int_type_spec(type_, int_type_checker<ErrorHandler>(*this));
 
  }
 
  FMT_CONSTEXPR void on_char() {}
 
};
 

	
 
template <typename ErrorHandler>
 
class cstring_type_checker : public ErrorHandler {
 
 public:
 
  FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh)
 
      : ErrorHandler(eh) {}
 

	
 
  FMT_CONSTEXPR void on_string() {}
 
  FMT_CONSTEXPR void on_pointer() {}
 
};
 

	
 
template <typename OutputIt, typename Char>
 
FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
 
  auto fill_size = fill.size();
 
  if (fill_size == 1) return std::fill_n(it, n, fill[0]);
 
  for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it);
 
  return it;
 
}
 

	
 
// Writes the output of f, padded according to format specifications in specs.
 
// size: output size in code units.
 
// width: output display width in (terminal) column positions.
 
template <align::type align = align::left, typename OutputIt, typename Char,
 
          typename F>
 
inline OutputIt write_padded(OutputIt out,
 
                             const basic_format_specs<Char>& specs, size_t size,
 
                             size_t width, F&& f) {
 
  static_assert(align == align::left || align == align::right, "");
 
  unsigned spec_width = to_unsigned(specs.width);
 
  size_t padding = spec_width > width ? spec_width - width : 0;
 
  auto* shifts = align == align::left ? data::left_padding_shifts
 
                                      : data::right_padding_shifts;
 
  size_t left_padding = padding >> shifts[specs.align];
 
  auto it = reserve(out, size + padding * specs.fill.size());
 
  it = fill(it, left_padding, specs.fill);
 
  it = f(it);
 
  it = fill(it, padding - left_padding, specs.fill);
 
  return base_iterator(out, it);
 
}
 

	
 
template <align::type align = align::left, typename OutputIt, typename Char,
 
          typename F>
 
inline OutputIt write_padded(OutputIt out,
 
                             const basic_format_specs<Char>& specs, size_t size,
 
                             F&& f) {
 
  return write_padded<align>(out, specs, size, size, f);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write_bytes(OutputIt out, string_view bytes,
 
                     const basic_format_specs<Char>& specs) {
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
 
    const char* data = bytes.data();
 
    return copy_str<Char>(data, data + bytes.size(), it);
 
  });
 
}
 

	
 
// Data for write_int that doesn't depend on output iterator type. It is used to
 
// avoid template code bloat.
 
template <typename Char> struct write_int_data {
 
  size_t size;
 
  size_t padding;
 

	
 
  write_int_data(int num_digits, string_view prefix,
 
                 const basic_format_specs<Char>& specs)
 
      : size(prefix.size() + to_unsigned(num_digits)), padding(0) {
 
    if (specs.align == align::numeric) {
 
      auto width = to_unsigned(specs.width);
 
      if (width > size) {
 
        padding = width - size;
 
        size = width;
 
      }
 
    } else if (specs.precision > num_digits) {
 
      size = prefix.size() + to_unsigned(specs.precision);
 
      padding = to_unsigned(specs.precision - num_digits);
 
    }
 
  }
 
};
 

	
 
// Writes an integer in the format
 
//   <left-padding><prefix><numeric-padding><digits><right-padding>
 
// where <digits> are written by f(it).
 
template <typename OutputIt, typename Char, typename F>
 
OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
 
                   const basic_format_specs<Char>& specs, F f) {
 
  auto data = write_int_data<Char>(num_digits, prefix, specs);
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  return write_padded<align::right>(out, specs, data.size, [=](iterator it) {
 
    if (prefix.size() != 0)
 
      it = copy_str<Char>(prefix.begin(), prefix.end(), it);
 
    it = std::fill_n(it, data.padding, static_cast<Char>('0'));
 
    return f(it);
 
  });
 
}
 

	
 
template <typename StrChar, typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, basic_string_view<StrChar> s,
 
               const basic_format_specs<Char>& specs) {
 
  auto data = s.data();
 
  auto size = s.size();
 
  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
 
    size = code_point_index(s, to_unsigned(specs.precision));
 
  auto width = specs.width != 0
 
                   ? count_code_points(basic_string_view<StrChar>(data, size))
 
                   : 0;
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  return write_padded(out, specs, size, width, [=](iterator it) {
 
    return copy_str<Char>(data, data + size, it);
 
  });
 
}
 

	
 
// The handle_int_type_spec handler that writes an integer.
 
template <typename OutputIt, typename Char, typename UInt> struct int_writer {
 
  OutputIt out;
 
  locale_ref locale;
 
  const basic_format_specs<Char>& specs;
 
  UInt abs_value;
 
  char prefix[4];
 
  unsigned prefix_size;
 

	
 
  using iterator =
 
      remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
 

	
 
  string_view get_prefix() const { return string_view(prefix, prefix_size); }
 

	
 
  template <typename Int>
 
  int_writer(OutputIt output, locale_ref loc, Int value,
 
             const basic_format_specs<Char>& s)
 
      : out(output),
 
        locale(loc),
 
        specs(s),
 
        abs_value(static_cast<UInt>(value)),
 
        prefix_size(0) {
 
    static_assert(std::is_same<uint32_or_64_or_128_t<Int>, UInt>::value, "");
 
    if (is_negative(value)) {
 
      prefix[0] = '-';
 
      ++prefix_size;
 
      abs_value = 0 - abs_value;
 
    } else if (specs.sign != sign::none && specs.sign != sign::minus) {
 
      prefix[0] = specs.sign == sign::plus ? '+' : ' ';
 
      ++prefix_size;
 
    }
 
  }
 

	
 
  void on_dec() {
 
    auto num_digits = count_digits(abs_value);
 
    out = write_int(
 
        out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
 
          return format_decimal<Char>(it, abs_value, num_digits).end;
 
        });
 
  }
 

	
 
  void on_hex() {
 
    if (specs.alt) {
 
      prefix[prefix_size++] = '0';
 
      prefix[prefix_size++] = specs.type;
 
    }
 
    int num_digits = count_digits<4>(abs_value);
 
    out = write_int(out, num_digits, get_prefix(), specs,
 
                    [this, num_digits](iterator it) {
 
                      return format_uint<4, Char>(it, abs_value, num_digits,
 
                                                  specs.type != 'x');
 
                    });
 
  }
 

	
 
  void on_bin() {
 
    if (specs.alt) {
 
      prefix[prefix_size++] = '0';
 
      prefix[prefix_size++] = static_cast<char>(specs.type);
 
    }
 
    int num_digits = count_digits<1>(abs_value);
 
    out = write_int(out, num_digits, get_prefix(), specs,
 
                    [this, num_digits](iterator it) {
 
                      return format_uint<1, Char>(it, abs_value, num_digits);
 
                    });
 
  }
 

	
 
  void on_oct() {
 
    int num_digits = count_digits<3>(abs_value);
 
    if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
 
      // Octal prefix '0' is counted as a digit, so only add it if precision
 
      // is not greater than the number of digits.
 
      prefix[prefix_size++] = '0';
 
    }
 
    out = write_int(out, num_digits, get_prefix(), specs,
 
                    [this, num_digits](iterator it) {
 
                      return format_uint<3, Char>(it, abs_value, num_digits);
 
                    });
 
  }
 

	
 
  enum { sep_size = 1 };
 

	
 
  void on_num() {
 
    std::string groups = grouping<Char>(locale);
 
    if (groups.empty()) return on_dec();
 
    auto sep = thousands_sep<Char>(locale);
 
    if (!sep) return on_dec();
 
    int num_digits = count_digits(abs_value);
 
    int size = num_digits, n = num_digits;
 
    std::string::const_iterator group = groups.cbegin();
 
    while (group != groups.cend() && n > *group && *group > 0 &&
 
           *group != max_value<char>()) {
 
      size += sep_size;
 
      n -= *group;
 
      ++group;
 
    }
 
    if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
 
    char digits[40];
 
    format_decimal(digits, abs_value, num_digits);
 
    basic_memory_buffer<Char> buffer;
 
    size += static_cast<int>(prefix_size);
 
    const auto usize = to_unsigned(size);
 
    buffer.resize(usize);
 
    basic_string_view<Char> s(&sep, sep_size);
 
    // Index of a decimal digit with the least significant digit having index 0.
 
    int digit_index = 0;
 
    group = groups.cbegin();
 
    auto p = buffer.data() + size - 1;
 
    for (int i = num_digits - 1; i > 0; --i) {
 
      *p-- = static_cast<Char>(digits[i]);
 
      if (*group <= 0 || ++digit_index % *group != 0 ||
 
          *group == max_value<char>())
 
        continue;
 
      if (group + 1 != groups.cend()) {
 
        digit_index = 0;
 
        ++group;
 
      }
 
      std::uninitialized_copy(s.data(), s.data() + s.size(),
 
                              make_checked(p, s.size()));
 
      p -= s.size();
 
    }
 
    *p-- = static_cast<Char>(*digits);
 
    if (prefix_size != 0) *p = static_cast<Char>('-');
 
    auto data = buffer.data();
 
    out = write_padded<align::right>(
 
        out, specs, usize, usize,
 
        [=](iterator it) { return copy_str<Char>(data, data + size, it); });
 
  }
 

	
 
  void on_chr() { *out++ = static_cast<Char>(abs_value); }
 

	
 
  FMT_NORETURN void on_error() {
 
    FMT_THROW(format_error("invalid type specifier"));
 
  }
 
};
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write_nonfinite(OutputIt out, bool isinf,
 
                         const basic_format_specs<Char>& specs,
 
                         const float_specs& fspecs) {
 
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
 
                                     format_specs<Char> specs,
 
                                     const float_specs& fspecs) -> OutputIt {
 
  auto str =
 
      isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan");
 
      isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
 
  constexpr size_t str_size = 3;
 
  auto sign = fspecs.sign;
 
  auto size = str_size + (sign ? 1 : 0);
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  return write_padded(out, specs, size, [=](iterator it) {
 
    if (sign) *it++ = static_cast<Char>(data::signs[sign]);
 
  // Replace '0'-padding with space for non-finite values.
 
  const bool is_zero_fill =
 
      specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
 
  if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
 
  return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
 
    if (sign) *it++ = detail::sign<Char>(sign);
 
    return copy_str<Char>(str, str + str_size, it);
 
  });
 
}
 
@@ -1738,76 +2563,120 @@ struct big_decimal_fp {
 
  int exponent;
 
};
 

	
 
inline int get_significand_size(const big_decimal_fp& fp) {
 
  return fp.significand_size;
 
constexpr auto get_significand_size(const big_decimal_fp& f) -> int {
 
  return f.significand_size;
 
}
 
template <typename T>
 
inline int get_significand_size(const dragonbox::decimal_fp<T>& fp) {
 
  return count_digits(fp.significand);
 
inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
 
  return count_digits(f.significand);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
inline OutputIt write_significand(OutputIt out, const char* significand,
 
                                  int& significand_size) {
 
constexpr auto write_significand(OutputIt out, const char* significand,
 
                                 int significand_size) -> OutputIt {
 
  return copy_str<Char>(significand, significand + significand_size, out);
 
}
 
template <typename Char, typename OutputIt, typename UInt>
 
inline OutputIt write_significand(OutputIt out, UInt significand,
 
                                  int significand_size) {
 
inline auto write_significand(OutputIt out, UInt significand,
 
                              int significand_size) -> OutputIt {
 
  return format_decimal<Char>(out, significand, significand_size).end;
 
}
 
template <typename Char, typename OutputIt, typename T, typename Grouping>
 
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
 
                                       int significand_size, int exponent,
 
                                       const Grouping& grouping) -> OutputIt {
 
  if (!grouping.has_separator()) {
 
    out = write_significand<Char>(out, significand, significand_size);
 
    return detail::fill_n(out, exponent, static_cast<Char>('0'));
 
  }
 
  auto buffer = memory_buffer();
 
  write_significand<char>(appender(buffer), significand, significand_size);
 
  detail::fill_n(appender(buffer), exponent, '0');
 
  return grouping.apply(out, string_view(buffer.data(), buffer.size()));
 
}
 

	
 
template <typename Char, typename UInt,
 
          FMT_ENABLE_IF(std::is_integral<UInt>::value)>
 
inline Char* write_significand(Char* out, UInt significand,
 
                               int significand_size, int integral_size,
 
                               Char decimal_point) {
 
inline auto write_significand(Char* out, UInt significand, int significand_size,
 
                              int integral_size, Char decimal_point) -> Char* {
 
  if (!decimal_point)
 
    return format_decimal(out, significand, significand_size).end;
 
  auto end = format_decimal(out + 1, significand, significand_size).end;
 
  if (integral_size == 1)
 
    out[0] = out[1];
 
  else
 
    std::copy_n(out + 1, integral_size, out);
 
  out[integral_size] = decimal_point;
 
  out += significand_size + 1;
 
  Char* end = out;
 
  int floating_size = significand_size - integral_size;
 
  for (int i = floating_size / 2; i > 0; --i) {
 
    out -= 2;
 
    copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
 
    significand /= 100;
 
  }
 
  if (floating_size % 2 != 0) {
 
    *--out = static_cast<Char>('0' + significand % 10);
 
    significand /= 10;
 
  }
 
  *--out = decimal_point;
 
  format_decimal(out - integral_size, significand, integral_size);
 
  return end;
 
}
 

	
 
template <typename OutputIt, typename UInt, typename Char,
 
          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
 
inline OutputIt write_significand(OutputIt out, UInt significand,
 
inline auto write_significand(OutputIt out, UInt significand,
 
                                  int significand_size, int integral_size,
 
                                  Char decimal_point) {
 
                              Char decimal_point) -> OutputIt {
 
  // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
 
  Char buffer[digits10<UInt>() + 2];
 
  auto end = write_significand(buffer, significand, significand_size,
 
                               integral_size, decimal_point);
 
  return detail::copy_str<Char>(buffer, end, out);
 
  return detail::copy_str_noinline<Char>(buffer, end, out);
 
}
 

	
 
template <typename OutputIt, typename Char>
 
inline OutputIt write_significand(OutputIt out, const char* significand,
 
FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
 
                                  int significand_size, int integral_size,
 
                                  Char decimal_point) {
 
  out = detail::copy_str<Char>(significand, significand + integral_size, out);
 
                                     Char decimal_point) -> OutputIt {
 
  out = detail::copy_str_noinline<Char>(significand,
 
                                        significand + integral_size, out);
 
  if (!decimal_point) return out;
 
  *out++ = decimal_point;
 
  return detail::copy_str<Char>(significand + integral_size,
 
  return detail::copy_str_noinline<Char>(significand + integral_size,
 
                                significand + significand_size, out);
 
}
 

	
 
template <typename OutputIt, typename DecimalFP, typename Char>
 
OutputIt write_float(OutputIt out, const DecimalFP& fp,
 
                     const basic_format_specs<Char>& specs, float_specs fspecs,
 
                     Char decimal_point) {
 
  auto significand = fp.significand;
 
  int significand_size = get_significand_size(fp);
 
  static const Char zero = static_cast<Char>('0');
 
template <typename OutputIt, typename Char, typename T, typename Grouping>
 
FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
 
                                       int significand_size, int integral_size,
 
                                       Char decimal_point,
 
                                       const Grouping& grouping) -> OutputIt {
 
  if (!grouping.has_separator()) {
 
    return write_significand(out, significand, significand_size, integral_size,
 
                             decimal_point);
 
  }
 
  auto buffer = basic_memory_buffer<Char>();
 
  write_significand(buffer_appender<Char>(buffer), significand,
 
                    significand_size, integral_size, decimal_point);
 
  grouping.apply(
 
      out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
 
  return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
 
                                         buffer.end(), out);
 
}
 

	
 
template <typename OutputIt, typename DecimalFP, typename Char,
 
          typename Grouping = digit_grouping<Char>>
 
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
 
                                    const format_specs<Char>& specs,
 
                                    float_specs fspecs, locale_ref loc)
 
    -> OutputIt {
 
  auto significand = f.significand;
 
  int significand_size = get_significand_size(f);
 
  const Char zero = static_cast<Char>('0');
 
  auto sign = fspecs.sign;
 
  size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 

	
 
  int output_exp = fp.exponent + significand_size - 1;
 
  using iterator = reserve_iterator<OutputIt>;
 

	
 
  Char decimal_point =
 
      fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
 

	
 
  int output_exp = f.exponent + significand_size - 1;
 
  auto use_exp_format = [=]() {
 
    if (fspecs.format == float_format::exp) return true;
 
    if (fspecs.format != float_format::general) return false;
 
@@ -1820,7 +2689,8 @@ OutputIt write_float(OutputIt out, const
 
  if (use_exp_format()) {
 
    int num_zeros = 0;
 
    if (fspecs.showpoint) {
 
      num_zeros = (std::max)(fspecs.precision - significand_size, 0);
 
      num_zeros = fspecs.precision - significand_size;
 
      if (num_zeros < 0) num_zeros = 0;
 
      size += to_unsigned(num_zeros);
 
    } else if (significand_size == 1) {
 
      decimal_point = Char();
 
@@ -1832,11 +2702,11 @@ OutputIt write_float(OutputIt out, const
 
    size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
 
    char exp_char = fspecs.upper ? 'E' : 'e';
 
    auto write = [=](iterator it) {
 
      if (sign) *it++ = static_cast<Char>(data::signs[sign]);
 
      if (sign) *it++ = detail::sign<Char>(sign);
 
      // Insert a decimal point after the first digit and add an exponent.
 
      it = write_significand(it, significand, significand_size, 1,
 
                             decimal_point);
 
      if (num_zeros > 0) it = std::fill_n(it, num_zeros, zero);
 
      if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
 
      *it++ = static_cast<Char>(exp_char);
 
      return write_exponent<Char>(output_exp, it);
 
    };
 
@@ -1844,36 +2714,38 @@ OutputIt write_float(OutputIt out, const
 
                           : base_iterator(out, write(reserve(out, size)));
 
  }
 

	
 
  int exp = fp.exponent + significand_size;
 
  if (fp.exponent >= 0) {
 
  int exp = f.exponent + significand_size;
 
  if (f.exponent >= 0) {
 
    // 1234e5 -> 123400000[.0+]
 
    size += to_unsigned(fp.exponent);
 
    size += to_unsigned(f.exponent);
 
    int num_zeros = fspecs.precision - exp;
 
#ifdef FMT_FUZZ
 
    if (num_zeros > 5000)
 
      throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
 
#endif
 
    abort_fuzzing_if(num_zeros > 5000);
 
    if (fspecs.showpoint) {
 
      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
 
      ++size;
 
      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
 
      if (num_zeros > 0) size += to_unsigned(num_zeros);
 
    }
 
    auto grouping = Grouping(loc, fspecs.locale);
 
    size += to_unsigned(grouping.count_separators(exp));
 
    return write_padded<align::right>(out, specs, size, [&](iterator it) {
 
      if (sign) *it++ = static_cast<Char>(data::signs[sign]);
 
      it = write_significand<Char>(it, significand, significand_size);
 
      it = std::fill_n(it, fp.exponent, zero);
 
      if (sign) *it++ = detail::sign<Char>(sign);
 
      it = write_significand<Char>(it, significand, significand_size,
 
                                   f.exponent, grouping);
 
      if (!fspecs.showpoint) return it;
 
      *it++ = decimal_point;
 
      return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it;
 
      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
 
    });
 
  } else if (exp > 0) {
 
    // 1234e-2 -> 12.34[0+]
 
    int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
 
    size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
 
    auto grouping = Grouping(loc, fspecs.locale);
 
    size += to_unsigned(grouping.count_separators(exp));
 
    return write_padded<align::right>(out, specs, size, [&](iterator it) {
 
      if (sign) *it++ = static_cast<Char>(data::signs[sign]);
 
      if (sign) *it++ = detail::sign<Char>(sign);
 
      it = write_significand(it, significand, significand_size, exp,
 
                             decimal_point);
 
      return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it;
 
                             decimal_point, grouping);
 
      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
 
    });
 
  }
 
  // 1234e-6 -> 0.001234
 
@@ -1882,37 +2754,1086 @@ OutputIt write_float(OutputIt out, const
 
      fspecs.precision < num_zeros) {
 
    num_zeros = fspecs.precision;
 
  }
 
  size += 2 + to_unsigned(num_zeros);
 
  bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
 
  size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
 
  return write_padded<align::right>(out, specs, size, [&](iterator it) {
 
    if (sign) *it++ = static_cast<Char>(data::signs[sign]);
 
    if (sign) *it++ = detail::sign<Char>(sign);
 
    *it++ = zero;
 
    if (num_zeros == 0 && significand_size == 0 && !fspecs.showpoint) return it;
 
    if (!pointy) return it;
 
    *it++ = decimal_point;
 
    it = std::fill_n(it, num_zeros, zero);
 
    it = detail::fill_n(it, num_zeros, zero);
 
    return write_significand<Char>(it, significand, significand_size);
 
  });
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(std::is_floating_point<T>::value)>
 
OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs,
 
               locale_ref loc = {}) {
 
  if (const_check(!is_supported_floating_point(value))) return out;
 
template <typename Char> class fallback_digit_grouping {
 
 public:
 
  constexpr fallback_digit_grouping(locale_ref, bool) {}
 

	
 
  constexpr bool has_separator() const { return false; }
 

	
 
  constexpr int count_separators(int) const { return 0; }
 

	
 
  template <typename Out, typename C>
 
  constexpr Out apply(Out out, basic_string_view<C>) const {
 
    return out;
 
  }
 
};
 

	
 
template <typename OutputIt, typename DecimalFP, typename Char>
 
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
 
                                 const format_specs<Char>& specs,
 
                                 float_specs fspecs, locale_ref loc)
 
    -> OutputIt {
 
  if (is_constant_evaluated()) {
 
    return do_write_float<OutputIt, DecimalFP, Char,
 
                          fallback_digit_grouping<Char>>(out, f, specs, fspecs,
 
                                                         loc);
 
  } else {
 
    return do_write_float(out, f, specs, fspecs, loc);
 
  }
 
}
 

	
 
template <typename T> constexpr bool isnan(T value) {
 
  return !(value >= value);  // std::isnan doesn't support __float128.
 
}
 

	
 
template <typename T, typename Enable = void>
 
struct has_isfinite : std::false_type {};
 

	
 
template <typename T>
 
struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
 
    : std::true_type {};
 

	
 
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
 
                                        has_isfinite<T>::value)>
 
FMT_CONSTEXPR20 bool isfinite(T value) {
 
  constexpr T inf = T(std::numeric_limits<double>::infinity());
 
  if (is_constant_evaluated())
 
    return !detail::isnan(value) && value < inf && value > -inf;
 
  return std::isfinite(value);
 
}
 
template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
 
FMT_CONSTEXPR bool isfinite(T value) {
 
  T inf = T(std::numeric_limits<double>::infinity());
 
  // std::isfinite doesn't support __float128.
 
  return !detail::isnan(value) && value < inf && value > -inf;
 
}
 

	
 
template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
 
FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
 
  if (is_constant_evaluated()) {
 
#ifdef __cpp_if_constexpr
 
    if constexpr (std::numeric_limits<double>::is_iec559) {
 
      auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
 
      return (bits >> (num_bits<uint64_t>() - 1)) != 0;
 
    }
 
#endif
 
  }
 
  return std::signbit(static_cast<double>(value));
 
}
 

	
 
enum class round_direction { unknown, up, down };
 

	
 
// Given the divisor (normally a power of 10), the remainder = v % divisor for
 
// some number v and the error, returns whether v should be rounded up, down, or
 
// whether the rounding direction can't be determined due to error.
 
// error should be less than divisor / 2.
 
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
 
                                                         uint64_t remainder,
 
                                                         uint64_t error) {
 
  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
 
  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
 
  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
 
  // Round down if (remainder + error) * 2 <= divisor.
 
  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
 
    return round_direction::down;
 
  // Round up if (remainder - error) * 2 >= divisor.
 
  if (remainder >= error &&
 
      remainder - error >= divisor - (remainder - error)) {
 
    return round_direction::up;
 
  }
 
  return round_direction::unknown;
 
}
 

	
 
namespace digits {
 
enum result {
 
  more,  // Generate more digits.
 
  done,  // Done generating digits.
 
  error  // Digit generation cancelled due to an error.
 
};
 
}
 

	
 
struct gen_digits_handler {
 
  char* buf;
 
  int size;
 
  int precision;
 
  int exp10;
 
  bool fixed;
 

	
 
  FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
 
                                        uint64_t remainder, uint64_t error,
 
                                        bool integral) {
 
    FMT_ASSERT(remainder < divisor, "");
 
    buf[size++] = digit;
 
    if (!integral && error >= remainder) return digits::error;
 
    if (size < precision) return digits::more;
 
    if (!integral) {
 
      // Check if error * 2 < divisor with overflow prevention.
 
      // The check is not needed for the integral part because error = 1
 
      // and divisor > (1 << 32) there.
 
      if (error >= divisor || error >= divisor - error) return digits::error;
 
    } else {
 
      FMT_ASSERT(error == 1 && divisor > 2, "");
 
    }
 
    auto dir = get_round_direction(divisor, remainder, error);
 
    if (dir != round_direction::up)
 
      return dir == round_direction::down ? digits::done : digits::error;
 
    ++buf[size - 1];
 
    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
 
      buf[i] = '0';
 
      ++buf[i - 1];
 
    }
 
    if (buf[0] > '9') {
 
      buf[0] = '1';
 
      if (fixed)
 
        buf[size++] = '0';
 
      else
 
        ++exp10;
 
    }
 
    return digits::done;
 
  }
 
};
 

	
 
inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
 
  // Adjust fixed precision by exponent because it is relative to decimal
 
  // point.
 
  if (exp10 > 0 && precision > max_value<int>() - exp10)
 
    FMT_THROW(format_error("number is too big"));
 
  precision += exp10;
 
}
 

	
 
// Generates output using the Grisu digit-gen algorithm.
 
// error: the size of the region (lower, upper) outside of which numbers
 
// definitely do not round to value (Delta in Grisu3).
 
FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
 
                                                 int& exp,
 
                                                 gen_digits_handler& handler)
 
    -> digits::result {
 
  const fp one(1ULL << -value.e, value.e);
 
  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
 
  // zero because it contains a product of two 64-bit numbers with MSB set (due
 
  // to normalization) - 1, shifted right by at most 60 bits.
 
  auto integral = static_cast<uint32_t>(value.f >> -one.e);
 
  FMT_ASSERT(integral != 0, "");
 
  FMT_ASSERT(integral == value.f >> -one.e, "");
 
  // The fractional part of scaled value (p2 in Grisu) c = value % one.
 
  uint64_t fractional = value.f & (one.f - 1);
 
  exp = count_digits(integral);  // kappa in Grisu.
 
  // Non-fixed formats require at least one digit and no precision adjustment.
 
  if (handler.fixed) {
 
    adjust_precision(handler.precision, exp + handler.exp10);
 
    // Check if precision is satisfied just by leading zeros, e.g.
 
    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
 
    if (handler.precision <= 0) {
 
      if (handler.precision < 0) return digits::done;
 
      // Divide by 10 to prevent overflow.
 
      uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
 
      auto dir = get_round_direction(divisor, value.f / 10, error * 10);
 
      if (dir == round_direction::unknown) return digits::error;
 
      handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
 
      return digits::done;
 
    }
 
  }
 
  // Generate digits for the integral part. This can produce up to 10 digits.
 
  do {
 
    uint32_t digit = 0;
 
    auto divmod_integral = [&](uint32_t divisor) {
 
      digit = integral / divisor;
 
      integral %= divisor;
 
    };
 
    // This optimization by Milo Yip reduces the number of integer divisions by
 
    // one per iteration.
 
    switch (exp) {
 
    case 10:
 
      divmod_integral(1000000000);
 
      break;
 
    case 9:
 
      divmod_integral(100000000);
 
      break;
 
    case 8:
 
      divmod_integral(10000000);
 
      break;
 
    case 7:
 
      divmod_integral(1000000);
 
      break;
 
    case 6:
 
      divmod_integral(100000);
 
      break;
 
    case 5:
 
      divmod_integral(10000);
 
      break;
 
    case 4:
 
      divmod_integral(1000);
 
      break;
 
    case 3:
 
      divmod_integral(100);
 
      break;
 
    case 2:
 
      divmod_integral(10);
 
      break;
 
    case 1:
 
      digit = integral;
 
      integral = 0;
 
      break;
 
    default:
 
      FMT_ASSERT(false, "invalid number of digits");
 
    }
 
    --exp;
 
    auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
 
    auto result = handler.on_digit(static_cast<char>('0' + digit),
 
                                   data::power_of_10_64[exp] << -one.e,
 
                                   remainder, error, true);
 
    if (result != digits::more) return result;
 
  } while (exp > 0);
 
  // Generate digits for the fractional part.
 
  for (;;) {
 
    fractional *= 10;
 
    error *= 10;
 
    char digit = static_cast<char>('0' + (fractional >> -one.e));
 
    fractional &= one.f - 1;
 
    --exp;
 
    auto result = handler.on_digit(digit, one.f, fractional, error, false);
 
    if (result != digits::more) return result;
 
  }
 
}
 

	
 
class bigint {
 
 private:
 
  // A bigint is stored as an array of bigits (big digits), with bigit at index
 
  // 0 being the least significant one.
 
  using bigit = uint32_t;
 
  using double_bigit = uint64_t;
 
  enum { bigits_capacity = 32 };
 
  basic_memory_buffer<bigit, bigits_capacity> bigits_;
 
  int exp_;
 

	
 
  FMT_CONSTEXPR20 bigit operator[](int index) const {
 
    return bigits_[to_unsigned(index)];
 
  }
 
  FMT_CONSTEXPR20 bigit& operator[](int index) {
 
    return bigits_[to_unsigned(index)];
 
  }
 

	
 
  static constexpr const int bigit_bits = num_bits<bigit>();
 

	
 
  friend struct formatter<bigint>;
 

	
 
  FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
 
    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
 
    (*this)[index] = static_cast<bigit>(result);
 
    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
 
  }
 

	
 
  FMT_CONSTEXPR20 void remove_leading_zeros() {
 
    int num_bigits = static_cast<int>(bigits_.size()) - 1;
 
    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
 
    bigits_.resize(to_unsigned(num_bigits + 1));
 
  }
 

	
 
  // Computes *this -= other assuming aligned bigints and *this >= other.
 
  FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
 
    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
 
    FMT_ASSERT(compare(*this, other) >= 0, "");
 
    bigit borrow = 0;
 
    int i = other.exp_ - exp_;
 
    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
 
      subtract_bigits(i, other.bigits_[j], borrow);
 
    while (borrow > 0) subtract_bigits(i, 0, borrow);
 
    remove_leading_zeros();
 
  }
 

	
 
  FMT_CONSTEXPR20 void multiply(uint32_t value) {
 
    const double_bigit wide_value = value;
 
    bigit carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      double_bigit result = bigits_[i] * wide_value + carry;
 
      bigits_[i] = static_cast<bigit>(result);
 
      carry = static_cast<bigit>(result >> bigit_bits);
 
    }
 
    if (carry != 0) bigits_.push_back(carry);
 
  }
 

	
 
  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
 
                                         std::is_same<UInt, uint128_t>::value)>
 
  FMT_CONSTEXPR20 void multiply(UInt value) {
 
    using half_uint =
 
        conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
 
    const int shift = num_bits<half_uint>() - bigit_bits;
 
    const UInt lower = static_cast<half_uint>(value);
 
    const UInt upper = value >> num_bits<half_uint>();
 
    UInt carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
 
      carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) +
 
              (carry >> bigit_bits);
 
      bigits_[i] = static_cast<bigit>(result);
 
    }
 
    while (carry != 0) {
 
      bigits_.push_back(static_cast<bigit>(carry));
 
      carry >>= bigit_bits;
 
    }
 
  }
 

	
 
  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
 
                                         std::is_same<UInt, uint128_t>::value)>
 
  FMT_CONSTEXPR20 void assign(UInt n) {
 
    size_t num_bigits = 0;
 
    do {
 
      bigits_[num_bigits++] = static_cast<bigit>(n);
 
      n >>= bigit_bits;
 
    } while (n != 0);
 
    bigits_.resize(num_bigits);
 
    exp_ = 0;
 
  }
 

	
 
 public:
 
  FMT_CONSTEXPR20 bigint() : exp_(0) {}
 
  explicit bigint(uint64_t n) { assign(n); }
 

	
 
  bigint(const bigint&) = delete;
 
  void operator=(const bigint&) = delete;
 

	
 
  FMT_CONSTEXPR20 void assign(const bigint& other) {
 
    auto size = other.bigits_.size();
 
    bigits_.resize(size);
 
    auto data = other.bigits_.data();
 
    std::copy(data, data + size, make_checked(bigits_.data(), size));
 
    exp_ = other.exp_;
 
  }
 

	
 
  template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
 
    FMT_ASSERT(n > 0, "");
 
    assign(uint64_or_128_t<Int>(n));
 
  }
 

	
 
  FMT_CONSTEXPR20 int num_bigits() const {
 
    return static_cast<int>(bigits_.size()) + exp_;
 
  }
 

	
 
  FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
 
    FMT_ASSERT(shift >= 0, "");
 
    exp_ += shift / bigit_bits;
 
    shift %= bigit_bits;
 
    if (shift == 0) return *this;
 
    bigit carry = 0;
 
    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
 
      bigit c = bigits_[i] >> (bigit_bits - shift);
 
      bigits_[i] = (bigits_[i] << shift) + carry;
 
      carry = c;
 
    }
 
    if (carry != 0) bigits_.push_back(carry);
 
    return *this;
 
  }
 

	
 
  template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
 
    FMT_ASSERT(value > 0, "");
 
    multiply(uint32_or_64_or_128_t<Int>(value));
 
    return *this;
 
  }
 

	
 
  friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
 
    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
 
    if (num_lhs_bigits != num_rhs_bigits)
 
      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
 
    int i = static_cast<int>(lhs.bigits_.size()) - 1;
 
    int j = static_cast<int>(rhs.bigits_.size()) - 1;
 
    int end = i - j;
 
    if (end < 0) end = 0;
 
    for (; i >= end; --i, --j) {
 
      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
 
      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
 
    }
 
    if (i != j) return i > j ? 1 : -1;
 
    return 0;
 
  }
 

	
 
  // Returns compare(lhs1 + lhs2, rhs).
 
  friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
 
                                         const bigint& rhs) {
 
    auto minimum = [](int a, int b) { return a < b ? a : b; };
 
    auto maximum = [](int a, int b) { return a > b ? a : b; };
 
    int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
 
    int num_rhs_bigits = rhs.num_bigits();
 
    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
 
    if (max_lhs_bigits > num_rhs_bigits) return 1;
 
    auto get_bigit = [](const bigint& n, int i) -> bigit {
 
      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
 
    };
 
    double_bigit borrow = 0;
 
    int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
 
    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
 
      double_bigit sum =
 
          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
 
      bigit rhs_bigit = get_bigit(rhs, i);
 
      if (sum > rhs_bigit + borrow) return 1;
 
      borrow = rhs_bigit + borrow - sum;
 
      if (borrow > 1) return -1;
 
      borrow <<= bigit_bits;
 
    }
 
    return borrow != 0 ? -1 : 0;
 
  }
 

	
 
  // Assigns pow(10, exp) to this bigint.
 
  FMT_CONSTEXPR20 void assign_pow10(int exp) {
 
    FMT_ASSERT(exp >= 0, "");
 
    if (exp == 0) return *this = 1;
 
    // Find the top bit.
 
    int bitmask = 1;
 
    while (exp >= bitmask) bitmask <<= 1;
 
    bitmask >>= 1;
 
    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
 
    // repeated squaring and multiplication.
 
    *this = 5;
 
    bitmask >>= 1;
 
    while (bitmask != 0) {
 
      square();
 
      if ((exp & bitmask) != 0) *this *= 5;
 
      bitmask >>= 1;
 
    }
 
    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
 
  }
 

	
 
  FMT_CONSTEXPR20 void square() {
 
    int num_bigits = static_cast<int>(bigits_.size());
 
    int num_result_bigits = 2 * num_bigits;
 
    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
 
    bigits_.resize(to_unsigned(num_result_bigits));
 
    auto sum = uint128_t();
 
    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
 
      // Compute bigit at position bigit_index of the result by adding
 
      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
 
      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
 
        // Most terms are multiplied twice which can be optimized in the future.
 
        sum += static_cast<double_bigit>(n[i]) * n[j];
 
      }
 
      (*this)[bigit_index] = static_cast<bigit>(sum);
 
      sum >>= num_bits<bigit>();  // Compute the carry.
 
    }
 
    // Do the same for the top half.
 
    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
 
         ++bigit_index) {
 
      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
 
        sum += static_cast<double_bigit>(n[i++]) * n[j--];
 
      (*this)[bigit_index] = static_cast<bigit>(sum);
 
      sum >>= num_bits<bigit>();
 
    }
 
    remove_leading_zeros();
 
    exp_ *= 2;
 
  }
 

	
 
  // If this bigint has a bigger exponent than other, adds trailing zero to make
 
  // exponents equal. This simplifies some operations such as subtraction.
 
  FMT_CONSTEXPR20 void align(const bigint& other) {
 
    int exp_difference = exp_ - other.exp_;
 
    if (exp_difference <= 0) return;
 
    int num_bigits = static_cast<int>(bigits_.size());
 
    bigits_.resize(to_unsigned(num_bigits + exp_difference));
 
    for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
 
      bigits_[j] = bigits_[i];
 
    std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
 
    exp_ -= exp_difference;
 
  }
 

	
 
  // Divides this bignum by divisor, assigning the remainder to this and
 
  // returning the quotient.
 
  FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
 
    FMT_ASSERT(this != &divisor, "");
 
    if (compare(*this, divisor) < 0) return 0;
 
    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
 
    align(divisor);
 
    int quotient = 0;
 
    do {
 
      subtract_aligned(divisor);
 
      ++quotient;
 
    } while (compare(*this, divisor) >= 0);
 
    return quotient;
 
  }
 
};
 

	
 
// format_dragon flags.
 
enum dragon {
 
  predecessor_closer = 1,
 
  fixup = 2,  // Run fixup to correct exp10 which can be off by one.
 
  fixed = 4,
 
};
 

	
 
// Formats a floating-point number using a variation of the Fixed-Precision
 
// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
 
// https://fmt.dev/papers/p372-steele.pdf.
 
FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
 
                                          unsigned flags, int num_digits,
 
                                          buffer<char>& buf, int& exp10) {
 
  bigint numerator;    // 2 * R in (FPP)^2.
 
  bigint denominator;  // 2 * S in (FPP)^2.
 
  // lower and upper are differences between value and corresponding boundaries.
 
  bigint lower;             // (M^- in (FPP)^2).
 
  bigint upper_store;       // upper's value if different from lower.
 
  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
 
  // Shift numerator and denominator by an extra bit or two (if lower boundary
 
  // is closer) to make lower and upper integers. This eliminates multiplication
 
  // by 2 during later computations.
 
  bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
 
  int shift = is_predecessor_closer ? 2 : 1;
 
  if (value.e >= 0) {
 
    numerator = value.f;
 
    numerator <<= value.e + shift;
 
    lower = 1;
 
    lower <<= value.e;
 
    if (is_predecessor_closer) {
 
      upper_store = 1;
 
      upper_store <<= value.e + 1;
 
      upper = &upper_store;
 
    }
 
    denominator.assign_pow10(exp10);
 
    denominator <<= shift;
 
  } else if (exp10 < 0) {
 
    numerator.assign_pow10(-exp10);
 
    lower.assign(numerator);
 
    if (is_predecessor_closer) {
 
      upper_store.assign(numerator);
 
      upper_store <<= 1;
 
      upper = &upper_store;
 
    }
 
    numerator *= value.f;
 
    numerator <<= shift;
 
    denominator = 1;
 
    denominator <<= shift - value.e;
 
  } else {
 
    numerator = value.f;
 
    numerator <<= shift;
 
    denominator.assign_pow10(exp10);
 
    denominator <<= shift - value.e;
 
    lower = 1;
 
    if (is_predecessor_closer) {
 
      upper_store = 1ULL << 1;
 
      upper = &upper_store;
 
    }
 
  }
 
  int even = static_cast<int>((value.f & 1) == 0);
 
  if (!upper) upper = &lower;
 
  if ((flags & dragon::fixup) != 0) {
 
    if (add_compare(numerator, *upper, denominator) + even <= 0) {
 
      --exp10;
 
      numerator *= 10;
 
      if (num_digits < 0) {
 
        lower *= 10;
 
        if (upper != &lower) *upper *= 10;
 
      }
 
    }
 
    if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
 
  }
 
  // Invariant: value == (numerator / denominator) * pow(10, exp10).
 
  if (num_digits < 0) {
 
    // Generate the shortest representation.
 
    num_digits = 0;
 
    char* data = buf.data();
 
    for (;;) {
 
      int digit = numerator.divmod_assign(denominator);
 
      bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
 
      // numerator + upper >[=] pow10:
 
      bool high = add_compare(numerator, *upper, denominator) + even > 0;
 
      data[num_digits++] = static_cast<char>('0' + digit);
 
      if (low || high) {
 
        if (!low) {
 
          ++data[num_digits - 1];
 
        } else if (high) {
 
          int result = add_compare(numerator, numerator, denominator);
 
          // Round half to even.
 
          if (result > 0 || (result == 0 && (digit % 2) != 0))
 
            ++data[num_digits - 1];
 
        }
 
        buf.try_resize(to_unsigned(num_digits));
 
        exp10 -= num_digits - 1;
 
        return;
 
      }
 
      numerator *= 10;
 
      lower *= 10;
 
      if (upper != &lower) *upper *= 10;
 
    }
 
  }
 
  // Generate the given number of digits.
 
  exp10 -= num_digits - 1;
 
  if (num_digits == 0) {
 
    denominator *= 10;
 
    auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
 
    buf.push_back(digit);
 
    return;
 
  }
 
  buf.try_resize(to_unsigned(num_digits));
 
  for (int i = 0; i < num_digits - 1; ++i) {
 
    int digit = numerator.divmod_assign(denominator);
 
    buf[i] = static_cast<char>('0' + digit);
 
    numerator *= 10;
 
  }
 
  int digit = numerator.divmod_assign(denominator);
 
  auto result = add_compare(numerator, numerator, denominator);
 
  if (result > 0 || (result == 0 && (digit % 2) != 0)) {
 
    if (digit == 9) {
 
      const auto overflow = '0' + 10;
 
      buf[num_digits - 1] = overflow;
 
      // Propagate the carry.
 
      for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
 
        buf[i] = '0';
 
        ++buf[i - 1];
 
      }
 
      if (buf[0] == overflow) {
 
        buf[0] = '1';
 
        ++exp10;
 
      }
 
      return;
 
    }
 
    ++digit;
 
  }
 
  buf[num_digits - 1] = static_cast<char>('0' + digit);
 
}
 

	
 
// Formats a floating-point number using the hexfloat format.
 
template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
 
FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
 
                                     float_specs specs, buffer<char>& buf) {
 
  // float is passed as double to reduce the number of instantiations and to
 
  // simplify implementation.
 
  static_assert(!std::is_same<Float, float>::value, "");
 

	
 
  using info = dragonbox::float_info<Float>;
 

	
 
  // Assume Float is in the format [sign][exponent][significand].
 
  using carrier_uint = typename info::carrier_uint;
 

	
 
  constexpr auto num_float_significand_bits =
 
      detail::num_significand_bits<Float>();
 

	
 
  basic_fp<carrier_uint> f(value);
 
  f.e += num_float_significand_bits;
 
  if (!has_implicit_bit<Float>()) --f.e;
 

	
 
  constexpr auto num_fraction_bits =
 
      num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
 
  constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
 

	
 
  constexpr auto leading_shift = ((num_xdigits - 1) * 4);
 
  const auto leading_mask = carrier_uint(0xF) << leading_shift;
 
  const auto leading_xdigit =
 
      static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
 
  if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
 

	
 
  int print_xdigits = num_xdigits - 1;
 
  if (precision >= 0 && print_xdigits > precision) {
 
    const int shift = ((print_xdigits - precision - 1) * 4);
 
    const auto mask = carrier_uint(0xF) << shift;
 
    const auto v = static_cast<uint32_t>((f.f & mask) >> shift);
 

	
 
    if (v >= 8) {
 
      const auto inc = carrier_uint(1) << (shift + 4);
 
      f.f += inc;
 
      f.f &= ~(inc - 1);
 
    }
 

	
 
    // Check long double overflow
 
    if (!has_implicit_bit<Float>()) {
 
      const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
 
      if ((f.f & implicit_bit) == implicit_bit) {
 
        f.f >>= 4;
 
        f.e += 4;
 
      }
 
    }
 

	
 
    print_xdigits = precision;
 
  }
 

	
 
  char xdigits[num_bits<carrier_uint>() / 4];
 
  detail::fill_n(xdigits, sizeof(xdigits), '0');
 
  format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
 

	
 
  // Remove zero tail
 
  while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
 

	
 
  buf.push_back('0');
 
  buf.push_back(specs.upper ? 'X' : 'x');
 
  buf.push_back(xdigits[0]);
 
  if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision)
 
    buf.push_back('.');
 
  buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
 
  for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0');
 

	
 
  buf.push_back(specs.upper ? 'P' : 'p');
 

	
 
  uint32_t abs_e;
 
  if (f.e < 0) {
 
    buf.push_back('-');
 
    abs_e = static_cast<uint32_t>(-f.e);
 
  } else {
 
    buf.push_back('+');
 
    abs_e = static_cast<uint32_t>(f.e);
 
  }
 
  format_decimal<char>(appender(buf), abs_e, detail::count_digits(abs_e));
 
}
 

	
 
template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
 
FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
 
                                     float_specs specs, buffer<char>& buf) {
 
  format_hexfloat(static_cast<double>(value), precision, specs, buf);
 
}
 

	
 
template <typename Float>
 
FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
 
                                  buffer<char>& buf) -> int {
 
  // float is passed as double to reduce the number of instantiations.
 
  static_assert(!std::is_same<Float, float>::value, "");
 
  FMT_ASSERT(value >= 0, "value is negative");
 
  auto converted_value = convert_float(value);
 

	
 
  const bool fixed = specs.format == float_format::fixed;
 
  if (value <= 0) {  // <= instead of == to silence a warning.
 
    if (precision <= 0 || !fixed) {
 
      buf.push_back('0');
 
      return 0;
 
    }
 
    buf.try_resize(to_unsigned(precision));
 
    fill_n(buf.data(), precision, '0');
 
    return -precision;
 
  }
 

	
 
  int exp = 0;
 
  bool use_dragon = true;
 
  unsigned dragon_flags = 0;
 
  if (!is_fast_float<Float>()) {
 
    const auto inv_log2_10 = 0.3010299956639812;  // 1 / log2(10)
 
    using info = dragonbox::float_info<decltype(converted_value)>;
 
    const auto f = basic_fp<typename info::carrier_uint>(converted_value);
 
    // Compute exp, an approximate power of 10, such that
 
    //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
 
    // This is based on log10(value) == log2(value) / log2(10) and approximation
 
    // of log2(value) by e + num_fraction_bits idea from double-conversion.
 
    exp = static_cast<int>(
 
        std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
 
    dragon_flags = dragon::fixup;
 
  } else if (!is_constant_evaluated() && precision < 0) {
 
    // Use Dragonbox for the shortest format.
 
    if (specs.binary32) {
 
      auto dec = dragonbox::to_decimal(static_cast<float>(value));
 
      write<char>(buffer_appender<char>(buf), dec.significand);
 
      return dec.exponent;
 
    }
 
    auto dec = dragonbox::to_decimal(static_cast<double>(value));
 
    write<char>(buffer_appender<char>(buf), dec.significand);
 
    return dec.exponent;
 
  } else if (is_constant_evaluated()) {
 
    // Use Grisu + Dragon4 for the given precision:
 
    // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
 
    const int min_exp = -60;  // alpha in Grisu.
 
    int cached_exp10 = 0;     // K in Grisu.
 
    fp normalized = normalize(fp(converted_value));
 
    const auto cached_pow = get_cached_power(
 
        min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
 
    normalized = normalized * cached_pow;
 
    gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
 
    if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
 
        !is_constant_evaluated()) {
 
      exp += handler.exp10;
 
      buf.try_resize(to_unsigned(handler.size));
 
      use_dragon = false;
 
    } else {
 
      exp += handler.size - cached_exp10 - 1;
 
      precision = handler.precision;
 
    }
 
  } else {
 
    // Extract significand bits and exponent bits.
 
    using info = dragonbox::float_info<double>;
 
    auto br = bit_cast<uint64_t>(static_cast<double>(value));
 

	
 
    const uint64_t significand_mask =
 
        (static_cast<uint64_t>(1) << num_significand_bits<double>()) - 1;
 
    uint64_t significand = (br & significand_mask);
 
    int exponent = static_cast<int>((br & exponent_mask<double>()) >>
 
                                    num_significand_bits<double>());
 

	
 
    if (exponent != 0) {  // Check if normal.
 
      exponent -= exponent_bias<double>() + num_significand_bits<double>();
 
      significand |=
 
          (static_cast<uint64_t>(1) << num_significand_bits<double>());
 
      significand <<= 1;
 
    } else {
 
      // Normalize subnormal inputs.
 
      FMT_ASSERT(significand != 0, "zeros should not appear hear");
 
      int shift = countl_zero(significand);
 
      FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
 
                 "");
 
      shift -= (num_bits<uint64_t>() - num_significand_bits<double>() - 2);
 
      exponent = (std::numeric_limits<double>::min_exponent -
 
                  num_significand_bits<double>()) -
 
                 shift;
 
      significand <<= shift;
 
    }
 

	
 
    // Compute the first several nonzero decimal significand digits.
 
    // We call the number we get the first segment.
 
    const int k = info::kappa - dragonbox::floor_log10_pow2(exponent);
 
    exp = -k;
 
    const int beta = exponent + dragonbox::floor_log2_pow10(k);
 
    uint64_t first_segment;
 
    bool has_more_segments;
 
    int digits_in_the_first_segment;
 
    {
 
      const auto r = dragonbox::umul192_upper128(
 
          significand << beta, dragonbox::get_cached_power(k));
 
      first_segment = r.high();
 
      has_more_segments = r.low() != 0;
 

	
 
      // The first segment can have 18 ~ 19 digits.
 
      if (first_segment >= 1000000000000000000ULL) {
 
        digits_in_the_first_segment = 19;
 
      } else {
 
        // When it is of 18-digits, we align it to 19-digits by adding a bogus
 
        // zero at the end.
 
        digits_in_the_first_segment = 18;
 
        first_segment *= 10;
 
      }
 
    }
 

	
 
    // Compute the actual number of decimal digits to print.
 
    if (fixed) {
 
      adjust_precision(precision, exp + digits_in_the_first_segment);
 
    }
 

	
 
    // Use Dragon4 only when there might be not enough digits in the first
 
    // segment.
 
    if (digits_in_the_first_segment > precision) {
 
      use_dragon = false;
 

	
 
      if (precision <= 0) {
 
        exp += digits_in_the_first_segment;
 

	
 
        if (precision < 0) {
 
          // Nothing to do, since all we have are just leading zeros.
 
          buf.try_resize(0);
 
        } else {
 
          // We may need to round-up.
 
          buf.try_resize(1);
 
          if ((first_segment | static_cast<uint64_t>(has_more_segments)) >
 
              5000000000000000000ULL) {
 
            buf[0] = '1';
 
          } else {
 
            buf[0] = '0';
 
          }
 
        }
 
      }  // precision <= 0
 
      else {
 
        exp += digits_in_the_first_segment - precision;
 

	
 
        // When precision > 0, we divide the first segment into three
 
        // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits
 
        // in 32-bits which usually allows faster calculation than in
 
        // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize
 
        // division-by-constant for large 64-bit divisors, we do it here
 
        // manually. The magic number 7922816251426433760 below is equal to
 
        // ceil(2^(64+32) / 10^10).
 
        const uint32_t first_subsegment = static_cast<uint32_t>(
 
            dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >>
 
            32);
 
        const uint64_t second_third_subsegments =
 
            first_segment - first_subsegment * 10000000000ULL;
 

	
 
        uint64_t prod;
 
        uint32_t digits;
 
        bool should_round_up;
 
        int number_of_digits_to_print = precision > 9 ? 9 : precision;
 

	
 
        // Print a 9-digits subsegment, either the first or the second.
 
        auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
 
          int number_of_digits_printed = 0;
 

	
 
          // If we want to print an odd number of digits from the subsegment,
 
          if ((number_of_digits_to_print & 1) != 0) {
 
            // Convert to 64-bit fixed-point fractional form with 1-digit
 
            // integer part. The magic number 720575941 is a good enough
 
            // approximation of 2^(32 + 24) / 10^8; see
 
            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
 
            // for details.
 
            prod = ((subsegment * static_cast<uint64_t>(720575941)) >> 24) + 1;
 
            digits = static_cast<uint32_t>(prod >> 32);
 
            *buffer = static_cast<char>('0' + digits);
 
            number_of_digits_printed++;
 
          }
 
          // If we want to print an even number of digits from the
 
          // first_subsegment,
 
          else {
 
            // Convert to 64-bit fixed-point fractional form with 2-digits
 
            // integer part. The magic number 450359963 is a good enough
 
            // approximation of 2^(32 + 20) / 10^7; see
 
            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
 
            // for details.
 
            prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
 
            digits = static_cast<uint32_t>(prod >> 32);
 
            copy2(buffer, digits2(digits));
 
            number_of_digits_printed += 2;
 
          }
 

	
 
          // Print all digit pairs.
 
          while (number_of_digits_printed < number_of_digits_to_print) {
 
            prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
 
            digits = static_cast<uint32_t>(prod >> 32);
 
            copy2(buffer + number_of_digits_printed, digits2(digits));
 
            number_of_digits_printed += 2;
 
          }
 
        };
 

	
 
        // Print first subsegment.
 
        print_subsegment(first_subsegment, buf.data());
 

	
 
        // Perform rounding if the first subsegment is the last subsegment to
 
        // print.
 
        if (precision <= 9) {
 
          // Rounding inside the subsegment.
 
          // We round-up if:
 
          //  - either the fractional part is strictly larger than 1/2, or
 
          //  - the fractional part is exactly 1/2 and the last digit is odd.
 
          // We rely on the following observations:
 
          //  - If fractional_part >= threshold, then the fractional part is
 
          //    strictly larger than 1/2.
 
          //  - If the MSB of fractional_part is set, then the fractional part
 
          //    must be at least 1/2.
 
          //  - When the MSB of fractional_part is set, either
 
          //    second_third_subsegments being nonzero or has_more_segments
 
          //    being true means there are further digits not printed, so the
 
          //    fractional part is strictly larger than 1/2.
 
          if (precision < 9) {
 
            uint32_t fractional_part = static_cast<uint32_t>(prod);
 
            should_round_up = fractional_part >=
 
                                  data::fractional_part_rounding_thresholds
 
                                      [8 - number_of_digits_to_print] ||
 
                              ((fractional_part >> 31) &
 
                               ((digits & 1) | (second_third_subsegments != 0) |
 
                                has_more_segments)) != 0;
 
          }
 
          // Rounding at the subsegment boundary.
 
          // In this case, the fractional part is at least 1/2 if and only if
 
          // second_third_subsegments >= 5000000000ULL, and is strictly larger
 
          // than 1/2 if we further have either second_third_subsegments >
 
          // 5000000000ULL or has_more_segments == true.
 
          else {
 
            should_round_up = second_third_subsegments > 5000000000ULL ||
 
                              (second_third_subsegments == 5000000000ULL &&
 
                               ((digits & 1) != 0 || has_more_segments));
 
          }
 
        }
 
        // Otherwise, print the second subsegment.
 
        else {
 
          // Compilers are not aware of how to leverage the maximum value of
 
          // second_third_subsegments to find out a better magic number which
 
          // allows us to eliminate an additional shift. 1844674407370955162 =
 
          // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))).
 
          const uint32_t second_subsegment =
 
              static_cast<uint32_t>(dragonbox::umul128_upper64(
 
                  second_third_subsegments, 1844674407370955162ULL));
 
          const uint32_t third_subsegment =
 
              static_cast<uint32_t>(second_third_subsegments) -
 
              second_subsegment * 10;
 

	
 
          number_of_digits_to_print = precision - 9;
 
          print_subsegment(second_subsegment, buf.data() + 9);
 

	
 
          // Rounding inside the subsegment.
 
          if (precision < 18) {
 
            // The condition third_subsegment != 0 implies that the segment was
 
            // of 19 digits, so in this case the third segment should be
 
            // consisting of a genuine digit from the input.
 
            uint32_t fractional_part = static_cast<uint32_t>(prod);
 
            should_round_up = fractional_part >=
 
                                  data::fractional_part_rounding_thresholds
 
                                      [8 - number_of_digits_to_print] ||
 
                              ((fractional_part >> 31) &
 
                               ((digits & 1) | (third_subsegment != 0) |
 
                                has_more_segments)) != 0;
 
          }
 
          // Rounding at the subsegment boundary.
 
          else {
 
            // In this case, the segment must be of 19 digits, thus
 
            // the third subsegment should be consisting of a genuine digit from
 
            // the input.
 
            should_round_up = third_subsegment > 5 ||
 
                              (third_subsegment == 5 &&
 
                               ((digits & 1) != 0 || has_more_segments));
 
          }
 
        }
 

	
 
        // Round-up if necessary.
 
        if (should_round_up) {
 
          ++buf[precision - 1];
 
          for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) {
 
            buf[i] = '0';
 
            ++buf[i - 1];
 
          }
 
          if (buf[0] > '9') {
 
            buf[0] = '1';
 
            if (fixed)
 
              buf[precision++] = '0';
 
            else
 
              ++exp;
 
          }
 
        }
 
        buf.try_resize(to_unsigned(precision));
 
      }
 
    }  // if (digits_in_the_first_segment > precision)
 
    else {
 
      // Adjust the exponent for its use in Dragon4.
 
      exp += digits_in_the_first_segment - 1;
 
    }
 
  }
 
  if (use_dragon) {
 
    auto f = basic_fp<uint128_t>();
 
    bool is_predecessor_closer = specs.binary32
 
                                     ? f.assign(static_cast<float>(value))
 
                                     : f.assign(converted_value);
 
    if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
 
    if (fixed) dragon_flags |= dragon::fixed;
 
    // Limit precision to the maximum possible number of significant digits in
 
    // an IEEE754 double because we don't need to generate zeros.
 
    const int max_double_digits = 767;
 
    if (precision > max_double_digits) precision = max_double_digits;
 
    format_dragon(f, dragon_flags, precision, buf, exp);
 
  }
 
  if (!fixed && !specs.showpoint) {
 
    // Remove trailing zeros.
 
    auto num_digits = buf.size();
 
    while (num_digits > 0 && buf[num_digits - 1] == '0') {
 
      --num_digits;
 
      ++exp;
 
    }
 
    buf.try_resize(num_digits);
 
  }
 
  return exp;
 
}
 
template <typename Char, typename OutputIt, typename T>
 
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
 
                                 format_specs<Char> specs, locale_ref loc)
 
    -> OutputIt {
 
  float_specs fspecs = parse_float_type_spec(specs);
 
  fspecs.sign = specs.sign;
 
  if (std::signbit(value)) {  // value < 0 is false for NaN so use signbit.
 
  if (detail::signbit(value)) {  // value < 0 is false for NaN so use signbit.
 
    fspecs.sign = sign::minus;
 
    value = -value;
 
  } else if (fspecs.sign == sign::minus) {
 
    fspecs.sign = sign::none;
 
  }
 

	
 
  if (!std::isfinite(value))
 
    return write_nonfinite(out, std::isinf(value), specs, fspecs);
 
  if (!detail::isfinite(value))
 
    return write_nonfinite(out, detail::isnan(value), specs, fspecs);
 

	
 
  if (specs.align == align::numeric && fspecs.sign) {
 
    auto it = reserve(out, 1);
 
    *it++ = static_cast<Char>(data::signs[fspecs.sign]);
 
    *it++ = detail::sign<Char>(fspecs.sign);
 
    out = base_iterator(out, it);
 
    fspecs.sign = sign::none;
 
    if (specs.width != 0) --specs.width;
 
@@ -1920,489 +3841,219 @@ OutputIt write(OutputIt out, T value, ba
 

	
 
  memory_buffer buffer;
 
  if (fspecs.format == float_format::hex) {
 
    if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
 
    snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
 
    return write_bytes(out, {buffer.data(), buffer.size()}, specs);
 
  }
 
  int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
 
    if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
 
    format_hexfloat(convert_float(value), specs.precision, fspecs, buffer);
 
    return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
 
                                     specs);
 
  }
 
  int precision = specs.precision >= 0 || specs.type == presentation_type::none
 
                      ? specs.precision
 
                      : 6;
 
  if (fspecs.format == float_format::exp) {
 
    if (precision == max_value<int>())
 
      FMT_THROW(format_error("number is too big"));
 
      throw_format_error("number is too big");
 
    else
 
      ++precision;
 
  } else if (fspecs.format != float_format::fixed && precision == 0) {
 
    precision = 1;
 
  }
 
  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
 
  fspecs.use_grisu = is_fast_float<T>();
 
  int exp = format_float(promote_float(value), precision, fspecs, buffer);
 
  int exp = format_float(convert_float(value), precision, fspecs, buffer);
 
  fspecs.precision = precision;
 
  Char point =
 
      fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
 
  auto fp = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
 
  return write_float(out, fp, specs, fspecs, point);
 
  auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
 
  return write_float(out, f, specs, fspecs, loc);
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_floating_point<T>::value)>
 
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs,
 
                           locale_ref loc = {}) -> OutputIt {
 
  if (const_check(!is_supported_floating_point(value))) return out;
 
  return specs.localized && write_loc(out, value, specs, loc)
 
             ? out
 
             : write_float(out, value, specs, loc);
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_fast_float<T>::value)>
 
OutputIt write(OutputIt out, T value) {
 
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
 
  if (is_constant_evaluated()) return write(out, value, format_specs<Char>());
 
  if (const_check(!is_supported_floating_point(value))) return out;
 

	
 
  using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
 
  using uint = typename dragonbox::float_info<floaty>::carrier_uint;
 
  auto bits = bit_cast<uint>(value);
 

	
 
  auto fspecs = float_specs();
 
  auto sign_bit = bits & (uint(1) << (num_bits<uint>() - 1));
 
  if (sign_bit != 0) {
 
  if (detail::signbit(value)) {
 
    fspecs.sign = sign::minus;
 
    value = -value;
 
  }
 

	
 
  static const auto specs = basic_format_specs<Char>();
 
  uint mask = exponent_mask<floaty>();
 
  if ((bits & mask) == mask)
 
    return write_nonfinite(out, std::isinf(value), specs, fspecs);
 
  constexpr auto specs = format_specs<Char>();
 
  using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
 
  using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
 
  floaty_uint mask = exponent_mask<floaty>();
 
  if ((bit_cast<floaty_uint>(value) & mask) == mask)
 
    return write_nonfinite(out, std::isnan(value), specs, fspecs);
 

	
 
  auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
 
  return write_float(out, dec, specs, fspecs, static_cast<Char>('.'));
 
  return write_float(out, dec, specs, fspecs, {});
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(std::is_floating_point<T>::value &&
 
          FMT_ENABLE_IF(is_floating_point<T>::value &&
 
                        !is_fast_float<T>::value)>
 
inline OutputIt write(OutputIt out, T value) {
 
  return write(out, value, basic_format_specs<Char>());
 
inline auto write(OutputIt out, T value) -> OutputIt {
 
  return write(out, value, format_specs<Char>());
 
}
 

	
 
template <typename Char, typename OutputIt>
 
auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
 
    -> OutputIt {
 
  FMT_ASSERT(false, "");
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write_char(OutputIt out, Char value,
 
                    const basic_format_specs<Char>& specs) {
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  return write_padded(out, specs, 1, [=](iterator it) {
 
    *it++ = value;
 
    return it;
 
  });
 
}
 

	
 
template <typename Char, typename OutputIt, typename UIntPtr>
 
OutputIt write_ptr(OutputIt out, UIntPtr value,
 
                   const basic_format_specs<Char>* specs) {
 
  int num_digits = count_digits<4>(value);
 
  auto size = to_unsigned(num_digits) + size_t(2);
 
  using iterator = remove_reference_t<decltype(reserve(out, 0))>;
 
  auto write = [=](iterator it) {
 
    *it++ = static_cast<Char>('0');
 
    *it++ = static_cast<Char>('x');
 
    return format_uint<4, Char>(it, value, num_digits);
 
  };
 
  return specs ? write_padded<align::right>(out, *specs, size, write)
 
               : base_iterator(out, write(reserve(out, size)));
 
}
 

	
 
template <typename T> struct is_integral : std::is_integral<T> {};
 
template <> struct is_integral<int128_t> : std::true_type {};
 
template <> struct is_integral<uint128_t> : std::true_type {};
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, monostate) {
 
  FMT_ASSERT(false, "");
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
OutputIt write(OutputIt out, string_view value) {
 
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
 
    -> OutputIt {
 
  auto it = reserve(out, value.size());
 
  it = copy_str<Char>(value.begin(), value.end(), it);
 
  it = copy_str_noinline<Char>(value.begin(), value.end(), it);
 
  return base_iterator(out, it);
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_string<T>::value)>
 
constexpr auto write(OutputIt out, const T& value) -> OutputIt {
 
  return write<Char>(out, to_string_view(value));
 
}
 

	
 
// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
 
template <
 
    typename Char, typename OutputIt, typename T,
 
    bool check =
 
        std::is_enum<T>::value && !std::is_same<T, Char>::value &&
 
        mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
 
            type::custom_type,
 
    FMT_ENABLE_IF(check)>
 
FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
 
  return write<Char>(out, static_cast<underlying_t<T>>(value));
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(std::is_same<T, bool>::value)>
 
FMT_CONSTEXPR auto write(OutputIt out, T value,
 
                         const format_specs<Char>& specs = {}, locale_ref = {})
 
    -> OutputIt {
 
  return specs.type != presentation_type::none &&
 
                 specs.type != presentation_type::string
 
             ? write(out, value ? 1 : 0, specs, {})
 
             : write_bytes(out, value ? "true" : "false", specs);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, basic_string_view<Char> value) {
 
  auto it = reserve(out, value.size());
 
  it = std::copy(value.begin(), value.end(), it);
 
  return base_iterator(out, it);
 
}
 

	
 
template <typename Char>
 
buffer_appender<Char> write(buffer_appender<Char> out,
 
                            basic_string_view<Char> value) {
 
  get_container(out).append(value.begin(), value.end());
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(is_integral<T>::value &&
 
                        !std::is_same<T, bool>::value &&
 
                        !std::is_same<T, Char>::value)>
 
OutputIt write(OutputIt out, T value) {
 
  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
 
  bool negative = is_negative(value);
 
  // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
 
  if (negative) abs_value = ~abs_value + 1;
 
  int num_digits = count_digits(abs_value);
 
  auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
 
  auto it = reserve(out, size);
 
  if (auto ptr = to_pointer<Char>(it, size)) {
 
    if (negative) *ptr++ = static_cast<Char>('-');
 
    format_decimal<Char>(ptr, abs_value, num_digits);
 
    return out;
 
  }
 
  if (negative) *it++ = static_cast<Char>('-');
 
  it = format_decimal<Char>(it, abs_value, num_digits).end;
 
  return base_iterator(out, it);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, bool value) {
 
  return write<Char>(out, string_view(value ? "true" : "false"));
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, Char value) {
 
FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
 
  auto it = reserve(out, 1);
 
  *it++ = value;
 
  return base_iterator(out, it);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, const Char* value) {
 
  if (!value) {
 
    FMT_THROW(format_error("string pointer is null"));
 
  } else {
 
    auto length = std::char_traits<Char>::length(value);
 
    out = write(out, basic_string_view<Char>(value, length));
 
  }
 
FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
 
    -> OutputIt {
 
  if (value) return write(out, basic_string_view<Char>(value));
 
  throw_format_error("string pointer is null");
 
  return out;
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt write(OutputIt out, const void* value) {
 
  return write_ptr<Char>(out, to_uintptr(value), nullptr);
 
}
 

	
 
template <typename Char, typename OutputIt, typename T>
 
auto write(OutputIt out, const T& value) -> typename std::enable_if<
 
    mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
 
        type::custom_type,
 
    OutputIt>::type {
 
  using context_type = basic_format_context<OutputIt, Char>;
 
  using formatter_type =
 
      conditional_t<has_formatter<T, context_type>::value,
 
                    typename context_type::template formatter_type<T>,
 
                    fallback_formatter<T, Char>>;
 
  context_type ctx(out, {}, {});
 
  return formatter_type().format(value, ctx);
 
template <typename Char, typename OutputIt, typename T,
 
          FMT_ENABLE_IF(std::is_same<T, void>::value)>
 
auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
 
           locale_ref = {}) -> OutputIt {
 
  return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
 
}
 

	
 
// A write overload that handles implicit conversions.
 
template <typename Char, typename OutputIt, typename T,
 
          typename Context = basic_format_context<OutputIt, Char>>
 
FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
 
    std::is_class<T>::value && !is_string<T>::value &&
 
        !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
 
        !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
 
                             value))>>::value,
 
    OutputIt> {
 
  return write<Char>(out, arg_mapper<Context>().map(value));
 
}
 

	
 
template <typename Char, typename OutputIt, typename T,
 
          typename Context = basic_format_context<OutputIt, Char>>
 
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
 
    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
 
                   OutputIt> {
 
  auto ctx = Context(out, {}, {});
 
  return typename Context::template formatter_type<T>().format(value, ctx);
 
}
 

	
 
// An argument visitor that formats the argument and writes it via the output
 
// iterator. It's a class and not a generic lambda for compatibility with C++11.
 
template <typename OutputIt, typename Char> struct default_arg_formatter {
 
  using context = basic_format_context<OutputIt, Char>;
 

	
 
  OutputIt out;
 
template <typename Char> struct default_arg_formatter {
 
  using iterator = buffer_appender<Char>;
 
  using context = buffer_context<Char>;
 

	
 
  iterator out;
 
  basic_format_args<context> args;
 
  locale_ref loc;
 

	
 
  template <typename T> OutputIt operator()(T value) {
 
  template <typename T> auto operator()(T value) -> iterator {
 
    return write<Char>(out, value);
 
  }
 

	
 
  OutputIt operator()(typename basic_format_arg<context>::handle handle) {
 
  auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
 
    basic_format_parse_context<Char> parse_ctx({});
 
    basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
 
    handle.format(parse_ctx, format_ctx);
 
    context format_ctx(out, args, loc);
 
    h.format(parse_ctx, format_ctx);
 
    return format_ctx.out();
 
  }
 
};
 

	
 
template <typename OutputIt, typename Char,
 
          typename ErrorHandler = error_handler>
 
class arg_formatter_base {
 
 public:
 
  using iterator = OutputIt;
 
  using char_type = Char;
 
  using format_specs = basic_format_specs<Char>;
 

	
 
 private:
 
  iterator out_;
 
  locale_ref locale_;
 
  format_specs* specs_;
 

	
 
  // Attempts to reserve space for n extra characters in the output range.
 
  // Returns a pointer to the reserved range or a reference to out_.
 
  auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) {
 
    return detail::reserve(out_, n);
 
  }
 

	
 
  using reserve_iterator = remove_reference_t<decltype(
 
      detail::reserve(std::declval<iterator&>(), 0))>;
 

	
 
  template <typename T> void write_int(T value, const format_specs& spec) {
 
    using uint_type = uint32_or_64_or_128_t<T>;
 
    int_writer<iterator, Char, uint_type> w(out_, locale_, value, spec);
 
    handle_int_type_spec(spec.type, w);
 
    out_ = w.out;
 
  }
 

	
 
  void write(char value) {
 
    auto&& it = reserve(1);
 
    *it++ = value;
 
  }
 

	
 
  template <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
 
  void write(Ch value) {
 
    out_ = detail::write<Char>(out_, value);
 
  }
 

	
 
  void write(string_view value) {
 
    auto&& it = reserve(value.size());
 
    it = copy_str<Char>(value.begin(), value.end(), it);
 
  }
 
  void write(wstring_view value) {
 
    static_assert(std::is_same<Char, wchar_t>::value, "");
 
    auto&& it = reserve(value.size());
 
    it = std::copy(value.begin(), value.end(), it);
 
  }
 

	
 
  template <typename Ch>
 
  void write(const Ch* s, size_t size, const format_specs& specs) {
 
    auto width = specs.width != 0
 
                     ? count_code_points(basic_string_view<Ch>(s, size))
 
                     : 0;
 
    out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
 
      return copy_str<Char>(s, s + size, it);
 
    });
 
  }
 

	
 
  template <typename Ch>
 
  void write(basic_string_view<Ch> s, const format_specs& specs = {}) {
 
    out_ = detail::write(out_, s, specs);
 
  }
 

	
 
  void write_pointer(const void* p) {
 
    out_ = write_ptr<char_type>(out_, to_uintptr(p), specs_);
 
  }
 

	
 
  struct char_spec_handler : ErrorHandler {
 
    arg_formatter_base& formatter;
 
    Char value;
 

	
 
    char_spec_handler(arg_formatter_base& f, Char val)
 
        : formatter(f), value(val) {}
 

	
 
    void on_int() {
 
      // char is only formatted as int if there are specs.
 
      formatter.write_int(static_cast<int>(value), *formatter.specs_);
 
    }
 
    void on_char() {
 
      if (formatter.specs_)
 
        formatter.out_ = write_char(formatter.out_, value, *formatter.specs_);
 
      else
 
        formatter.write(value);
 
template <typename Char> struct arg_formatter {
 
  using iterator = buffer_appender<Char>;
 
  using context = buffer_context<Char>;
 

	
 
  iterator out;
 
  const format_specs<Char>& specs;
 
  locale_ref locale;
 

	
 
  template <typename T>
 
  FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
 
    return detail::write(out, value, specs, locale);
 
  }
 
  auto operator()(typename basic_format_arg<context>::handle) -> iterator {
 
    // User-defined types are handled separately because they require access
 
    // to the parse context.
 
    return out;
 
    }
 
  };
 

	
 
  struct cstring_spec_handler : error_handler {
 
    arg_formatter_base& formatter;
 
    const Char* value;
 

	
 
    cstring_spec_handler(arg_formatter_base& f, const Char* val)
 
        : formatter(f), value(val) {}
 

	
 
    void on_string() { formatter.write(value); }
 
    void on_pointer() { formatter.write_pointer(value); }
 
  };
 

	
 
 protected:
 
  iterator out() { return out_; }
 
  format_specs* specs() { return specs_; }
 

	
 
  void write(bool value) {
 
    if (specs_)
 
      write(string_view(value ? "true" : "false"), *specs_);
 
    else
 
      out_ = detail::write<Char>(out_, value);
 
  }
 

	
 
  void write(const Char* value) {
 
    if (!value) {
 
      FMT_THROW(format_error("string pointer is null"));
 
    } else {
 
      auto length = std::char_traits<char_type>::length(value);
 
      basic_string_view<char_type> sv(value, length);
 
      specs_ ? write(sv, *specs_) : write(sv);
 
    }
 
  }
 

	
 
 public:
 
  arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc)
 
      : out_(out), locale_(loc), specs_(s) {}
 

	
 
  iterator operator()(monostate) {
 
    FMT_ASSERT(false, "invalid argument type");
 
    return out_;
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
 
  FMT_INLINE iterator operator()(T value) {
 
    if (specs_)
 
      write_int(value, *specs_);
 
    else
 
      out_ = detail::write<Char>(out_, value);
 
    return out_;
 
  }
 

	
 
  iterator operator()(Char value) {
 
    handle_char_specs(specs_,
 
                      char_spec_handler(*this, static_cast<Char>(value)));
 
    return out_;
 
  }
 

	
 
  iterator operator()(bool value) {
 
    if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
 
    write(value != 0);
 
    return out_;
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
 
  iterator operator()(T value) {
 
    auto specs = specs_ ? *specs_ : format_specs();
 
    if (const_check(is_supported_floating_point(value)))
 
      out_ = detail::write(out_, value, specs, locale_);
 
    else
 
      FMT_ASSERT(false, "unsupported float argument type");
 
    return out_;
 
  }
 

	
 
  iterator operator()(const Char* value) {
 
    if (!specs_) return write(value), out_;
 
    handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value));
 
    return out_;
 
  }
 

	
 
  iterator operator()(basic_string_view<Char> value) {
 
    if (specs_) {
 
      check_string_type_spec(specs_->type, error_handler());
 
      write(value, *specs_);
 
    } else {
 
      write(value);
 
    }
 
    return out_;
 
  }
 

	
 
  iterator operator()(const void* value) {
 
    if (specs_) check_pointer_type_spec(specs_->type, error_handler());
 
    write_pointer(value);
 
    return out_;
 
  }
 
};
 

	
 
/** The default argument formatter. */
 
template <typename OutputIt, typename Char>
 
class arg_formatter : public arg_formatter_base<OutputIt, Char> {
 
 private:
 
  using char_type = Char;
 
  using base = arg_formatter_base<OutputIt, Char>;
 
  using context_type = basic_format_context<OutputIt, Char>;
 

	
 
  context_type& ctx_;
 
  basic_format_parse_context<char_type>* parse_ctx_;
 
  const Char* ptr_;
 

	
 
 public:
 
  using iterator = typename base::iterator;
 
  using format_specs = typename base::format_specs;
 

	
 
  /**
 
    \rst
 
    Constructs an argument formatter object.
 
    *ctx* is a reference to the formatting context,
 
    *specs* contains format specifier information for standard argument types.
 
    \endrst
 
   */
 
  explicit arg_formatter(
 
      context_type& ctx,
 
      basic_format_parse_context<char_type>* parse_ctx = nullptr,
 
      format_specs* specs = nullptr, const Char* ptr = nullptr)
 
      : base(ctx.out(), specs, ctx.locale()),
 
        ctx_(ctx),
 
        parse_ctx_(parse_ctx),
 
        ptr_(ptr) {}
 

	
 
  using base::operator();
 

	
 
  /** Formats an argument of a user-defined type. */
 
  iterator operator()(typename basic_format_arg<context_type>::handle handle) {
 
    if (ptr_) advance_to(*parse_ctx_, ptr_);
 
    handle.format(*parse_ctx_, ctx_);
 
    return ctx_.out();
 
  }
 
};
 

	
 
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
 
  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
 
}
 

	
 
// Parses the range [begin, end) as an unsigned integer. This function assumes
 
// that the range is non-empty and the first character is a digit.
 
template <typename Char, typename ErrorHandler>
 
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
 
                                        ErrorHandler&& eh) {
 
  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
 
  unsigned value = 0;
 
  // Convert to unsigned to prevent a warning.
 
  constexpr unsigned max_int = max_value<int>();
 
  unsigned big = max_int / 10;
 
  do {
 
    // Check for overflow.
 
    if (value > big) {
 
      value = max_int + 1;
 
      break;
 
    }
 
    value = value * 10 + unsigned(*begin - '0');
 
    ++begin;
 
  } while (begin != end && '0' <= *begin && *begin <= '9');
 
  if (value > max_int) eh.on_error("number is too big");
 
  return static_cast<int>(value);
 
}
 

	
 
template <typename Context> class custom_formatter {
 
 private:
 
  using char_type = typename Context::char_type;
 

	
 
  basic_format_parse_context<char_type>& parse_ctx_;
 
  Context& ctx_;
 

	
 
 public:
 
  explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
 
                            Context& ctx)
 
      : parse_ctx_(parse_ctx), ctx_(ctx) {}
 

	
 
  void operator()(typename basic_format_arg<Context>::handle h) const {
 
    h.format(parse_ctx_, ctx_);
 
  }
 

	
 
template <typename Char> struct custom_formatter {
 
  basic_format_parse_context<Char>& parse_ctx;
 
  buffer_context<Char>& ctx;
 

	
 
  void operator()(
 
      typename basic_format_arg<buffer_context<Char>>::handle h) const {
 
    h.format(parse_ctx, ctx);
 
  }
 
  template <typename T> void operator()(T) const {}
 
};
 

	
 
template <typename T>
 
using is_integer =
 
    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
 
                  !std::is_same<T, char>::value &&
 
                  !std::is_same<T, wchar_t>::value>;
 

	
 
template <typename ErrorHandler> class width_checker {
 
 public:
 
  explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
 

	
 
  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
 
  FMT_CONSTEXPR unsigned long long operator()(T value) {
 
  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
 
    if (is_negative(value)) handler_.on_error("negative width");
 
    return static_cast<unsigned long long>(value);
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
 
  FMT_CONSTEXPR unsigned long long operator()(T) {
 
  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
 
    handler_.on_error("width is not integer");
 
    return 0;
 
  }
 
@@ -2416,13 +4067,13 @@ template <typename ErrorHandler> class p
 
  explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
 

	
 
  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
 
  FMT_CONSTEXPR unsigned long long operator()(T value) {
 
  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
 
    if (is_negative(value)) handler_.on_error("negative precision");
 
    return static_cast<unsigned long long>(value);
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
 
  FMT_CONSTEXPR unsigned long long operator()(T) {
 
  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
 
    handler_.on_error("precision is not integer");
 
    return 0;
 
  }
 
@@ -2431,859 +4082,117 @@ template <typename ErrorHandler> class p
 
  ErrorHandler& handler_;
 
};
 

	
 
// A format specifier handler that sets fields in basic_format_specs.
 
template <typename Char> class specs_setter {
 
 public:
 
  explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
 
      : specs_(specs) {}
 

	
 
  FMT_CONSTEXPR specs_setter(const specs_setter& other)
 
      : specs_(other.specs_) {}
 

	
 
  FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
 
  FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
 
    specs_.fill = fill;
 
  }
 
  FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
 
  FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
 
  FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
 
  FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
 

	
 
  FMT_CONSTEXPR void on_zero() {
 
    specs_.align = align::numeric;
 
    specs_.fill[0] = Char('0');
 
  }
 

	
 
  FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
 
  FMT_CONSTEXPR void on_precision(int precision) {
 
    specs_.precision = precision;
 
  }
 
  FMT_CONSTEXPR void end_precision() {}
 

	
 
  FMT_CONSTEXPR void on_type(Char type) {
 
    specs_.type = static_cast<char>(type);
 
  }
 

	
 
 protected:
 
  basic_format_specs<Char>& specs_;
 
};
 

	
 
template <typename ErrorHandler> class numeric_specs_checker {
 
 public:
 
  FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type)
 
      : error_handler_(eh), arg_type_(arg_type) {}
 

	
 
  FMT_CONSTEXPR void require_numeric_argument() {
 
    if (!is_arithmetic_type(arg_type_))
 
      error_handler_.on_error("format specifier requires numeric argument");
 
  }
 

	
 
  FMT_CONSTEXPR void check_sign() {
 
    require_numeric_argument();
 
    if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
 
        arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
 
      error_handler_.on_error("format specifier requires signed argument");
 
    }
 
  }
 

	
 
  FMT_CONSTEXPR void check_precision() {
 
    if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
 
      error_handler_.on_error("precision not allowed for this argument type");
 
  }
 

	
 
 private:
 
  ErrorHandler& error_handler_;
 
  detail::type arg_type_;
 
};
 

	
 
// A format specifier handler that checks if specifiers are consistent with the
 
// argument type.
 
template <typename Handler> class specs_checker : public Handler {
 
 private:
 
  numeric_specs_checker<Handler> checker_;
 

	
 
  // Suppress an MSVC warning about using this in initializer list.
 
  FMT_CONSTEXPR Handler& error_handler() { return *this; }
 

	
 
 public:
 
  FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
 
      : Handler(handler), checker_(error_handler(), arg_type) {}
 

	
 
  FMT_CONSTEXPR specs_checker(const specs_checker& other)
 
      : Handler(other), checker_(error_handler(), other.arg_type_) {}
 

	
 
  FMT_CONSTEXPR void on_align(align_t align) {
 
    if (align == align::numeric) checker_.require_numeric_argument();
 
    Handler::on_align(align);
 
  }
 

	
 
  FMT_CONSTEXPR void on_plus() {
 
    checker_.check_sign();
 
    Handler::on_plus();
 
  }
 

	
 
  FMT_CONSTEXPR void on_minus() {
 
    checker_.check_sign();
 
    Handler::on_minus();
 
  }
 

	
 
  FMT_CONSTEXPR void on_space() {
 
    checker_.check_sign();
 
    Handler::on_space();
 
  }
 

	
 
  FMT_CONSTEXPR void on_hash() {
 
    checker_.require_numeric_argument();
 
    Handler::on_hash();
 
  }
 

	
 
  FMT_CONSTEXPR void on_zero() {
 
    checker_.require_numeric_argument();
 
    Handler::on_zero();
 
  }
 

	
 
  FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
 
};
 

	
 
template <template <typename> class Handler, typename FormatArg,
 
          typename ErrorHandler>
 
FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
 
FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
 
  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
 
  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
 
  return static_cast<int>(value);
 
}
 

	
 
struct auto_id {};
 

	
 
template <typename Context, typename ID>
 
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
 
FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
 
    typename Context::format_arg {
 
  auto arg = ctx.arg(id);
 
  if (!arg) ctx.on_error("argument not found");
 
  return arg;
 
}
 

	
 
// The standard format specifier handler with checking.
 
template <typename ParseContext, typename Context>
 
class specs_handler : public specs_setter<typename Context::char_type> {
 
 public:
 
  using char_type = typename Context::char_type;
 

	
 
  FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
 
                              ParseContext& parse_ctx, Context& ctx)
 
      : specs_setter<char_type>(specs),
 
        parse_context_(parse_ctx),
 
        context_(ctx) {}
 

	
 
  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
 
    this->specs_.width = get_dynamic_spec<width_checker>(
 
        get_arg(arg_id), context_.error_handler());
 
  }
 

	
 
  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
 
    this->specs_.precision = get_dynamic_spec<precision_checker>(
 
        get_arg(arg_id), context_.error_handler());
 
  }
 

	
 
  void on_error(const char* message) { context_.on_error(message); }
 

	
 
 private:
 
  // This is only needed for compatibility with gcc 4.4.
 
  using format_arg = typename Context::format_arg;
 

	
 
  FMT_CONSTEXPR format_arg get_arg(auto_id) {
 
    return detail::get_arg(context_, parse_context_.next_arg_id());
 
  }
 

	
 
  FMT_CONSTEXPR format_arg get_arg(int arg_id) {
 
    parse_context_.check_arg_id(arg_id);
 
    return detail::get_arg(context_, arg_id);
 
  }
 

	
 
  FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
 
    parse_context_.check_arg_id(arg_id);
 
    return detail::get_arg(context_, arg_id);
 
  }
 

	
 
  ParseContext& parse_context_;
 
  Context& context_;
 
};
 

	
 
enum class arg_id_kind { none, index, name };
 

	
 
// An argument reference.
 
template <typename Char> struct arg_ref {
 
  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
 

	
 
  FMT_CONSTEXPR explicit arg_ref(int index)
 
      : kind(arg_id_kind::index), val(index) {}
 
  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
 
      : kind(arg_id_kind::name), val(name) {}
 

	
 
  FMT_CONSTEXPR arg_ref& operator=(int idx) {
 
    kind = arg_id_kind::index;
 
    val.index = idx;
 
    return *this;
 
  }
 

	
 
  arg_id_kind kind;
 
  union value {
 
    FMT_CONSTEXPR value(int id = 0) : index{id} {}
 
    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 

	
 
    int index;
 
    basic_string_view<Char> name;
 
  } val;
 
};
 

	
 
// Format specifiers with width and precision resolved at formatting rather
 
// than parsing time to allow re-using the same parsed specifiers with
 
// different sets of arguments (precompilation of format strings).
 
template <typename Char>
 
struct dynamic_format_specs : basic_format_specs<Char> {
 
  arg_ref<Char> width_ref;
 
  arg_ref<Char> precision_ref;
 
};
 

	
 
// Format spec handler that saves references to arguments representing dynamic
 
// width and precision to be resolved at formatting time.
 
template <typename ParseContext>
 
class dynamic_specs_handler
 
    : public specs_setter<typename ParseContext::char_type> {
 
 public:
 
  using char_type = typename ParseContext::char_type;
 

	
 
  FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
 
                                      ParseContext& ctx)
 
      : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
 

	
 
  FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
 
      : specs_setter<char_type>(other),
 
        specs_(other.specs_),
 
        context_(other.context_) {}
 

	
 
  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
 
    specs_.width_ref = make_arg_ref(arg_id);
 
  }
 

	
 
  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
 
    specs_.precision_ref = make_arg_ref(arg_id);
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    context_.on_error(message);
 
  }
 

	
 
 private:
 
  using arg_ref_type = arg_ref<char_type>;
 

	
 
  FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) {
 
    context_.check_arg_id(arg_id);
 
    return arg_ref_type(arg_id);
 
  }
 

	
 
  FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
 
    return arg_ref_type(context_.next_arg_id());
 
  }
 

	
 
  FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
 
    context_.check_arg_id(arg_id);
 
    basic_string_view<char_type> format_str(
 
        context_.begin(), to_unsigned(context_.end() - context_.begin()));
 
    return arg_ref_type(arg_id);
 
  }
 

	
 
  dynamic_format_specs<char_type>& specs_;
 
  ParseContext& context_;
 
};
 

	
 
template <typename Char, typename IDHandler>
 
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
 
                                       IDHandler&& handler) {
 
  FMT_ASSERT(begin != end, "");
 
  Char c = *begin;
 
  if (c == '}' || c == ':') {
 
    handler();
 
    return begin;
 
  }
 
  if (c >= '0' && c <= '9') {
 
    int index = 0;
 
    if (c != '0')
 
      index = parse_nonnegative_int(begin, end, handler);
 
    else
 
      ++begin;
 
    if (begin == end || (*begin != '}' && *begin != ':'))
 
      handler.on_error("invalid format string");
 
    else
 
      handler(index);
 
    return begin;
 
  }
 
  if (!is_name_start(c)) {
 
    handler.on_error("invalid format string");
 
    return begin;
 
  }
 
  auto it = begin;
 
  do {
 
    ++it;
 
  } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
 
  handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
 
  return it;
 
}
 

	
 
// Adapts SpecHandler to IDHandler API for dynamic width.
 
template <typename SpecHandler, typename Char> struct width_adapter {
 
  explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
 

	
 
  FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
 
  FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
 
  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
 
    handler.on_dynamic_width(id);
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    handler.on_error(message);
 
  }
 

	
 
  SpecHandler& handler;
 
};
 

	
 
// Adapts SpecHandler to IDHandler API for dynamic precision.
 
template <typename SpecHandler, typename Char> struct precision_adapter {
 
  explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
 

	
 
  FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
 
  FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
 
  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
 
    handler.on_dynamic_precision(id);
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    handler.on_error(message);
 
  }
 

	
 
  SpecHandler& handler;
 
};
 

	
 
template <typename Char>
 
FMT_CONSTEXPR int code_point_length(const Char* begin) {
 
  if (const_check(sizeof(Char) != 1)) return 1;
 
  constexpr char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 
                              0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
 
  int len = lengths[static_cast<unsigned char>(*begin) >> 3];
 

	
 
  // Compute the pointer to the next character early so that the next
 
  // iteration can start working on the next character. Neither Clang
 
  // nor GCC figure out this reordering on their own.
 
  return len + !len;
 
}
 

	
 
template <typename Char> constexpr bool is_ascii_letter(Char c) {
 
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
 
}
 

	
 
// Converts a character to ASCII. Returns a number > 127 on conversion failure.
 
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
 
constexpr Char to_ascii(Char value) {
 
  return value;
 
}
 
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
 
constexpr typename std::underlying_type<Char>::type to_ascii(Char value) {
 
  return value;
 
}
 

	
 
// Parses fill and alignment.
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
 
                                      Handler&& handler) {
 
  FMT_ASSERT(begin != end, "");
 
  auto align = align::none;
 
  auto p = begin + code_point_length(begin);
 
  if (p >= end) p = begin;
 
  for (;;) {
 
    switch (to_ascii(*p)) {
 
    case '<':
 
      align = align::left;
 
      break;
 
    case '>':
 
      align = align::right;
 
      break;
 
#if FMT_DEPRECATED_NUMERIC_ALIGN
 
    case '=':
 
      align = align::numeric;
 
      break;
 
#endif
 
    case '^':
 
      align = align::center;
 
      break;
 
    }
 
    if (align != align::none) {
 
      if (p != begin) {
 
        auto c = *begin;
 
        if (c == '{')
 
          return handler.on_error("invalid fill character '{'"), begin;
 
        handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
 
        begin = p + 1;
 
      } else
 
        ++begin;
 
      handler.on_align(align);
 
      break;
 
    } else if (p == begin) {
 
      break;
 
    }
 
    p = begin;
 
  }
 
  return begin;
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
 
                                      Handler&& handler) {
 
  FMT_ASSERT(begin != end, "");
 
  if ('0' <= *begin && *begin <= '9') {
 
    handler.on_width(parse_nonnegative_int(begin, end, handler));
 
  } else if (*begin == '{') {
 
    ++begin;
 
    if (begin != end)
 
      begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
 
    if (begin == end || *begin != '}')
 
      return handler.on_error("invalid format string"), begin;
 
    ++begin;
 
  }
 
  return begin;
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
 
                                          Handler&& handler) {
 
  ++begin;
 
  auto c = begin != end ? *begin : Char();
 
  if ('0' <= c && c <= '9') {
 
    handler.on_precision(parse_nonnegative_int(begin, end, handler));
 
  } else if (c == '{') {
 
    ++begin;
 
    if (begin != end) {
 
      begin =
 
          parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
 
    }
 
    if (begin == end || *begin++ != '}')
 
      return handler.on_error("invalid format string"), begin;
 
  } else {
 
    return handler.on_error("missing precision specifier"), begin;
 
  }
 
  handler.end_precision();
 
  return begin;
 
}
 

	
 
// Parses standard format specifiers and sends notifications about parsed
 
// components to handler.
 
template <typename Char, typename SpecHandler>
 
FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
 
                                             SpecHandler&& handler) {
 
  if (begin == end) return begin;
 

	
 
  begin = parse_align(begin, end, handler);
 
  if (begin == end) return begin;
 

	
 
  // Parse sign.
 
  switch (to_ascii(*begin)) {
 
  case '+':
 
    handler.on_plus();
 
    ++begin;
 
    break;
 
  case '-':
 
    handler.on_minus();
 
    ++begin;
 
    break;
 
  case ' ':
 
    handler.on_space();
 
    ++begin;
 
    break;
 
  }
 
  if (begin == end) return begin;
 

	
 
  if (*begin == '#') {
 
    handler.on_hash();
 
    if (++begin == end) return begin;
 
  }
 

	
 
  // Parse zero flag.
 
  if (*begin == '0') {
 
    handler.on_zero();
 
    if (++begin == end) return begin;
 
  }
 

	
 
  begin = parse_width(begin, end, handler);
 
  if (begin == end) return begin;
 

	
 
  // Parse precision.
 
  if (*begin == '.') {
 
    begin = parse_precision(begin, end, handler);
 
  }
 

	
 
  // Parse type.
 
  if (begin != end && *begin != '}') handler.on_type(*begin++);
 
  return begin;
 
}
 

	
 
// Return the result via the out param to workaround gcc bug 77539.
 
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
 
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
 
  for (out = first; out != last; ++out) {
 
    if (*out == value) return true;
 
  }
 
  return false;
 
}
 

	
 
template <>
 
inline bool find<false, char>(const char* first, const char* last, char value,
 
                              const char*& out) {
 
  out = static_cast<const char*>(
 
      std::memchr(first, value, detail::to_unsigned(last - first)));
 
  return out != nullptr;
 
}
 

	
 
template <typename Handler, typename Char> struct id_adapter {
 
  Handler& handler;
 
  int arg_id;
 

	
 
  FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
 
  FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
 
  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
 
    arg_id = handler.on_arg_id(id);
 
  }
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    handler.on_error(message);
 
  }
 
};
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
 
                                                  const Char* end,
 
                                                  Handler&& handler) {
 
  ++begin;
 
  if (begin == end) return handler.on_error("invalid format string"), end;
 
  if (*begin == '}') {
 
    handler.on_replacement_field(handler.on_arg_id(), begin);
 
  } else if (*begin == '{') {
 
    handler.on_text(begin, begin + 1);
 
  } else {
 
    auto adapter = id_adapter<Handler, Char>{handler, 0};
 
    begin = parse_arg_id(begin, end, adapter);
 
    Char c = begin != end ? *begin : Char();
 
    if (c == '}') {
 
      handler.on_replacement_field(adapter.arg_id, begin);
 
    } else if (c == ':') {
 
      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
 
      if (begin == end || *begin != '}')
 
        return handler.on_error("unknown format specifier"), end;
 
    } else {
 
      return handler.on_error("missing '}' in format string"), end;
 
    }
 
  }
 
  return begin + 1;
 
}
 

	
 
template <bool IS_CONSTEXPR, typename Char, typename Handler>
 
FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
 
    basic_string_view<Char> format_str, Handler&& handler) {
 
  auto begin = format_str.data();
 
  auto end = begin + format_str.size();
 
  if (end - begin < 32) {
 
    // Use a simple loop instead of memchr for small strings.
 
    const Char* p = begin;
 
    while (p != end) {
 
      auto c = *p++;
 
      if (c == '{') {
 
        handler.on_text(begin, p - 1);
 
        begin = p = parse_replacement_field(p - 1, end, handler);
 
      } else if (c == '}') {
 
        if (p == end || *p != '}')
 
          return handler.on_error("unmatched '}' in format string");
 
        handler.on_text(begin, p);
 
        begin = ++p;
 
      }
 
    }
 
    handler.on_text(begin, end);
 
    return;
 
  }
 
  struct writer {
 
    FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) {
 
      if (pbegin == pend) return;
 
      for (;;) {
 
        const Char* p = nullptr;
 
        if (!find<IS_CONSTEXPR>(pbegin, pend, '}', p))
 
          return handler_.on_text(pbegin, pend);
 
        ++p;
 
        if (p == pend || *p != '}')
 
          return handler_.on_error("unmatched '}' in format string");
 
        handler_.on_text(pbegin, p);
 
        pbegin = p + 1;
 
      }
 
    }
 
    Handler& handler_;
 
  } write{handler};
 
  while (begin != end) {
 
    // Doing two passes with memchr (one for '{' and another for '}') is up to
 
    // 2.5x faster than the naive one-pass implementation on big format strings.
 
    const Char* p = begin;
 
    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
 
      return write(begin, end);
 
    write(begin, p);
 
    begin = parse_replacement_field(p, end, handler);
 
  }
 
}
 

	
 
template <typename T, typename ParseContext>
 
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
 
    ParseContext& ctx) {
 
  using char_type = typename ParseContext::char_type;
 
  using context = buffer_context<char_type>;
 
  using mapped_type =
 
      conditional_t<detail::mapped_type_constant<T, context>::value !=
 
                        type::custom_type,
 
                    decltype(arg_mapper<context>().map(std::declval<T>())), T>;
 
  auto f = conditional_t<has_formatter<mapped_type, context>::value,
 
                         formatter<mapped_type, char_type>,
 
                         detail::fallback_formatter<T, char_type>>();
 
  return f.parse(ctx);
 
}
 

	
 
template <typename OutputIt, typename Char, typename Context>
 
struct format_handler : detail::error_handler {
 
  basic_format_parse_context<Char> parse_context;
 
  Context context;
 

	
 
  format_handler(OutputIt out, basic_string_view<Char> str,
 
                 basic_format_args<Context> format_args, detail::locale_ref loc)
 
      : parse_context(str), context(out, format_args, loc) {}
 

	
 
  void on_text(const Char* begin, const Char* end) {
 
    auto size = to_unsigned(end - begin);
 
    auto out = context.out();
 
    auto&& it = reserve(out, size);
 
    it = std::copy_n(begin, size, it);
 
    context.advance_to(out);
 
  }
 

	
 
  int on_arg_id() { return parse_context.next_arg_id(); }
 
  int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
 
  int on_arg_id(basic_string_view<Char> id) {
 
    int arg_id = context.arg_id(id);
 
    if (arg_id < 0) on_error("argument not found");
 
    return arg_id;
 
  }
 

	
 
  FMT_INLINE void on_replacement_field(int id, const Char*) {
 
    auto arg = get_arg(context, id);
 
    context.advance_to(visit_format_arg(
 
        default_arg_formatter<OutputIt, Char>{context.out(), context.args(),
 
                                              context.locale()},
 
        arg));
 
  }
 

	
 
  const Char* on_format_specs(int id, const Char* begin, const Char* end) {
 
    auto arg = get_arg(context, id);
 
    if (arg.type() == type::custom_type) {
 
      advance_to(parse_context, begin);
 
      visit_format_arg(custom_formatter<Context>(parse_context, context), arg);
 
      return parse_context.begin();
 
    }
 
    auto specs = basic_format_specs<Char>();
 
    if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin)) {
 
      specs.type = static_cast<char>(*begin++);
 
    } else {
 
      using parse_context_t = basic_format_parse_context<Char>;
 
      specs_checker<specs_handler<parse_context_t, Context>> handler(
 
          specs_handler<parse_context_t, Context>(specs, parse_context,
 
                                                  context),
 
          arg.type());
 
      begin = parse_format_specs(begin, end, handler);
 
      if (begin == end || *begin != '}')
 
        on_error("missing '}' in format string");
 
    }
 
    context.advance_to(visit_format_arg(
 
        arg_formatter<OutputIt, Char>(context, &parse_context, &specs), arg));
 
    return begin;
 
  }
 
};
 

	
 
// A parse context with extra argument id checks. It is only used at compile
 
// time because adding checks at runtime would introduce substantial overhead
 
// and would be redundant since argument ids are checked when arguments are
 
// retrieved anyway.
 
template <typename Char, typename ErrorHandler = error_handler>
 
class compile_parse_context
 
    : public basic_format_parse_context<Char, ErrorHandler> {
 
 private:
 
  int num_args_;
 
  using base = basic_format_parse_context<Char, ErrorHandler>;
 

	
 
 public:
 
  explicit FMT_CONSTEXPR compile_parse_context(
 
      basic_string_view<Char> format_str, int num_args = max_value<int>(),
 
      ErrorHandler eh = {})
 
      : base(format_str, eh), num_args_(num_args) {}
 

	
 
  FMT_CONSTEXPR int next_arg_id() {
 
    int id = base::next_arg_id();
 
    if (id >= num_args_) this->on_error("argument not found");
 
    return id;
 
  }
 

	
 
  FMT_CONSTEXPR void check_arg_id(int id) {
 
    base::check_arg_id(id);
 
    if (id >= num_args_) this->on_error("argument not found");
 
  }
 
  using base::check_arg_id;
 
};
 

	
 
template <typename Char, typename ErrorHandler, typename... Args>
 
class format_string_checker {
 
 public:
 
  explicit FMT_CONSTEXPR format_string_checker(
 
      basic_string_view<Char> format_str, ErrorHandler eh)
 
      : context_(format_str, num_args, eh),
 
        parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
 

	
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 

	
 
  FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
 
  FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
 
  FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
 
    on_error("compile-time checks don't support named arguments");
 
    return 0;
 
  }
 

	
 
  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
 

	
 
  FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
 
                                            const Char*) {
 
    advance_to(context_, begin);
 
    return id < num_args ? parse_funcs_[id](context_) : begin;
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    context_.on_error(message);
 
  }
 

	
 
 private:
 
  using parse_context_type = compile_parse_context<Char, ErrorHandler>;
 
  enum { num_args = sizeof...(Args) };
 

	
 
  // Format specifier parsing function.
 
  using parse_func = const Char* (*)(parse_context_type&);
 

	
 
  parse_context_type context_;
 
  parse_func parse_funcs_[num_args > 0 ? num_args : 1];
 
};
 

	
 
// Converts string literals to basic_string_view.
 
template <typename Char, size_t N>
 
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
 
    const Char (&s)[N]) {
 
  // Remove trailing null character if needed. Won't be present if this is used
 
  // with raw character array (i.e. not defined as a string).
 
  return {s,
 
          N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
 
}
 

	
 
// Converts string_view to basic_string_view.
 
template <typename Char>
 
FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
 
    const std_string_view<Char>& s) {
 
  return {s.data(), s.size()};
 
}
 

	
 
#define FMT_STRING_IMPL(s, base)                                  \
 
  [] {                                                            \
 
    /* Use a macro-like name to avoid shadowing warnings. */      \
 
    struct FMT_COMPILE_STRING : base {                            \
 
      using char_type = fmt::remove_cvref_t<decltype(s[0])>;      \
 
      FMT_MAYBE_UNUSED FMT_CONSTEXPR                              \
 
      operator fmt::basic_string_view<char_type>() const {        \
 
        return fmt::detail::compile_string_to_view<char_type>(s); \
 
      }                                                           \
 
    };                                                            \
 
    return FMT_COMPILE_STRING();                                  \
 
  }()
 

	
 
/**
 
  \rst
 
  Constructs a compile-time format string from a string literal *s*.
 

	
 
  **Example**::
 

	
 
    // A compile-time error because 'd' is an invalid specifier for strings.
 
    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
 
  \endrst
 
 */
 
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
 

	
 
template <typename... Args, typename S,
 
          enable_if_t<(is_compile_string<S>::value), int>>
 
void check_format_string(S format_str) {
 
  FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
 
  using checker = format_string_checker<typename S::char_type, error_handler,
 
                                        remove_cvref_t<Args>...>;
 
  FMT_CONSTEXPR_DECL bool invalid_format =
 
      (parse_format_string<true>(s, checker(s, {})), true);
 
  (void)invalid_format;
 
}
 

	
 
template <template <typename> class Handler, typename Context>
 
void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
 
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
 
                                       arg_ref<typename Context::char_type> ref,
 
                         Context& ctx) {
 
  switch (ref.kind) {
 
  case arg_id_kind::none:
 
    break;
 
  case arg_id_kind::index:
 
    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
 
    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
 
                                              ctx.error_handler());
 
    break;
 
  case arg_id_kind::name:
 
    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
 
    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
 
                                              ctx.error_handler());
 
    break;
 
  }
 
}
 

	
 
using format_func = void (*)(detail::buffer<char>&, int, string_view);
 
#if FMT_USE_USER_DEFINED_LITERALS
 
template <typename Char> struct udl_formatter {
 
  basic_string_view<Char> str;
 

	
 
  template <typename... T>
 
  auto operator()(T&&... args) const -> std::basic_string<Char> {
 
    return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
 
  }
 
};
 

	
 
#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
template <typename T, typename Char, size_t N,
 
          fmt::detail_exported::fixed_string<Char, N> Str>
 
struct statically_named_arg : view {
 
  static constexpr auto name = Str.data;
 

	
 
  const T& value;
 
  statically_named_arg(const T& v) : value(v) {}
 
};
 

	
 
template <typename T, typename Char, size_t N,
 
          fmt::detail_exported::fixed_string<Char, N> Str>
 
struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
 

	
 
template <typename T, typename Char, size_t N,
 
          fmt::detail_exported::fixed_string<Char, N> Str>
 
struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
 
    : std::true_type {};
 

	
 
template <typename Char, size_t N,
 
          fmt::detail_exported::fixed_string<Char, N> Str>
 
struct udl_arg {
 
  template <typename T> auto operator=(T&& value) const {
 
    return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
 
  }
 
};
 
#  else
 
template <typename Char> struct udl_arg {
 
  const Char* str;
 

	
 
  template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
 
    return {str, std::forward<T>(value)};
 
  }
 
};
 
#  endif
 
#endif  // FMT_USE_USER_DEFINED_LITERALS
 

	
 
template <typename Locale, typename Char>
 
auto vformat(const Locale& loc, basic_string_view<Char> fmt,
 
             basic_format_args<buffer_context<type_identity_t<Char>>> args)
 
    -> std::basic_string<Char> {
 
  auto buf = basic_memory_buffer<Char>();
 
  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
 
  return {buf.data(), buf.size()};
 
}
 

	
 
using format_func = void (*)(detail::buffer<char>&, int, const char*);
 

	
 
FMT_API void format_error_code(buffer<char>& out, int error_code,
 
                               string_view message) FMT_NOEXCEPT;
 
                               string_view message) noexcept;
 

	
 
FMT_API void report_error(format_func func, int error_code,
 
                          string_view message) FMT_NOEXCEPT;
 
}  // namespace detail
 

	
 
template <typename OutputIt, typename Char>
 
using arg_formatter FMT_DEPRECATED_ALIAS =
 
    detail::arg_formatter<OutputIt, Char>;
 
                          const char* message) noexcept;
 
FMT_END_DETAIL_NAMESPACE
 

	
 
FMT_API auto vsystem_error(int error_code, string_view format_str,
 
                           format_args args) -> std::system_error;
 

	
 
/**
 
 An error returned by an operating system or a language runtime,
 
 for example a file opening error.
 
*/
 
FMT_CLASS_API
 
class FMT_API system_error : public std::runtime_error {
 
 private:
 
  void init(int err_code, string_view format_str, format_args args);
 

	
 
 protected:
 
  int error_code_;
 

	
 
  system_error() : std::runtime_error(""), error_code_(0) {}
 

	
 
 public:
 
  /**
 
   \rst
 
   Constructs a :class:`fmt::system_error` object with a description
 
   formatted with `fmt::format_system_error`. *message* and additional
 
   arguments passed into the constructor are formatted similarly to
 
   `fmt::format`.
 
 Constructs :class:`std::system_error` with a message formatted with
 
 ``fmt::format(fmt, args...)``.
 
  *error_code* is a system error code as given by ``errno``.
 

	
 
   **Example**::
 

	
 
     // This throws a system_error with the description
 
   // This throws std::system_error with the description
 
     //   cannot open file 'madeup': No such file or directory
 
     // or similar (system message may vary).
 
     const char *filename = "madeup";
 
@@ -3292,43 +4201,34 @@ class FMT_API system_error : public std:
 
       throw fmt::system_error(errno, "cannot open file '{}'", filename);
 
   \endrst
 
  */
 
  template <typename... Args>
 
  system_error(int error_code, string_view message, const Args&... args)
 
      : std::runtime_error("") {
 
    init(error_code, message, make_format_args(args...));
 
  }
 
  system_error(const system_error&) = default;
 
  system_error& operator=(const system_error&) = default;
 
  system_error(system_error&&) = default;
 
  system_error& operator=(system_error&&) = default;
 
  ~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
 

	
 
  int error_code() const { return error_code_; }
 
};
 
template <typename... T>
 
auto system_error(int error_code, format_string<T...> fmt, T&&... args)
 
    -> std::system_error {
 
  return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
 
}
 

	
 
/**
 
  \rst
 
  Formats an error returned by an operating system or a language runtime,
 
  for example a file opening error, and writes it to *out* in the following
 
  form:
 
  Formats an error message for an error returned by an operating system or a
 
  language runtime, for example a file opening error, and writes it to *out*.
 
  The format is the same as the one used by ``std::system_error(ec, message)``
 
  where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
 
  It is implementation-defined but normally looks like:
 

	
 
  .. parsed-literal::
 
     *<message>*: *<system-message>*
 

	
 
  where *<message>* is the passed message and *<system-message>* is
 
  the system message corresponding to the error code.
 
  where *<message>* is the passed message and *<system-message>* is the system
 
  message corresponding to the error code.
 
  *error_code* is a system error code as given by ``errno``.
 
  If *error_code* is not a valid error code such as -1, the system message
 
  may look like "Unknown error -1" and is platform-dependent.
 
  \endrst
 
 */
 
FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
 
                                 string_view message) FMT_NOEXCEPT;
 
                                 const char* message) noexcept;
 

	
 
// Reports a system error without throwing an exception.
 
// Can be used to report errors from destructors.
 
FMT_API void report_system_error(int error_code,
 
                                 string_view message) FMT_NOEXCEPT;
 
FMT_API void report_system_error(int error_code, const char* message) noexcept;
 

	
 
/** Fast integer formatter. */
 
class format_int {
 
@@ -3339,12 +4239,12 @@ class format_int {
 
  mutable char buffer_[buffer_size];
 
  char* str_;
 

	
 
  template <typename UInt> char* format_unsigned(UInt value) {
 
  template <typename UInt> auto format_unsigned(UInt value) -> char* {
 
    auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
 
    return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
 
  }
 

	
 
  template <typename Int> char* format_signed(Int value) {
 
  template <typename Int> auto format_signed(Int value) -> char* {
 
    auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
 
    bool negative = value < 0;
 
    if (negative) abs_value = 0 - abs_value;
 
@@ -3363,7 +4263,7 @@ class format_int {
 
      : str_(format_unsigned(value)) {}
 

	
 
  /** Returns the number of characters written to the output buffer. */
 
  size_t size() const {
 
  auto size() const -> size_t {
 
    return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
 
  }
 

	
 
@@ -3371,13 +4271,13 @@ class format_int {
 
    Returns a pointer to the output buffer content. No terminating null
 
    character is appended.
 
   */
 
  const char* data() const { return str_; }
 
  auto data() const -> const char* { return str_; }
 

	
 
  /**
 
    Returns a pointer to the output buffer content with terminating null
 
    character appended.
 
   */
 
  const char* c_str() const {
 
  auto c_str() const -> const char* {
 
    buffer_[buffer_size - 1] = '\0';
 
    return str_;
 
  }
 
@@ -3387,121 +4287,25 @@ class format_int {
 
    Returns the content of the output buffer as an ``std::string``.
 
    \endrst
 
   */
 
  std::string str() const { return std::string(str_, size()); }
 
  auto str() const -> std::string { return std::string(str_, size()); }
 
};
 

	
 
// A formatter specialization for the core types corresponding to detail::type
 
// constants.
 
template <typename T, typename Char>
 
struct formatter<T, Char,
 
                 enable_if_t<detail::type_constant<T, Char>::value !=
 
                             detail::type::custom_type>> {
 
  FMT_CONSTEXPR formatter() = default;
 

	
 
  // Parses format specifiers stopping either at the end of the range or at the
 
  // terminating '}'.
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    using handler_type = detail::dynamic_specs_handler<ParseContext>;
 
    auto type = detail::type_constant<T, Char>::value;
 
    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
 
                                                type);
 
    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
 
    auto eh = ctx.error_handler();
 
    switch (type) {
 
    case detail::type::none_type:
 
      FMT_ASSERT(false, "invalid argument type");
 
      break;
 
    case detail::type::int_type:
 
    case detail::type::uint_type:
 
    case detail::type::long_long_type:
 
    case detail::type::ulong_long_type:
 
    case detail::type::int128_type:
 
    case detail::type::uint128_type:
 
    case detail::type::bool_type:
 
      handle_int_type_spec(specs_.type,
 
                           detail::int_type_checker<decltype(eh)>(eh));
 
      break;
 
    case detail::type::char_type:
 
      handle_char_specs(
 
          &specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
 
      break;
 
    case detail::type::float_type:
 
      if (detail::const_check(FMT_USE_FLOAT))
 
        detail::parse_float_type_spec(specs_, eh);
 
      else
 
        FMT_ASSERT(false, "float support disabled");
 
      break;
 
    case detail::type::double_type:
 
      if (detail::const_check(FMT_USE_DOUBLE))
 
        detail::parse_float_type_spec(specs_, eh);
 
      else
 
        FMT_ASSERT(false, "double support disabled");
 
      break;
 
    case detail::type::long_double_type:
 
      if (detail::const_check(FMT_USE_LONG_DOUBLE))
 
        detail::parse_float_type_spec(specs_, eh);
 
      else
 
        FMT_ASSERT(false, "long double support disabled");
 
      break;
 
    case detail::type::cstring_type:
 
      detail::handle_cstring_type_spec(
 
          specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
 
      break;
 
    case detail::type::string_type:
 
      detail::check_string_type_spec(specs_.type, eh);
 
      break;
 
    case detail::type::pointer_type:
 
      detail::check_pointer_type_spec(specs_.type, eh);
 
      break;
 
    case detail::type::custom_type:
 
      // Custom format specifiers should be checked in parse functions of
 
      // formatter specializations.
 
      break;
 
    }
 
    return it;
 
  }
 
struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
 
    : private formatter<detail::format_as_t<T>> {
 
  using base = formatter<detail::format_as_t<T>>;
 
  using base::parse;
 

	
 
  template <typename FormatContext>
 
  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
 
                                                       specs_.width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(
 
        specs_.precision, specs_.precision_ref, ctx);
 
    using af = detail::arg_formatter<typename FormatContext::iterator,
 
                                     typename FormatContext::char_type>;
 
    return visit_format_arg(af(ctx, nullptr, &specs_),
 
                            detail::make_arg<FormatContext>(val));
 
  }
 

	
 
 private:
 
  detail::dynamic_format_specs<Char> specs_;
 
  auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    return base::format(format_as(value), ctx);
 
  }
 
};
 

	
 
#define FMT_FORMAT_AS(Type, Base)                                             \
 
  template <typename Char>                                                    \
 
  struct formatter<Type, Char> : formatter<Base, Char> {                      \
 
    template <typename FormatContext>                                         \
 
    auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
 
      return formatter<Base, Char>::format(val, ctx);                         \
 
    }                                                                         \
 
  }
 

	
 
FMT_FORMAT_AS(signed char, int);
 
FMT_FORMAT_AS(unsigned char, unsigned);
 
FMT_FORMAT_AS(short, int);
 
FMT_FORMAT_AS(unsigned short, unsigned);
 
FMT_FORMAT_AS(long, long long);
 
FMT_FORMAT_AS(unsigned long, unsigned long long);
 
FMT_FORMAT_AS(Char*, const Char*);
 
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
 
FMT_FORMAT_AS(std::nullptr_t, const void*);
 
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
 

	
 
template <typename Char>
 
struct formatter<void*, Char> : formatter<const void*, Char> {
 
  template <typename FormatContext>
 
  auto format(void* val, FormatContext& ctx) -> decltype(ctx.out()) {
 
  auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    return formatter<const void*, Char>::format(val, ctx);
 
  }
 
};
 
@@ -3509,88 +4313,12 @@ struct formatter<void*, Char> : formatte
 
template <typename Char, size_t N>
 
struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
 
  template <typename FormatContext>
 
  auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
 
  FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    return formatter<basic_string_view<Char>, Char>::format(val, ctx);
 
  }
 
};
 

	
 
// A formatter for types known only at run time such as variant alternatives.
 
//
 
// Usage:
 
//   using variant = std::variant<int, std::string>;
 
//   template <>
 
//   struct formatter<variant>: dynamic_formatter<> {
 
//     auto format(const variant& v, format_context& ctx) {
 
//       return visit([&](const auto& val) {
 
//           return dynamic_formatter<>::format(val, ctx);
 
//       }, v);
 
//     }
 
//   };
 
template <typename Char = char> class dynamic_formatter {
 
 private:
 
  struct null_handler : detail::error_handler {
 
    void on_align(align_t) {}
 
    void on_plus() {}
 
    void on_minus() {}
 
    void on_space() {}
 
    void on_hash() {}
 
  };
 

	
 
 public:
 
  template <typename ParseContext>
 
  auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    format_str_ = ctx.begin();
 
    // Checks are deferred to formatting time when the argument type is known.
 
    detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
 
    return parse_format_specs(ctx.begin(), ctx.end(), handler);
 
  }
 

	
 
  template <typename T, typename FormatContext>
 
  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
 
    handle_specs(ctx);
 
    detail::specs_checker<null_handler> checker(
 
        null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
 
    checker.on_align(specs_.align);
 
    switch (specs_.sign) {
 
    case sign::none:
 
      break;
 
    case sign::plus:
 
      checker.on_plus();
 
      break;
 
    case sign::minus:
 
      checker.on_minus();
 
      break;
 
    case sign::space:
 
      checker.on_space();
 
      break;
 
    }
 
    if (specs_.alt) checker.on_hash();
 
    if (specs_.precision >= 0) checker.end_precision();
 
    using af = detail::arg_formatter<typename FormatContext::iterator,
 
                                     typename FormatContext::char_type>;
 
    visit_format_arg(af(ctx, nullptr, &specs_),
 
                     detail::make_arg<FormatContext>(val));
 
    return ctx.out();
 
  }
 

	
 
 private:
 
  template <typename Context> void handle_specs(Context& ctx) {
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
 
                                                       specs_.width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(
 
        specs_.precision, specs_.precision_ref, ctx);
 
  }
 

	
 
  detail::dynamic_format_specs<Char> specs_;
 
  const Char* format_str_;
 
};
 

	
 
template <typename Char, typename ErrorHandler>
 
FMT_CONSTEXPR void advance_to(
 
    basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
 
  ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
 
}
 

	
 
/**
 
  \rst
 
  Converts ``p`` to ``const void*`` for pointer formatting.
 
@@ -3600,13 +4328,39 @@ FMT_CONSTEXPR void advance_to(
 
    auto s = fmt::format("{}", fmt::ptr(p));
 
  \endrst
 
 */
 
template <typename T> inline const void* ptr(const T* p) { return p; }
 
template <typename T> inline const void* ptr(const std::unique_ptr<T>& p) {
 
template <typename T> auto ptr(T p) -> const void* {
 
  static_assert(std::is_pointer<T>::value, "");
 
  return detail::bit_cast<const void*>(p);
 
}
 
template <typename T, typename Deleter>
 
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
 
  return p.get();
 
}
 
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
 
  return p.get();
 
}
 
template <typename T> inline const void* ptr(const std::shared_ptr<T>& p) {
 
  return p.get();
 
}
 

	
 
/**
 
  \rst
 
  Converts ``e`` to the underlying type.
 

	
 
  **Example**::
 

	
 
    enum class color { red, green, blue };
 
    auto s = fmt::format("{}", fmt::underlying(color::red));
 
  \endrst
 
 */
 
template <typename Enum>
 
constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
 
  return static_cast<underlying_t<Enum>>(e);
 
}
 

	
 
namespace enums {
 
template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
 
constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
 
  return static_cast<underlying_t<Enum>>(e);
 
}
 
}  // namespace enums
 

	
 
class bytes {
 
 private:
 
@@ -3619,17 +4373,13 @@ class bytes {
 

	
 
template <> struct formatter<bytes> {
 
 private:
 
  detail::dynamic_format_specs<char> specs_;
 
  detail::dynamic_format_specs<> specs_;
 

	
 
 public:
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    using handler_type = detail::dynamic_specs_handler<ParseContext>;
 
    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
 
    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
 
                                                detail::type::string_type);
 
    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
 
    detail::check_string_type_spec(specs_.type, ctx.error_handler());
 
    return it;
 
  }
 

	
 
  template <typename FormatContext>
 
@@ -3642,31 +4392,89 @@ template <> struct formatter<bytes> {
 
  }
 
};
 

	
 
template <typename It, typename Sentinel, typename Char>
 
struct arg_join : detail::view {
 
// group_digits_view is not derived from view because it copies the argument.
 
template <typename T> struct group_digits_view { T value; };
 

	
 
/**
 
  \rst
 
  Returns a view that formats an integer value using ',' as a locale-independent
 
  thousands separator.
 

	
 
  **Example**::
 

	
 
    fmt::print("{}", fmt::group_digits(12345));
 
    // Output: "12,345"
 
  \endrst
 
 */
 
template <typename T> auto group_digits(T value) -> group_digits_view<T> {
 
  return {value};
 
}
 

	
 
template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
 
 private:
 
  detail::dynamic_format_specs<> specs_;
 

	
 
 public:
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
 
    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
 
                              detail::type::int_type);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(group_digits_view<T> t, FormatContext& ctx)
 
      -> decltype(ctx.out()) {
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
 
                                                       specs_.width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(
 
        specs_.precision, specs_.precision_ref, ctx);
 
    return detail::write_int(
 
        ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
 
        detail::digit_grouping<char>("\3", ","));
 
  }
 
};
 

	
 
// DEPRECATED! join_view will be moved to ranges.h.
 
template <typename It, typename Sentinel, typename Char = char>
 
struct join_view : detail::view {
 
  It begin;
 
  Sentinel end;
 
  basic_string_view<Char> sep;
 

	
 
  arg_join(It b, Sentinel e, basic_string_view<Char> s)
 
  join_view(It b, Sentinel e, basic_string_view<Char> s)
 
      : begin(b), end(e), sep(s) {}
 
};
 

	
 
template <typename It, typename Sentinel, typename Char>
 
struct formatter<arg_join<It, Sentinel, Char>, Char>
 
    : formatter<typename std::iterator_traits<It>::value_type, Char> {
 
struct formatter<join_view<It, Sentinel, Char>, Char> {
 
 private:
 
  using value_type =
 
#ifdef __cpp_lib_ranges
 
      std::iter_value_t<It>;
 
#else
 
      typename std::iterator_traits<It>::value_type;
 
#endif
 
  formatter<remove_cvref_t<value_type>, Char> value_formatter_;
 

	
 
 public:
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
 
    return value_formatter_.parse(ctx);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
 
      -> decltype(ctx.out()) {
 
    using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
 
  auto format(const join_view<It, Sentinel, Char>& value,
 
              FormatContext& ctx) const -> decltype(ctx.out()) {
 
    auto it = value.begin;
 
    auto out = ctx.out();
 
    if (it != value.end) {
 
      out = base::format(*it++, ctx);
 
      out = value_formatter_.format(*it, ctx);
 
      ++it;
 
      while (it != value.end) {
 
        out = std::copy(value.sep.begin(), value.sep.end(), out);
 
        out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
 
        ctx.advance_to(out);
 
        out = base::format(*it++, ctx);
 
        out = value_formatter_.format(*it, ctx);
 
        ++it;
 
      }
 
    }
 
    return out;
 
@@ -3674,22 +4482,17 @@ struct formatter<arg_join<It, Sentinel, 
 
};
 

	
 
/**
 
  Returns an object that formats the iterator range `[begin, end)` with elements
 
  Returns a view that formats the iterator range `[begin, end)` with elements
 
  separated by `sep`.
 
 */
 
template <typename It, typename Sentinel>
 
arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
 
  return {begin, end, sep};
 
}
 

	
 
template <typename It, typename Sentinel>
 
arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
 
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
 
  return {begin, end, sep};
 
}
 

	
 
/**
 
  \rst
 
  Returns an object that formats `range` with elements separated by `sep`.
 
  Returns a view that formats `range` with elements separated by `sep`.
 

	
 
  **Example**::
 

	
 
@@ -3704,14 +4507,8 @@ arg_join<It, Sentinel, wchar_t> join(It 
 
  \endrst
 
 */
 
template <typename Range>
 
arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, char> join(
 
    Range&& range, string_view sep) {
 
  return join(std::begin(range), std::end(range), sep);
 
}
 

	
 
template <typename Range>
 
arg_join<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t> join(
 
    Range&& range, wstring_view sep) {
 
auto join(Range&& range, string_view sep)
 
    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
 
  return join(std::begin(range), std::end(range), sep);
 
}
 

	
 
@@ -3727,209 +4524,117 @@ arg_join<detail::iterator_t<Range>, deta
 
  \endrst
 
 */
 
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
 
inline std::string to_string(const T& value) {
 
  std::string result;
 
  detail::write<char>(std::back_inserter(result), value);
 
  return result;
 
inline auto to_string(const T& value) -> std::string {
 
  auto buffer = memory_buffer();
 
  detail::write<char>(appender(buffer), value);
 
  return {buffer.data(), buffer.size()};
 
}
 

	
 
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
inline std::string to_string(T value) {
 
  // The buffer should be large enough to store the number including the sign or
 
  // "false" for bool.
 
FMT_NODISCARD inline auto to_string(T value) -> std::string {
 
  // The buffer should be large enough to store the number including the sign
 
  // or "false" for bool.
 
  constexpr int max_size = detail::digits10<T>() + 2;
 
  char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
 
  char* begin = buffer;
 
  return std::string(begin, detail::write<char>(begin, value));
 
}
 

	
 
/**
 
  Converts *value* to ``std::wstring`` using the default format for type *T*.
 
 */
 
template <typename T> inline std::wstring to_wstring(const T& value) {
 
  return format(L"{}", value);
 
}
 

	
 
template <typename Char, size_t SIZE>
 
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
 
FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
 
    -> std::basic_string<Char> {
 
  auto size = buf.size();
 
  detail::assume(size < std::basic_string<Char>().max_size());
 
  return std::basic_string<Char>(buf.data(), size);
 
}
 

	
 
FMT_BEGIN_DETAIL_NAMESPACE
 

	
 
template <typename Char>
 
void detail::vformat_to(
 
    detail::buffer<Char>& buf, basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args,
 
    detail::locale_ref loc) {
 
  using iterator = typename buffer_context<Char>::iterator;
 
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
 
                typename vformat_args<Char>::type args, locale_ref loc) {
 
  auto out = buffer_appender<Char>(buf);
 
  if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
 
  if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
 
    auto arg = args.get(0);
 
    if (!arg) error_handler().on_error("argument not found");
 
    visit_format_arg(default_arg_formatter<iterator, Char>{out, args, loc},
 
                     arg);
 
    visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
 
    return;
 
  }
 
  format_handler<iterator, Char, buffer_context<Char>> h(out, format_str, args,
 
                                                         loc);
 
  parse_format_string<false>(format_str, h);
 

	
 
  struct format_handler : error_handler {
 
    basic_format_parse_context<Char> parse_context;
 
    buffer_context<Char> context;
 

	
 
    format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
 
                   basic_format_args<buffer_context<Char>> p_args,
 
                   locale_ref p_loc)
 
        : parse_context(str), context(p_out, p_args, p_loc) {}
 

	
 
    void on_text(const Char* begin, const Char* end) {
 
      auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
 
      context.advance_to(write<Char>(context.out(), text));
 
    }
 

	
 
    FMT_CONSTEXPR auto on_arg_id() -> int {
 
      return parse_context.next_arg_id();
 
    }
 
    FMT_CONSTEXPR auto on_arg_id(int id) -> int {
 
      return parse_context.check_arg_id(id), id;
 
    }
 
    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
 
      int arg_id = context.arg_id(id);
 
      if (arg_id < 0) on_error("argument not found");
 
      return arg_id;
 
    }
 

	
 
    FMT_INLINE void on_replacement_field(int id, const Char*) {
 
      auto arg = get_arg(context, id);
 
      context.advance_to(visit_format_arg(
 
          default_arg_formatter<Char>{context.out(), context.args(),
 
                                      context.locale()},
 
          arg));
 
    }
 

	
 
    auto on_format_specs(int id, const Char* begin, const Char* end)
 
        -> const Char* {
 
      auto arg = get_arg(context, id);
 
      if (arg.type() == type::custom_type) {
 
        parse_context.advance_to(begin);
 
        visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
 
        return parse_context.begin();
 
      }
 
      auto specs = detail::dynamic_format_specs<Char>();
 
      begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
 
      detail::handle_dynamic_spec<detail::width_checker>(
 
          specs.width, specs.width_ref, context);
 
      detail::handle_dynamic_spec<detail::precision_checker>(
 
          specs.precision, specs.precision_ref, context);
 
      if (begin == end || *begin != '}')
 
        on_error("missing '}' in format string");
 
      auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
 
      context.advance_to(visit_format_arg(f, arg));
 
      return begin;
 
    }
 
  };
 
  detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
 
}
 

	
 
#ifndef FMT_HEADER_ONLY
 
extern template void detail::vformat_to(detail::buffer<char>&, string_view,
 
                                        basic_format_args<format_context>,
 
                                        detail::locale_ref);
 
namespace detail {
 

	
 
extern template FMT_API std::string grouping_impl<char>(locale_ref loc);
 
extern template FMT_API std::string grouping_impl<wchar_t>(locale_ref loc);
 
extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
 
extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
 
extern template FMT_API char decimal_point_impl(locale_ref loc);
 
extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
 
extern template int format_float<double>(double value, int precision,
 
                                         float_specs specs, buffer<char>& buf);
 
extern template int format_float<long double>(long double value, int precision,
 
                                              float_specs specs,
 
                                              buffer<char>& buf);
 
int snprintf_float(float value, int precision, float_specs specs,
 
                   buffer<char>& buf) = delete;
 
extern template int snprintf_float<double>(double value, int precision,
 
                                           float_specs specs,
 
                                           buffer<char>& buf);
 
extern template int snprintf_float<long double>(long double value,
 
                                                int precision,
 
                                                float_specs specs,
 
                                                buffer<char>& buf);
 
}  // namespace detail
 
#endif
 

	
 
template <typename S, typename Char = char_t<S>,
 
          FMT_ENABLE_IF(detail::is_string<S>::value)>
 
inline void vformat_to(
 
    detail::buffer<Char>& buf, const S& format_str,
 
    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args) {
 
  return detail::vformat_to(buf, to_string_view(format_str), args);
 
}
 

	
 
template <typename S, typename... Args, size_t SIZE = inline_buffer_size,
 
          typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
 
inline typename buffer_context<Char>::iterator format_to(
 
    basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  detail::vformat_to(buf, to_string_view(format_str), vargs);
 
  return detail::buffer_appender<Char>(buf);
 
}
 

	
 
template <typename OutputIt, typename Char = char>
 
using format_context_t = basic_format_context<OutputIt, Char>;
 

	
 
template <typename OutputIt, typename Char = char>
 
using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
 

	
 
template <typename OutputIt, typename Char = typename OutputIt::value_type>
 
using format_to_n_context FMT_DEPRECATED_ALIAS = buffer_context<Char>;
 

	
 
template <typename OutputIt, typename Char = typename OutputIt::value_type>
 
using format_to_n_args FMT_DEPRECATED_ALIAS =
 
    basic_format_args<buffer_context<Char>>;
 

	
 
template <typename OutputIt, typename Char, typename... Args>
 
FMT_DEPRECATED format_arg_store<buffer_context<Char>, Args...>
 
make_format_to_n_args(const Args&... args) {
 
  return format_arg_store<buffer_context<Char>, Args...>(args...);
 
}
 

	
 
template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>>
 
std::basic_string<Char> detail::vformat(
 
    basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  basic_memory_buffer<Char> buffer;
 
  detail::vformat_to(buffer, format_str, args);
 
  return to_string(buffer);
 
}
 

	
 
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
 
void vprint(std::FILE* f, basic_string_view<Char> format_str,
 
            wformat_args args) {
 
  wmemory_buffer buffer;
 
  detail::vformat_to(buffer, format_str, args);
 
  buffer.push_back(L'\0');
 
  if (std::fputws(buffer.data(), f) == -1)
 
    FMT_THROW(system_error(errno, "cannot write to file"));
 
}
 

	
 
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
 
void vprint(basic_string_view<Char> format_str, wformat_args args) {
 
  vprint(stdout, format_str, args);
 
}
 
extern template FMT_API void vformat_to(buffer<char>&, string_view,
 
                                        typename vformat_args<>::type,
 
                                        locale_ref);
 
extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
 
    -> thousands_sep_result<char>;
 
extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
 
    -> thousands_sep_result<wchar_t>;
 
extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
 
extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
 
#endif  // FMT_HEADER_ONLY
 

	
 
FMT_END_DETAIL_NAMESPACE
 

	
 
#if FMT_USE_USER_DEFINED_LITERALS
 
namespace detail {
 

	
 
#  if FMT_USE_UDL_TEMPLATE
 
template <typename Char, Char... CHARS> class udl_formatter {
 
 public:
 
  template <typename... Args>
 
  std::basic_string<Char> operator()(Args&&... args) const {
 
    static FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
 
    return format(FMT_STRING(s), std::forward<Args>(args)...);
 
  }
 
};
 
#  else
 
template <typename Char> struct udl_formatter {
 
  basic_string_view<Char> str;
 

	
 
  template <typename... Args>
 
  std::basic_string<Char> operator()(Args&&... args) const {
 
    return format(str, std::forward<Args>(args)...);
 
  }
 
};
 
#  endif  // FMT_USE_UDL_TEMPLATE
 

	
 
template <typename Char> struct udl_arg {
 
  const Char* str;
 

	
 
  template <typename T> named_arg<Char, T> operator=(T&& value) const {
 
    return {str, std::forward<T>(value)};
 
  }
 
};
 
}  // namespace detail
 

	
 
inline namespace literals {
 
#  if FMT_USE_UDL_TEMPLATE
 
#    pragma GCC diagnostic push
 
#    pragma GCC diagnostic ignored "-Wpedantic"
 
#    if FMT_CLANG_VERSION
 
#      pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
 
#    endif
 
template <typename Char, Char... CHARS>
 
FMT_CONSTEXPR detail::udl_formatter<Char, CHARS...> operator""_format() {
 
  return {};
 
}
 
#    pragma GCC diagnostic pop
 
#  else
 
/**
 
  \rst
 
  User-defined literal equivalent of :func:`fmt::format`.
 

	
 
  **Example**::
 

	
 
    using namespace fmt::literals;
 
    std::string message = "The answer is {}"_format(42);
 
  \endrst
 
 */
 
FMT_CONSTEXPR detail::udl_formatter<char> operator"" _format(const char* s,
 
                                                             size_t n) {
 
  return {{s, n}};
 
}
 
FMT_CONSTEXPR detail::udl_formatter<wchar_t> operator"" _format(
 
    const wchar_t* s, size_t n) {
 
  return {{s, n}};
 
}
 
#  endif  // FMT_USE_UDL_TEMPLATE
 

	
 
/**
 
  \rst
 
  User-defined literal equivalent of :func:`fmt::arg`.
 
@@ -3940,14 +4645,84 @@ FMT_CONSTEXPR detail::udl_formatter<wcha
 
    fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
 
  \endrst
 
 */
 
FMT_CONSTEXPR detail::udl_arg<char> operator"" _a(const char* s, size_t) {
 
#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
 
  using char_t = remove_cvref_t<decltype(Str.data[0])>;
 
  return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
 
}
 
#  else
 
constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
 
  return {s};
 
}
 
FMT_CONSTEXPR detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
 
  return {s};
 
}
 
#  endif
 
}  // namespace literals
 
#endif  // FMT_USE_USER_DEFINED_LITERALS
 

	
 
template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
 
inline auto vformat(const Locale& loc, string_view fmt, format_args args)
 
    -> std::string {
 
  return detail::vformat(loc, fmt, args);
 
}
 

	
 
template <typename Locale, typename... T,
 
          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
 
inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
 
    -> std::string {
 
  return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
 
}
 

	
 
template <typename OutputIt, typename Locale,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
 
                            detail::is_locale<Locale>::value)>
 
auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
 
                format_args args) -> OutputIt {
 
  using detail::get_buffer;
 
  auto&& buf = get_buffer<char>(out);
 
  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
 
  return detail::get_iterator(buf, out);
 
}
 

	
 
template <typename OutputIt, typename Locale, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
 
                            detail::is_locale<Locale>::value)>
 
FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
 
                          format_string<T...> fmt, T&&... args) -> OutputIt {
 
  return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
 
}
 

	
 
template <typename Locale, typename... T,
 
          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
 
FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
 
                                             format_string<T...> fmt,
 
                                             T&&... args) -> size_t {
 
  auto buf = detail::counting_buffer<>();
 
  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
 
                           detail::locale_ref(loc));
 
  return buf.count();
 
}
 

	
 
FMT_END_EXPORT
 

	
 
template <typename T, typename Char>
 
template <typename FormatContext>
 
FMT_CONSTEXPR FMT_INLINE auto
 
formatter<T, Char,
 
          enable_if_t<detail::type_constant<T, Char>::value !=
 
                      detail::type::custom_type>>::format(const T& val,
 
                                                          FormatContext& ctx)
 
    const -> decltype(ctx.out()) {
 
  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
 
      specs_.precision_ref.kind != detail::arg_id_kind::none) {
 
    auto specs = specs_;
 
    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
 
                                                       specs.width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(
 
        specs.precision, specs.precision_ref, ctx);
 
    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
 
  }
 
  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
 
}
 

	
 
FMT_END_NAMESPACE
 

	
 
#ifdef FMT_HEADER_ONLY
src/3rdparty/fmt/ostream.h
Show inline comments
 
new file 100644
 
// Formatting library for C++ - std::ostream support
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
//
 
// For the license information refer to format.h.
 

	
 
#ifndef FMT_OSTREAM_H_
 
#define FMT_OSTREAM_H_
 

	
 
#include <fstream>  // std::filebuf
 

	
 
#if defined(_WIN32) && defined(__GLIBCXX__)
 
#  include <ext/stdio_filebuf.h>
 
#  include <ext/stdio_sync_filebuf.h>
 
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
 
#  include <__std_stream>
 
#endif
 

	
 
#include "format.h"
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
namespace detail {
 

	
 
// Generate a unique explicit instantion in every translation unit using a tag
 
// type in an anonymous namespace.
 
namespace {
 
struct file_access_tag {};
 
}  // namespace
 
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
 
class file_access {
 
  friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
 
};
 

	
 
#if FMT_MSC_VERSION
 
template class file_access<file_access_tag, std::filebuf,
 
                           &std::filebuf::_Myfile>;
 
auto get_file(std::filebuf&) -> FILE*;
 
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
 
template class file_access<file_access_tag, std::__stdoutbuf<char>,
 
                           &std::__stdoutbuf<char>::__file_>;
 
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
 
#endif
 

	
 
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
 
#if FMT_MSC_VERSION
 
  if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
 
    if (FILE* f = get_file(*buf)) return write_console(f, data);
 
#elif defined(_WIN32) && defined(__GLIBCXX__)
 
  auto* rdbuf = os.rdbuf();
 
  FILE* c_file;
 
  if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
 
    c_file = sfbuf->file();
 
  else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
 
    c_file = fbuf->file();
 
  else
 
    return false;
 
  if (c_file) return write_console(c_file, data);
 
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
 
  if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
 
    if (FILE* f = get_file(*buf)) return write_console(f, data);
 
#else
 
  ignore_unused(os, data);
 
#endif
 
  return false;
 
}
 
inline bool write_ostream_unicode(std::wostream&,
 
                                  fmt::basic_string_view<wchar_t>) {
 
  return false;
 
}
 

	
 
// Write the content of buf to os.
 
// It is a separate function rather than a part of vprint to simplify testing.
 
template <typename Char>
 
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
 
  const Char* buf_data = buf.data();
 
  using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
 
  unsigned_streamsize size = buf.size();
 
  unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
 
  do {
 
    unsigned_streamsize n = size <= max_size ? size : max_size;
 
    os.write(buf_data, static_cast<std::streamsize>(n));
 
    buf_data += n;
 
    size -= n;
 
  } while (size != 0);
 
}
 

	
 
template <typename Char, typename T>
 
void format_value(buffer<Char>& buf, const T& value,
 
                  locale_ref loc = locale_ref()) {
 
  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
 
  auto&& output = std::basic_ostream<Char>(&format_buf);
 
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
 
  if (loc) output.imbue(loc.get<std::locale>());
 
#endif
 
  output << value;
 
  output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
 
}
 

	
 
template <typename T> struct streamed_view { const T& value; };
 

	
 
}  // namespace detail
 

	
 
// Formats an object of type T that has an overloaded ostream operator<<.
 
template <typename Char>
 
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
 
  void set_debug_format() = delete;
 

	
 
  template <typename T, typename OutputIt>
 
  auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
 
      -> OutputIt {
 
    auto buffer = basic_memory_buffer<Char>();
 
    detail::format_value(buffer, value, ctx.locale());
 
    return formatter<basic_string_view<Char>, Char>::format(
 
        {buffer.data(), buffer.size()}, ctx);
 
  }
 
};
 

	
 
using ostream_formatter = basic_ostream_formatter<char>;
 

	
 
template <typename T, typename Char>
 
struct formatter<detail::streamed_view<T>, Char>
 
    : basic_ostream_formatter<Char> {
 
  template <typename OutputIt>
 
  auto format(detail::streamed_view<T> view,
 
              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
 
    return basic_ostream_formatter<Char>::format(view.value, ctx);
 
  }
 
};
 

	
 
/**
 
  \rst
 
  Returns a view that formats `value` via an ostream ``operator<<``.
 

	
 
  **Example**::
 

	
 
    fmt::print("Current thread id: {}\n",
 
               fmt::streamed(std::this_thread::get_id()));
 
  \endrst
 
 */
 
template <typename T>
 
auto streamed(const T& value) -> detail::streamed_view<T> {
 
  return {value};
 
}
 

	
 
namespace detail {
 

	
 
inline void vprint_directly(std::ostream& os, string_view format_str,
 
                            format_args args) {
 
  auto buffer = memory_buffer();
 
  detail::vformat_to(buffer, format_str, args);
 
  detail::write_buffer(os, buffer);
 
}
 

	
 
}  // namespace detail
 

	
 
FMT_MODULE_EXPORT template <typename Char>
 
void vprint(std::basic_ostream<Char>& os,
 
            basic_string_view<type_identity_t<Char>> format_str,
 
            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  auto buffer = basic_memory_buffer<Char>();
 
  detail::vformat_to(buffer, format_str, args);
 
  if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
 
  detail::write_buffer(os, buffer);
 
}
 

	
 
/**
 
  \rst
 
  Prints formatted data to the stream *os*.
 

	
 
  **Example**::
 

	
 
    fmt::print(cerr, "Don't {}!", "panic");
 
  \endrst
 
 */
 
FMT_MODULE_EXPORT template <typename... T>
 
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
 
  const auto& vargs = fmt::make_format_args(args...);
 
  if (detail::is_utf8())
 
    vprint(os, fmt, vargs);
 
  else
 
    detail::vprint_directly(os, fmt, vargs);
 
}
 

	
 
FMT_MODULE_EXPORT
 
template <typename... Args>
 
void print(std::wostream& os,
 
           basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
 
           Args&&... args) {
 
  vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
 
}
 

	
 
FMT_MODULE_EXPORT template <typename... T>
 
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
 
  fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
 
}
 

	
 
FMT_MODULE_EXPORT
 
template <typename... Args>
 
void println(std::wostream& os,
 
             basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
 
             Args&&... args) {
 
  print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
 
}
 

	
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_OSTREAM_H_
src/3rdparty/fmt/ranges.h
Show inline comments
 
new file 100644
 
// Formatting library for C++ - experimental range support
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
//
 
// For the license information refer to format.h.
 
//
 
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
 
// All Rights Reserved
 
// {fmt} support for ranges, containers and types tuple interface.
 

	
 
#ifndef FMT_RANGES_H_
 
#define FMT_RANGES_H_
 

	
 
#include <initializer_list>
 
#include <tuple>
 
#include <type_traits>
 

	
 
#include "format.h"
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
namespace detail {
 

	
 
template <typename Range, typename OutputIt>
 
auto copy(const Range& range, OutputIt out) -> OutputIt {
 
  for (auto it = range.begin(), end = range.end(); it != end; ++it)
 
    *out++ = *it;
 
  return out;
 
}
 

	
 
template <typename OutputIt>
 
auto copy(const char* str, OutputIt out) -> OutputIt {
 
  while (*str) *out++ = *str++;
 
  return out;
 
}
 

	
 
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
 
  *out++ = ch;
 
  return out;
 
}
 

	
 
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
 
  *out++ = ch;
 
  return out;
 
}
 

	
 
// Returns true if T has a std::string-like interface, like std::string_view.
 
template <typename T> class is_std_string_like {
 
  template <typename U>
 
  static auto check(U* p)
 
      -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
 
  template <typename> static void check(...);
 

	
 
 public:
 
  static constexpr const bool value =
 
      is_string<T>::value ||
 
      std::is_convertible<T, std_string_view<char>>::value ||
 
      !std::is_void<decltype(check<T>(nullptr))>::value;
 
};
 

	
 
template <typename Char>
 
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
 

	
 
template <typename T> class is_map {
 
  template <typename U> static auto check(U*) -> typename U::mapped_type;
 
  template <typename> static void check(...);
 

	
 
 public:
 
#ifdef FMT_FORMAT_MAP_AS_LIST  // DEPRECATED!
 
  static constexpr const bool value = false;
 
#else
 
  static constexpr const bool value =
 
      !std::is_void<decltype(check<T>(nullptr))>::value;
 
#endif
 
};
 

	
 
template <typename T> class is_set {
 
  template <typename U> static auto check(U*) -> typename U::key_type;
 
  template <typename> static void check(...);
 

	
 
 public:
 
#ifdef FMT_FORMAT_SET_AS_LIST  // DEPRECATED!
 
  static constexpr const bool value = false;
 
#else
 
  static constexpr const bool value =
 
      !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
 
#endif
 
};
 

	
 
template <typename... Ts> struct conditional_helper {};
 

	
 
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
 

	
 
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
 

	
 
#  define FMT_DECLTYPE_RETURN(val)  \
 
    ->decltype(val) { return val; } \
 
    static_assert(                  \
 
        true, "")  // This makes it so that a semicolon is required after the
 
                   // macro, which helps clang-format handle the formatting.
 

	
 
// C array overload
 
template <typename T, std::size_t N>
 
auto range_begin(const T (&arr)[N]) -> const T* {
 
  return arr;
 
}
 
template <typename T, std::size_t N>
 
auto range_end(const T (&arr)[N]) -> const T* {
 
  return arr + N;
 
}
 

	
 
template <typename T, typename Enable = void>
 
struct has_member_fn_begin_end_t : std::false_type {};
 

	
 
template <typename T>
 
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
 
                                           decltype(std::declval<T>().end())>>
 
    : std::true_type {};
 

	
 
// Member function overload
 
template <typename T>
 
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
 
template <typename T>
 
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
 

	
 
// ADL overload. Only participates in overload resolution if member functions
 
// are not found.
 
template <typename T>
 
auto range_begin(T&& rng)
 
    -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
 
                   decltype(begin(static_cast<T&&>(rng)))> {
 
  return begin(static_cast<T&&>(rng));
 
}
 
template <typename T>
 
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
 
                                       decltype(end(static_cast<T&&>(rng)))> {
 
  return end(static_cast<T&&>(rng));
 
}
 

	
 
template <typename T, typename Enable = void>
 
struct has_const_begin_end : std::false_type {};
 
template <typename T, typename Enable = void>
 
struct has_mutable_begin_end : std::false_type {};
 

	
 
template <typename T>
 
struct has_const_begin_end<
 
    T,
 
    void_t<
 
        decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
 
        decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
 
    : std::true_type {};
 

	
 
template <typename T>
 
struct has_mutable_begin_end<
 
    T, void_t<decltype(detail::range_begin(std::declval<T>())),
 
              decltype(detail::range_end(std::declval<T>())),
 
              // the extra int here is because older versions of MSVC don't
 
              // SFINAE properly unless there are distinct types
 
              int>> : std::true_type {};
 

	
 
template <typename T>
 
struct is_range_<T, void>
 
    : std::integral_constant<bool, (has_const_begin_end<T>::value ||
 
                                    has_mutable_begin_end<T>::value)> {};
 
#  undef FMT_DECLTYPE_RETURN
 
#endif
 

	
 
// tuple_size and tuple_element check.
 
template <typename T> class is_tuple_like_ {
 
  template <typename U>
 
  static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
 
  template <typename> static void check(...);
 

	
 
 public:
 
  static constexpr const bool value =
 
      !std::is_void<decltype(check<T>(nullptr))>::value;
 
};
 

	
 
// Check for integer_sequence
 
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
 
template <typename T, T... N>
 
using integer_sequence = std::integer_sequence<T, N...>;
 
template <size_t... N> using index_sequence = std::index_sequence<N...>;
 
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
 
#else
 
template <typename T, T... N> struct integer_sequence {
 
  using value_type = T;
 

	
 
  static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
 
};
 

	
 
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
 

	
 
template <typename T, size_t N, T... Ns>
 
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
 
template <typename T, T... Ns>
 
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
 

	
 
template <size_t N>
 
using make_index_sequence = make_integer_sequence<size_t, N>;
 
#endif
 

	
 
template <typename T>
 
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
 

	
 
template <typename T, typename C, bool = is_tuple_like_<T>::value>
 
class is_tuple_formattable_ {
 
 public:
 
  static constexpr const bool value = false;
 
};
 
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
 
  template <std::size_t... Is>
 
  static std::true_type check2(index_sequence<Is...>,
 
                               integer_sequence<bool, (Is == Is)...>);
 
  static std::false_type check2(...);
 
  template <std::size_t... Is>
 
  static decltype(check2(
 
      index_sequence<Is...>{},
 
      integer_sequence<
 
          bool, (is_formattable<typename std::tuple_element<Is, T>::type,
 
                                C>::value)...>{})) check(index_sequence<Is...>);
 

	
 
 public:
 
  static constexpr const bool value =
 
      decltype(check(tuple_index_sequence<T>{}))::value;
 
};
 

	
 
template <typename Tuple, typename F, size_t... Is>
 
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
 
  using std::get;
 
  // Using a free function get<Is>(Tuple) now.
 
  const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
 
  ignore_unused(unused);
 
}
 

	
 
template <typename Tuple, typename F>
 
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
 
  for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
 
           std::forward<Tuple>(t), std::forward<F>(f));
 
}
 

	
 
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
 
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
 
  using std::get;
 
  const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
 
  ignore_unused(unused);
 
}
 

	
 
template <typename Tuple1, typename Tuple2, typename F>
 
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
 
  for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
 
            std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
 
            std::forward<F>(f));
 
}
 

	
 
namespace tuple {
 
// Workaround a bug in MSVC 2019 (v140).
 
template <typename Char, typename... T>
 
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
 

	
 
using std::get;
 
template <typename Tuple, typename Char, std::size_t... Is>
 
auto get_formatters(index_sequence<Is...>)
 
    -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
 
}  // namespace tuple
 

	
 
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
 
// Older MSVC doesn't get the reference type correctly for arrays.
 
template <typename R> struct range_reference_type_impl {
 
  using type = decltype(*detail::range_begin(std::declval<R&>()));
 
};
 

	
 
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
 
  using type = T&;
 
};
 

	
 
template <typename T>
 
using range_reference_type = typename range_reference_type_impl<T>::type;
 
#else
 
template <typename Range>
 
using range_reference_type =
 
    decltype(*detail::range_begin(std::declval<Range&>()));
 
#endif
 

	
 
// We don't use the Range's value_type for anything, but we do need the Range's
 
// reference type, with cv-ref stripped.
 
template <typename Range>
 
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
 

	
 
template <typename Formatter>
 
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
 
    -> decltype(f.set_debug_format(set)) {
 
  f.set_debug_format(set);
 
}
 
template <typename Formatter>
 
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
 

	
 
// These are not generic lambdas for compatibility with C++11.
 
template <typename ParseContext> struct parse_empty_specs {
 
  template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
 
    f.parse(ctx);
 
    detail::maybe_set_debug_format(f, true);
 
  }
 
  ParseContext& ctx;
 
};
 
template <typename FormatContext> struct format_tuple_element {
 
  using char_type = typename FormatContext::char_type;
 

	
 
  template <typename T>
 
  void operator()(const formatter<T, char_type>& f, const T& v) {
 
    if (i > 0)
 
      ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
 
    ctx.advance_to(f.format(v, ctx));
 
    ++i;
 
  }
 

	
 
  int i;
 
  FormatContext& ctx;
 
  basic_string_view<char_type> separator;
 
};
 

	
 
}  // namespace detail
 

	
 
template <typename T> struct is_tuple_like {
 
  static constexpr const bool value =
 
      detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
 
};
 

	
 
template <typename T, typename C> struct is_tuple_formattable {
 
  static constexpr const bool value =
 
      detail::is_tuple_formattable_<T, C>::value;
 
};
 

	
 
template <typename Tuple, typename Char>
 
struct formatter<Tuple, Char,
 
                 enable_if_t<fmt::is_tuple_like<Tuple>::value &&
 
                             fmt::is_tuple_formattable<Tuple, Char>::value>> {
 
 private:
 
  decltype(detail::tuple::get_formatters<Tuple, Char>(
 
      detail::tuple_index_sequence<Tuple>())) formatters_;
 

	
 
  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
 
  basic_string_view<Char> opening_bracket_ =
 
      detail::string_literal<Char, '('>{};
 
  basic_string_view<Char> closing_bracket_ =
 
      detail::string_literal<Char, ')'>{};
 

	
 
 public:
 
  FMT_CONSTEXPR formatter() {}
 

	
 
  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
 
    separator_ = sep;
 
  }
 

	
 
  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
 
                                  basic_string_view<Char> close) {
 
    opening_bracket_ = open;
 
    closing_bracket_ = close;
 
  }
 

	
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    auto it = ctx.begin();
 
    if (it != ctx.end() && *it != '}')
 
      FMT_THROW(format_error("invalid format specifier"));
 
    detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
 
    return it;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const Tuple& value, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
 
    detail::for_each2(
 
        formatters_, value,
 
        detail::format_tuple_element<FormatContext>{0, ctx, separator_});
 
    return detail::copy_str<Char>(closing_bracket_, ctx.out());
 
  }
 
};
 

	
 
template <typename T, typename Char> struct is_range {
 
  static constexpr const bool value =
 
      detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
 
      !std::is_convertible<T, std::basic_string<Char>>::value &&
 
      !std::is_convertible<T, detail::std_string_view<Char>>::value;
 
};
 

	
 
namespace detail {
 
template <typename Context> struct range_mapper {
 
  using mapper = arg_mapper<Context>;
 

	
 
  template <typename T,
 
            FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
 
  static auto map(T&& value) -> T&& {
 
    return static_cast<T&&>(value);
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
 
  static auto map(T&& value)
 
      -> decltype(mapper().map(static_cast<T&&>(value))) {
 
    return mapper().map(static_cast<T&&>(value));
 
  }
 
};
 

	
 
template <typename Char, typename Element>
 
using range_formatter_type =
 
    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
 
                  std::declval<Element>()))>,
 
              Char>;
 

	
 
template <typename R>
 
using maybe_const_range =
 
    conditional_t<has_const_begin_end<R>::value, const R, R>;
 

	
 
// Workaround a bug in MSVC 2015 and earlier.
 
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
 
template <typename R, typename Char>
 
struct is_formattable_delayed
 
    : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
 
#endif
 
}  // namespace detail
 

	
 
template <typename T, typename Char, typename Enable = void>
 
struct range_formatter;
 

	
 
template <typename T, typename Char>
 
struct range_formatter<
 
    T, Char,
 
    enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
 
                            is_formattable<T, Char>>::value>> {
 
 private:
 
  detail::range_formatter_type<Char, T> underlying_;
 
  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
 
  basic_string_view<Char> opening_bracket_ =
 
      detail::string_literal<Char, '['>{};
 
  basic_string_view<Char> closing_bracket_ =
 
      detail::string_literal<Char, ']'>{};
 

	
 
 public:
 
  FMT_CONSTEXPR range_formatter() {}
 

	
 
  FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
 
    return underlying_;
 
  }
 

	
 
  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
 
    separator_ = sep;
 
  }
 

	
 
  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
 
                                  basic_string_view<Char> close) {
 
    opening_bracket_ = open;
 
    closing_bracket_ = close;
 
  }
 

	
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    auto it = ctx.begin();
 
    auto end = ctx.end();
 

	
 
    if (it != end && *it == 'n') {
 
      set_brackets({}, {});
 
      ++it;
 
    }
 

	
 
    if (it != end && *it != '}') {
 
      if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
 
      ++it;
 
    } else {
 
      detail::maybe_set_debug_format(underlying_, true);
 
    }
 

	
 
    ctx.advance_to(it);
 
    return underlying_.parse(ctx);
 
  }
 

	
 
  template <typename R, typename FormatContext>
 
  auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    detail::range_mapper<buffer_context<Char>> mapper;
 
    auto out = ctx.out();
 
    out = detail::copy_str<Char>(opening_bracket_, out);
 
    int i = 0;
 
    auto it = detail::range_begin(range);
 
    auto end = detail::range_end(range);
 
    for (; it != end; ++it) {
 
      if (i > 0) out = detail::copy_str<Char>(separator_, out);
 
      ctx.advance_to(out);
 
      out = underlying_.format(mapper.map(*it), ctx);
 
      ++i;
 
    }
 
    out = detail::copy_str<Char>(closing_bracket_, out);
 
    return out;
 
  }
 
};
 

	
 
enum class range_format { disabled, map, set, sequence, string, debug_string };
 

	
 
namespace detail {
 
template <typename T>
 
struct range_format_kind_
 
    : std::integral_constant<range_format,
 
                             std::is_same<uncvref_type<T>, T>::value
 
                                 ? range_format::disabled
 
                             : is_map<T>::value ? range_format::map
 
                             : is_set<T>::value ? range_format::set
 
                                                : range_format::sequence> {};
 

	
 
template <range_format K, typename R, typename Char, typename Enable = void>
 
struct range_default_formatter;
 

	
 
template <range_format K>
 
using range_format_constant = std::integral_constant<range_format, K>;
 

	
 
template <range_format K, typename R, typename Char>
 
struct range_default_formatter<
 
    K, R, Char,
 
    enable_if_t<(K == range_format::sequence || K == range_format::map ||
 
                 K == range_format::set)>> {
 
  using range_type = detail::maybe_const_range<R>;
 
  range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
 

	
 
  FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
 

	
 
  FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
 
    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
 
                             detail::string_literal<Char, '}'>{});
 
  }
 

	
 
  FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
 
    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
 
                             detail::string_literal<Char, '}'>{});
 
    underlying_.underlying().set_brackets({}, {});
 
    underlying_.underlying().set_separator(
 
        detail::string_literal<Char, ':', ' '>{});
 
  }
 

	
 
  FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
 

	
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    return underlying_.parse(ctx);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(range_type& range, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    return underlying_.format(range, ctx);
 
  }
 
};
 
}  // namespace detail
 

	
 
template <typename T, typename Char, typename Enable = void>
 
struct range_format_kind
 
    : conditional_t<
 
          is_range<T, Char>::value, detail::range_format_kind_<T>,
 
          std::integral_constant<range_format, range_format::disabled>> {};
 

	
 
template <typename R, typename Char>
 
struct formatter<
 
    R, Char,
 
    enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
 
                                          range_format::disabled>
 
// Workaround a bug in MSVC 2015 and earlier.
 
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
 
                            ,
 
                            detail::is_formattable_delayed<R, Char>
 
#endif
 
                            >::value>>
 
    : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
 
                                      Char> {
 
};
 

	
 
template <typename Char, typename... T> struct tuple_join_view : detail::view {
 
  const std::tuple<T...>& tuple;
 
  basic_string_view<Char> sep;
 

	
 
  tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
 
      : tuple(t), sep{s} {}
 
};
 

	
 
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
 
// support in tuple_join. It is disabled by default because of issues with
 
// the dynamic width and precision.
 
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
 
#  define FMT_TUPLE_JOIN_SPECIFIERS 0
 
#endif
 

	
 
template <typename Char, typename... T>
 
struct formatter<tuple_join_view<Char, T...>, Char> {
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const tuple_join_view<Char, T...>& value,
 
              FormatContext& ctx) const -> typename FormatContext::iterator {
 
    return do_format(value, ctx,
 
                     std::integral_constant<size_t, sizeof...(T)>());
 
  }
 

	
 
 private:
 
  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
 

	
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
 
                              std::integral_constant<size_t, 0>)
 
      -> decltype(ctx.begin()) {
 
    return ctx.begin();
 
  }
 

	
 
  template <typename ParseContext, size_t N>
 
  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
 
                              std::integral_constant<size_t, N>)
 
      -> decltype(ctx.begin()) {
 
    auto end = ctx.begin();
 
#if FMT_TUPLE_JOIN_SPECIFIERS
 
    end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
 
    if (N > 1) {
 
      auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
 
      if (end != end1)
 
        FMT_THROW(format_error("incompatible format specs for tuple elements"));
 
    }
 
#endif
 
    return end;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
 
                 std::integral_constant<size_t, 0>) const ->
 
      typename FormatContext::iterator {
 
    return ctx.out();
 
  }
 

	
 
  template <typename FormatContext, size_t N>
 
  auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
 
                 std::integral_constant<size_t, N>) const ->
 
      typename FormatContext::iterator {
 
    auto out = std::get<sizeof...(T) - N>(formatters_)
 
                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
 
    if (N > 1) {
 
      out = std::copy(value.sep.begin(), value.sep.end(), out);
 
      ctx.advance_to(out);
 
      return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
 
    }
 
    return out;
 
  }
 
};
 

	
 
namespace detail {
 
// Check if T has an interface like a container adaptor (e.g. std::stack,
 
// std::queue, std::priority_queue).
 
template <typename T> class is_container_adaptor_like {
 
  template <typename U> static auto check(U* p) -> typename U::container_type;
 
  template <typename> static void check(...);
 

	
 
 public:
 
  static constexpr const bool value =
 
      !std::is_void<decltype(check<T>(nullptr))>::value;
 
};
 

	
 
template <typename Container> struct all {
 
  const Container& c;
 
  auto begin() const -> typename Container::const_iterator { return c.begin(); }
 
  auto end() const -> typename Container::const_iterator { return c.end(); }
 
};
 
}  // namespace detail
 

	
 
template <typename T, typename Char>
 
struct formatter<T, Char,
 
                 enable_if_t<detail::is_container_adaptor_like<T>::value>>
 
    : formatter<detail::all<typename T::container_type>, Char> {
 
  using all = detail::all<typename T::container_type>;
 
  template <typename FormatContext>
 
  auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    struct getter : T {
 
      static auto get(const T& t) -> all {
 
        return {t.*(&getter::c)};  // Access c through the derived class.
 
      }
 
    };
 
    return formatter<all>::format(getter::get(t), ctx);
 
  }
 
};
 

	
 
FMT_BEGIN_EXPORT
 

	
 
/**
 
  \rst
 
  Returns an object that formats `tuple` with elements separated by `sep`.
 

	
 
  **Example**::
 

	
 
    std::tuple<int, char> t = {1, 'a'};
 
    fmt::print("{}", fmt::join(t, ", "));
 
    // Output: "1, a"
 
  \endrst
 
 */
 
template <typename... T>
 
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
 
    -> tuple_join_view<char, T...> {
 
  return {tuple, sep};
 
}
 

	
 
template <typename... T>
 
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
 
                        basic_string_view<wchar_t> sep)
 
    -> tuple_join_view<wchar_t, T...> {
 
  return {tuple, sep};
 
}
 

	
 
/**
 
  \rst
 
  Returns an object that formats `initializer_list` with elements separated by
 
  `sep`.
 

	
 
  **Example**::
 

	
 
    fmt::print("{}", fmt::join({1, 2, 3}, ", "));
 
    // Output: "1, 2, 3"
 
  \endrst
 
 */
 
template <typename T>
 
auto join(std::initializer_list<T> list, string_view sep)
 
    -> join_view<const T*, const T*> {
 
  return join(std::begin(list), std::end(list), sep);
 
}
 

	
 
FMT_END_EXPORT
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_RANGES_H_
src/3rdparty/fmt/std.h
Show inline comments
 
new file 100644
 
// Formatting library for C++ - formatters for standard library types
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
//
 
// For the license information refer to format.h.
 

	
 
#ifndef FMT_STD_H_
 
#define FMT_STD_H_
 

	
 
#include <cstdlib>
 
#include <exception>
 
#include <memory>
 
#include <thread>
 
#include <type_traits>
 
#include <typeinfo>
 
#include <utility>
 

	
 
#include "ostream.h"
 

	
 
#if FMT_HAS_INCLUDE(<version>)
 
#  include <version>
 
#endif
 
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
 
#if FMT_CPLUSPLUS >= 201703L
 
#  if FMT_HAS_INCLUDE(<filesystem>)
 
#    include <filesystem>
 
#  endif
 
#  if FMT_HAS_INCLUDE(<variant>)
 
#    include <variant>
 
#  endif
 
#  if FMT_HAS_INCLUDE(<optional>)
 
#    include <optional>
 
#  endif
 
#endif
 

	
 
// GCC 4 does not support FMT_HAS_INCLUDE.
 
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
 
#  include <cxxabi.h>
 
// Android NDK with gabi++ library on some architectures does not implement
 
// abi::__cxa_demangle().
 
#  ifndef __GABIXX_CXXABI_H__
 
#    define FMT_HAS_ABI_CXA_DEMANGLE
 
#  endif
 
#endif
 

	
 
#ifdef __cpp_lib_filesystem
 
FMT_BEGIN_NAMESPACE
 

	
 
namespace detail {
 

	
 
template <typename Char>
 
void write_escaped_path(basic_memory_buffer<Char>& quoted,
 
                        const std::filesystem::path& p) {
 
  write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
 
}
 
#  ifdef _WIN32
 
template <>
 
inline void write_escaped_path<char>(memory_buffer& quoted,
 
                                     const std::filesystem::path& p) {
 
  auto buf = basic_memory_buffer<wchar_t>();
 
  write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
 
  // Convert UTF-16 to UTF-8.
 
  if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
 
    FMT_THROW(std::runtime_error("invalid utf16"));
 
}
 
#  endif
 
template <>
 
inline void write_escaped_path<std::filesystem::path::value_type>(
 
    basic_memory_buffer<std::filesystem::path::value_type>& quoted,
 
    const std::filesystem::path& p) {
 
  write_escaped_string<std::filesystem::path::value_type>(
 
      std::back_inserter(quoted), p.native());
 
}
 

	
 
}  // namespace detail
 

	
 
FMT_MODULE_EXPORT
 
template <typename Char>
 
struct formatter<std::filesystem::path, Char>
 
    : formatter<basic_string_view<Char>> {
 
  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
 
    auto out = formatter<basic_string_view<Char>>::parse(ctx);
 
    this->set_debug_format(false);
 
    return out;
 
  }
 
  template <typename FormatContext>
 
  auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
 
      typename FormatContext::iterator {
 
    auto quoted = basic_memory_buffer<Char>();
 
    detail::write_escaped_path(quoted, p);
 
    return formatter<basic_string_view<Char>>::format(
 
        basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
 
  }
 
};
 
FMT_END_NAMESPACE
 
#endif
 

	
 
FMT_BEGIN_NAMESPACE
 
FMT_MODULE_EXPORT
 
template <typename Char>
 
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
 
FMT_END_NAMESPACE
 

	
 
#ifdef __cpp_lib_optional
 
FMT_BEGIN_NAMESPACE
 
FMT_MODULE_EXPORT
 
template <typename T, typename Char>
 
struct formatter<std::optional<T>, Char,
 
                 std::enable_if_t<is_formattable<T, Char>::value>> {
 
 private:
 
  formatter<T, Char> underlying_;
 
  static constexpr basic_string_view<Char> optional =
 
      detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
 
                             '('>{};
 
  static constexpr basic_string_view<Char> none =
 
      detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
 

	
 
  template <class U>
 
  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
 
      -> decltype(u.set_debug_format(set)) {
 
    u.set_debug_format(set);
 
  }
 

	
 
  template <class U>
 
  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
 

	
 
 public:
 
  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
 
    maybe_set_debug_format(underlying_, true);
 
    return underlying_.parse(ctx);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(std::optional<T> const& opt, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    if (!opt) return detail::write<Char>(ctx.out(), none);
 

	
 
    auto out = ctx.out();
 
    out = detail::write<Char>(out, optional);
 
    ctx.advance_to(out);
 
    out = underlying_.format(*opt, ctx);
 
    return detail::write(out, ')');
 
  }
 
};
 
FMT_END_NAMESPACE
 
#endif  // __cpp_lib_optional
 

	
 
#ifdef __cpp_lib_variant
 
FMT_BEGIN_NAMESPACE
 
FMT_MODULE_EXPORT
 
template <typename Char> struct formatter<std::monostate, Char> {
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    return ctx.begin();
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const std::monostate&, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    auto out = ctx.out();
 
    out = detail::write<Char>(out, "monostate");
 
    return out;
 
  }
 
};
 

	
 
namespace detail {
 

	
 
template <typename T>
 
using variant_index_sequence =
 
    std::make_index_sequence<std::variant_size<T>::value>;
 

	
 
template <typename> struct is_variant_like_ : std::false_type {};
 
template <typename... Types>
 
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
 

	
 
// formattable element check.
 
template <typename T, typename C> class is_variant_formattable_ {
 
  template <std::size_t... Is>
 
  static std::conjunction<
 
      is_formattable<std::variant_alternative_t<Is, T>, C>...>
 
      check(std::index_sequence<Is...>);
 

	
 
 public:
 
  static constexpr const bool value =
 
      decltype(check(variant_index_sequence<T>{}))::value;
 
};
 

	
 
template <typename Char, typename OutputIt, typename T>
 
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
 
  if constexpr (is_string<T>::value)
 
    return write_escaped_string<Char>(out, detail::to_string_view(v));
 
  else if constexpr (std::is_same_v<T, Char>)
 
    return write_escaped_char(out, v);
 
  else
 
    return write<Char>(out, v);
 
}
 

	
 
}  // namespace detail
 
template <typename T> struct is_variant_like {
 
  static constexpr const bool value = detail::is_variant_like_<T>::value;
 
};
 

	
 
template <typename T, typename C> struct is_variant_formattable {
 
  static constexpr const bool value =
 
      detail::is_variant_formattable_<T, C>::value;
 
};
 

	
 
FMT_MODULE_EXPORT
 
template <typename Variant, typename Char>
 
struct formatter<
 
    Variant, Char,
 
    std::enable_if_t<std::conjunction_v<
 
        is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    return ctx.begin();
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const Variant& value, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    auto out = ctx.out();
 

	
 
    out = detail::write<Char>(out, "variant(");
 
    try {
 
      std::visit(
 
          [&](const auto& v) {
 
            out = detail::write_variant_alternative<Char>(out, v);
 
          },
 
          value);
 
    } catch (const std::bad_variant_access&) {
 
      detail::write<Char>(out, "valueless by exception");
 
    }
 
    *out++ = ')';
 
    return out;
 
  }
 
};
 
FMT_END_NAMESPACE
 
#endif  // __cpp_lib_variant
 

	
 
FMT_BEGIN_NAMESPACE
 
FMT_MODULE_EXPORT
 
template <typename Char> struct formatter<std::error_code, Char> {
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
 
    return ctx.begin();
 
  }
 

	
 
  template <typename FormatContext>
 
  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    auto out = ctx.out();
 
    out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
 
    out = detail::write<Char>(out, Char(':'));
 
    out = detail::write<Char>(out, ec.value());
 
    return out;
 
  }
 
};
 

	
 
FMT_MODULE_EXPORT
 
template <typename T, typename Char>
 
struct formatter<
 
    T, Char,
 
    typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
 
 private:
 
  bool with_typename_ = false;
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto it = ctx.begin();
 
    auto end = ctx.end();
 
    if (it == end || *it == '}') return it;
 
    if (*it == 't') {
 
      ++it;
 
      with_typename_ = true;
 
    }
 
    return it;
 
  }
 

	
 
  template <typename OutputIt>
 
  auto format(const std::exception& ex,
 
              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
 
    format_specs<Char> spec;
 
    auto out = ctx.out();
 
    if (!with_typename_)
 
      return detail::write_bytes(out, string_view(ex.what()), spec);
 

	
 
    const std::type_info& ti = typeid(ex);
 
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
 
    int status = 0;
 
    std::size_t size = 0;
 
    std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
 
        abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
 

	
 
    string_view demangled_name_view;
 
    if (demangled_name_ptr) {
 
      demangled_name_view = demangled_name_ptr.get();
 

	
 
      // Normalization of stdlib inline namespace names.
 
      // libc++ inline namespaces.
 
      //  std::__1::*       -> std::*
 
      //  std::__1::__fs::* -> std::*
 
      // libstdc++ inline namespaces.
 
      //  std::__cxx11::*             -> std::*
 
      //  std::filesystem::__cxx11::* -> std::filesystem::*
 
      if (demangled_name_view.starts_with("std::")) {
 
        char* begin = demangled_name_ptr.get();
 
        char* to = begin + 5;  // std::
 
        for (char *from = to, *end = begin + demangled_name_view.size();
 
             from < end;) {
 
          // This is safe, because demangled_name is NUL-terminated.
 
          if (from[0] == '_' && from[1] == '_') {
 
            char* next = from + 1;
 
            while (next < end && *next != ':') next++;
 
            if (next[0] == ':' && next[1] == ':') {
 
              from = next + 2;
 
              continue;
 
            }
 
          }
 
          *to++ = *from++;
 
        }
 
        demangled_name_view = {begin, detail::to_unsigned(to - begin)};
 
      }
 
    } else {
 
      demangled_name_view = string_view(ti.name());
 
    }
 
    out = detail::write_bytes(out, demangled_name_view, spec);
 
#elif FMT_MSC_VERSION
 
    string_view demangled_name_view(ti.name());
 
    if (demangled_name_view.starts_with("class "))
 
      demangled_name_view.remove_prefix(6);
 
    else if (demangled_name_view.starts_with("struct "))
 
      demangled_name_view.remove_prefix(7);
 
    out = detail::write_bytes(out, demangled_name_view, spec);
 
#else
 
    out = detail::write_bytes(out, string_view(ti.name()), spec);
 
#endif
 
    out = detail::write<Char>(out, Char(':'));
 
    out = detail::write<Char>(out, Char(' '));
 
    out = detail::write_bytes(out, string_view(ex.what()), spec);
 

	
 
    return out;
 
  }
 
};
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_STD_H_
0 comments (0 inline, 0 general)