35#include <fmt/compile.h> 
   36#include <fmt/format.h> 
   38#include <userver/decimal64/format_options.hpp> 
   39#include <userver/formats/common/meta.hpp> 
   40#include <userver/utils/assert.hpp> 
   41#include <userver/utils/flags.hpp> 
   42#include <userver/utils/meta_light.hpp> 
   44USERVER_NAMESPACE_BEGIN
 
   58  using std::runtime_error::runtime_error;
 
   76  DivisionByZeroError();
 
   81inline constexpr auto kMaxInt64 = std::numeric_limits<int64_t>::max();
 
   82inline constexpr auto kMinInt64 = std::numeric_limits<int64_t>::min();
 
   86inline constexpr auto kMinRepresentableLongDouble =
 
   87    static_cast<
long double>(impl::kMinInt64) *
 
   88    (1 - 2 * std::numeric_limits<
long double>::epsilon());
 
   89inline constexpr auto kMaxRepresentableLongDouble =
 
   90    static_cast<
long double>(impl::kMaxInt64) *
 
   91    (1 - 2 * std::numeric_limits<
long double>::epsilon());
 
   94using EnableIfInt = std::enable_if_t<meta::kIsInteger<T>, 
int>;
 
   97using EnableIfFloat = std::enable_if_t<std::is_floating_point_v<T>, 
int>;
 
  100constexpr std::array<int64_t, MaxExp + 1> PowSeries(int64_t base) {
 
  102  std::array<int64_t, MaxExp + 1> result{};
 
  103  for (
int i = 0; i < MaxExp; ++i) {
 
  105    if (pow > kMaxInt64 / base) {
 
  110  result[MaxExp] = pow;
 
  114inline constexpr int kMaxDecimalDigits = 18;
 
  115inline constexpr auto kPowSeries10 = PowSeries<kMaxDecimalDigits>(10);
 
  118static_assert(kMaxInt64 / 10 < kPowSeries10[kMaxDecimalDigits]);
 
  120template <
typename RoundPolicy>
 
  121constexpr int64_t Div(int64_t nominator, int64_t denominator,
 
  122                      bool extra_odd_quotient = 
false) {
 
  125  if (denominator == -1) {
 
  130  return RoundPolicy::DivRounded(nominator, denominator, extra_odd_quotient);
 
  134template <
typename RoundPolicy>
 
  135constexpr int64_t MulDiv(int64_t value1, int64_t value2, int64_t divisor) {
 
  138#if __x86_64__ 
|| __ppc64__ || __aarch64__ 
  139  using LongInt = __int128_t;
 
  140  static_assert(
sizeof(
void*) == 8);
 
  142  using LongInt = int64_t;
 
  143  static_assert(
sizeof(
void*) == 4);
 
  147  if constexpr (
sizeof(
void*) == 4) {
 
  148    if (__builtin_mul_overflow(
static_cast<LongInt>(value1),
 
  149                               static_cast<LongInt>(value2), &prod)) {
 
  153    prod = 
static_cast<LongInt>(value1) * value2;
 
  155  const auto whole = prod / divisor;
 
  156  const auto rem = 
static_cast<int64_t>(prod % divisor);
 
  160  const auto whole64 = 
static_cast<int64_t>(whole);
 
  161  const bool extra_odd_quotient = whole64 % 2 != 0;
 
  162  const int64_t rem_divided =
 
  163      Div<RoundPolicy>(rem, divisor, extra_odd_quotient);
 
  164  UASSERT(rem_divided == -1 || rem_divided == 0 || rem_divided == 1);
 
  166  return whole64 + rem_divided;
 
  169constexpr int Sign(int64_t value) { 
return (value > 0) - (value < 0); }
 
  172constexpr int64_t Abs(int64_t value) {
 
  174  return value >= 0 ? value : -value;
 
  180constexpr int64_t Abs(T value) {
 
  181  static_assert(std::is_floating_point_v<T>);
 
  182  return value >= 0 ? value : -value;
 
  188constexpr int64_t Floor(T value) {
 
  189  if (
static_cast<int64_t>(value) <= value) {  
 
  190    return static_cast<int64_t>(value);
 
  192    return static_cast<int64_t>(value) - 1;
 
  199constexpr int64_t Ceil(T value) {
 
  200  if (
static_cast<int64_t>(value) >= value) {  
 
  201    return static_cast<int64_t>(value);
 
  203    return static_cast<int64_t>(value) + 1;
 
  207template <
typename Int>
 
  208constexpr int64_t ToInt64(Int value) {
 
  209  static_assert(meta::kIsInteger<Int>);
 
  210  static_assert(
sizeof(Int) <= 
sizeof(int64_t));
 
  212  if constexpr (
sizeof(Int) == 
sizeof(int64_t)) {
 
  215  return static_cast<int64_t>(value);
 
  218class HalfUpPolicy 
final {
 
  221  template <
typename T>
 
  222  [[nodiscard]] 
static constexpr bool ShouldRoundAwayFromZero(T abs) {
 
  223    const T abs_remainder = abs - 
static_cast<int64_t>(abs);
 
  224    return abs_remainder >= 0.5;
 
  228  static constexpr bool ShouldRoundAwayFromZeroDiv(
 
  229      int64_t a, int64_t b, 
bool ) {
 
  230    const int64_t abs_a = impl::Abs(a);
 
  231    const int64_t abs_b = impl::Abs(b);
 
  232    const int64_t half_b = abs_b / 2;
 
  233    const int64_t abs_remainder = abs_a % abs_b;
 
  234    return abs_b % 2 == 0 ? abs_remainder >= half_b : abs_remainder > half_b;
 
  238class HalfDownPolicy 
final {
 
  241  template <
typename T>
 
  242  [[nodiscard]] 
static constexpr bool ShouldRoundAwayFromZero(T abs) {
 
  243    const T abs_remainder = abs - 
static_cast<int64_t>(abs);
 
  244    return abs_remainder > 0.5;
 
  248  static constexpr bool ShouldRoundAwayFromZeroDiv(
 
  249      int64_t a, int64_t b, 
bool ) {
 
  250    const int64_t abs_a = impl::Abs(a);
 
  251    const int64_t abs_b = impl::Abs(b);
 
  252    const int64_t half_b = abs_b / 2;
 
  253    const int64_t abs_remainder = abs_a % abs_b;
 
  254    return abs_remainder > half_b;
 
  258class HalfEvenPolicy 
final {
 
  261  template <
typename T>
 
  262  [[nodiscard]] 
static constexpr bool ShouldRoundAwayFromZero(T abs) {
 
  263    const T abs_remainder = abs - 
static_cast<int64_t>(abs);
 
  264    return abs_remainder == 0.5 ? impl::Floor(abs) % 2 != 0
 
  265                                : abs_remainder > 0.5;
 
  269  static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
 
  270                                                   bool extra_odd_quotient) {
 
  271    const int64_t abs_a = impl::Abs(a);
 
  272    const int64_t abs_b = impl::Abs(b);
 
  273    const int64_t half_b = abs_b / 2;
 
  274    const int64_t abs_remainder = abs_a % abs_b;
 
  275    return (abs_b % 2 == 0 && abs_remainder == half_b)
 
  276               ? ((abs_a / abs_b) % 2 == 0) == extra_odd_quotient
 
  277               : abs_remainder > half_b;
 
  281template <
typename HalfPolicy>
 
  282class HalfRoundPolicyBase {
 
  284  template <
typename T>
 
  285  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  286    if ((value >= 0.0) == HalfPolicy::ShouldRoundAwayFromZero(value)) {
 
  287      return impl::Ceil(value);
 
  289      return impl::Floor(value);
 
  293  [[nodiscard]] 
static constexpr int64_t DivRounded(int64_t a, int64_t b,
 
  294                                                    bool extra_odd_quotient) {
 
  295    if (HalfPolicy::ShouldRoundAwayFromZeroDiv(a, b, extra_odd_quotient)) {
 
  296      const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
 
  297      return (a / b) + quotient_sign;  
 
  308  if (exp < 0 || exp > impl::kMaxDecimalDigits) {
 
  309    throw std::runtime_error(
"Pow10: invalid power of 10");
 
  311  return impl::kPowSeries10[
static_cast<size_t>(exp)];
 
  322class DefRoundPolicy 
final {
 
  324  template <
typename T>
 
  325  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  326    return static_cast<int64_t>(value + (value < 0 ? -0.5 : 0.5));
 
  329  [[nodiscard]] 
static constexpr int64_t DivRounded(
 
  330      int64_t a, int64_t b, 
bool ) {
 
  331    const int64_t divisor_corr = impl::Abs(b / 2);
 
  334      return (a + divisor_corr) / b;
 
  337      return (a - divisor_corr) / b;
 
  343class HalfDownRoundPolicy 
final 
  344    : 
public impl::HalfRoundPolicyBase<impl::HalfDownPolicy> {};
 
  347class HalfUpRoundPolicy 
final 
  348    : 
public impl::HalfRoundPolicyBase<impl::HalfUpPolicy> {};
 
  351class HalfEvenRoundPolicy 
final 
  352    : 
public impl::HalfRoundPolicyBase<impl::HalfEvenPolicy> {};
 
  357  template <
typename T>
 
  358  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  359    return impl::Ceil(value);
 
  362  [[
nodiscard]] 
static constexpr int64_t DivRounded(
 
  363      int64_t a, int64_t b, 
bool ) {
 
  364    const bool quotient_positive = (a >= 0) == (b >= 0);
 
  365    return (a / b) + (a % b != 0 && quotient_positive);
 
  370class FloorRoundPolicy 
final {
 
  372  template <
typename T>
 
  373  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  374    return impl::Floor(value);
 
  377  [[nodiscard]] 
static constexpr int64_t DivRounded(
 
  378      int64_t a, int64_t b, 
bool ) {
 
  379    const bool quotient_negative = (a < 0) != (b < 0);
 
  380    return (a / b) - (a % b != 0 && quotient_negative);
 
  385class RoundDownRoundPolicy 
final {
 
  387  template <
typename T>
 
  388  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  389    return static_cast<int64_t>(value);
 
  392  [[nodiscard]] 
static constexpr int64_t DivRounded(
 
  393      int64_t a, int64_t b, 
bool ) {
 
  399class RoundUpRoundPolicy 
final {
 
  401  template <
typename T>
 
  402  [[nodiscard]] 
static constexpr int64_t Round(T value) {
 
  404      return impl::Ceil(value);
 
  406      return impl::Floor(value);
 
  410  [[nodiscard]] 
static constexpr int64_t DivRounded(
 
  411      int64_t a, int64_t b, 
bool ) {
 
  412    const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
 
  413    return (a / b) + (a % b != 0) * quotient_sign;
 
  454template <
int Prec, 
typename RoundPolicy_ = DefRoundPolicy>
 
  461  using RoundPolicy = RoundPolicy_;
 
  470  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  486  explicit constexpr Decimal(std::string_view value);
 
  495  template <
typename T>
 
  497    static_assert(std::is_floating_point_v<T>);
 
  500    static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
 
  501    static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
 
  503    const auto unbiased_float =
 
  505    if (unbiased_float < impl::kMinRepresentableLongDouble ||
 
  506        unbiased_float > impl::kMaxRepresentableLongDouble) {
 
  531    result.value_ = value;
 
  547                                      int original_precision) {
 
  548    const int exponent_for_pack = Prec - original_precision;
 
  550    if (exponent_for_pack >= 0) {
 
  551      return FromUnbiased(original_unbiased) * Pow10(exponent_for_pack);
 
  554          impl::Div<RoundPolicy>(original_unbiased, Pow10(-exponent_for_pack)));
 
  564    *
this = FromDecimal(rhs);
 
  569  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  575  constexpr bool operator==(
Decimal rhs) 
const { 
return value_ == rhs.value_; }
 
  577  constexpr bool operator!=(
Decimal rhs) 
const { 
return value_ != rhs.value_; }
 
  579  constexpr bool operator<(
Decimal rhs) 
const { 
return value_ < rhs.value_; }
 
  581  constexpr bool operator<=(
Decimal rhs) 
const { 
return value_ <= rhs.value_; }
 
  583  constexpr bool operator>(
Decimal rhs) 
const { 
return value_ > rhs.value_; }
 
  585  constexpr bool operator>=(
Decimal rhs) 
const { 
return value_ >= rhs.value_; }
 
  587  constexpr Decimal operator+() 
const { 
return *
this; }
 
  589  constexpr Decimal operator-() 
const {
 
  595  constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs) 
const {
 
  596    if constexpr (Prec2 > Prec) {
 
  597      return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
 
  598    } 
else if constexpr (Prec2 < Prec) {
 
  599      return *
this + FromDecimal(rhs);
 
  602      if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
 
  609  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  610  constexpr Decimal operator+(Int rhs) 
const {
 
  614  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  621    static_assert(Prec2 <= Prec,
 
  622                  "Implicit cast to Decimal of lower precision in assignment");
 
  627  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  628  constexpr Decimal& operator+=(Int rhs) {
 
  634  constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs) 
const {
 
  635    if constexpr (Prec2 > Prec) {
 
  636      return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
 
  637    } 
else if constexpr (Prec2 < Prec) {
 
  638      return *
this - FromDecimal(rhs);
 
  641      if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
 
  648  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  649  constexpr Decimal operator-(Int rhs) 
const {
 
  653  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  660    static_assert(Prec2 <= Prec,
 
  661                  "Implicit cast to Decimal of lower precision in assignment");
 
  666  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  667  constexpr Decimal& operator-=(Int rhs) {
 
  672  template <
typename Int, 
typename = impl::EnableIfInt<Int>>
 
  673  constexpr Decimal operator*(Int rhs) 
const {
 
  675    if (rhs > impl::kMaxInt64 ||
 
  676        __builtin_mul_overflow(value_, 
static_cast<int64_t>(rhs), &result)) {
 
  682  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  687  template <
typename Int, impl::EnableIfInt<Int> = 0>
 
  688  constexpr Decimal& operator*=(Int rhs) {
 
  694  constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs) 
const {
 
  705  template <
typename Int, 
typename = impl::EnableIfInt<Int>>
 
  706  constexpr Decimal operator/(Int rhs) 
const {
 
  710  template <
typename Int, 
typename = impl::EnableIfInt<Int>>
 
  715  template <
typename Int, 
typename = impl::EnableIfInt<Int>>
 
  716  constexpr Decimal& operator/=(Int rhs) {
 
  722  constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs) 
const {
 
  734  constexpr int Sign() 
const { 
return impl::Sign(value_); }
 
  742    return *
this / base.AsUnbiased() * base.AsUnbiased();
 
  759    constexpr std::int64_t kLossLimit =
 
  760        (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
 
  762    if (value_ > -kLossLimit && value_ < kLossLimit) {
 
  766    constexpr int kCoef =
 
  767        1 << (std::max(std::numeric_limits<std::int64_t>::digits -
 
  768                           std::numeric_limits<
double>::digits - 3 * Prec,
 
  789  template <
int Prec2, 
typename RoundPolicy2>
 
  790  static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
 
  791    if constexpr (Prec > Prec2) {
 
  793      if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>,
 
  798    } 
else if constexpr (Prec < Prec2) {
 
  800          impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
 
  806  template <
int Prec2, 
typename RoundPolicy2>
 
  809  template <
typename T, 
int OldPrec, 
typename OldRound>
 
  818struct IsDecimal : std::false_type {};
 
  820template <
int Prec, 
typename RoundPolicy>
 
  821struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
 
  842template <
typename T, 
int OldPrec, 
typename OldRound>
 
  844  static_assert(kIsDecimal<T>);
 
  845  return T::FromDecimal(arg);
 
  853template <
int Prec, 
typename RoundPolicy>
 
  854constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before,
 
  856  using Dec = 
Decimal<Prec, RoundPolicy>;
 
  857  UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
 
  860  if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
 
  861      __builtin_add_overflow(result, after, &result)) {
 
  865  return Dec::FromUnbiased(result);
 
  869template <
int Prec, 
typename RoundPolicy>
 
  870constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
 
  871                                                  int original_precision) {
 
  872  UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
 
  873  UASSERT(after > -Pow10(original_precision) &&
 
  874          after < Pow10(original_precision));
 
  876  if (original_precision <= Prec) {
 
  878    const int missing_digits = Prec - original_precision;
 
  879    const int64_t factor = Pow10(missing_digits);
 
  880    return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
 
  883    const int extra_digits = original_precision - Prec;
 
  884    const int64_t factor = Pow10(extra_digits);
 
  887    const int64_t rounded_after = Div<RoundPolicy>(after, factor);
 
  888    return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
 
  892struct UnpackedDecimal {
 
  900template <
int Prec, 
typename RoundPolicy>
 
  901constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
 
  902  using Dec = 
Decimal<Prec, RoundPolicy>;
 
  903  return {dec.AsUnbiased() / Dec::kDecimalFactor,
 
  904          dec.AsUnbiased() % Dec::kDecimalFactor};
 
  910template <
int Prec, 
typename RoundPolicy>
 
  911UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec, 
int new_prec) {
 
  912  if (new_prec == Prec) {
 
  913    return AsUnpacked(dec);
 
  916  if (new_prec > Prec) {
 
  917    if (__builtin_mul_overflow(dec.AsUnbiased(), Pow10(new_prec - Prec),
 
  922    result = impl::Div<RoundPolicy>(dec.AsUnbiased(), Pow10(Prec - new_prec));
 
  924  const auto dec_factor = Pow10(new_prec);
 
  925  return {result / dec_factor, result % dec_factor};
 
  928template <
typename CharT>
 
  929constexpr bool IsSpace(CharT c) {
 
  930  return c == 
' ' || c == 
'\t' || c == 
'\r' || c == 
'\n' || c == 
'\v';
 
  933template <
typename CharT, 
typename Traits>
 
  934class StringCharSequence {
 
  936  explicit constexpr StringCharSequence(
 
  937      std::basic_string_view<CharT, Traits> sv)
 
  938      : current_(sv.begin()), end_(sv.end()) {}
 
  941  constexpr CharT Get() { 
return current_ == end_ ? CharT{
'\0'} : *current_++; }
 
  943  constexpr void Unget() { --current_; }
 
  946  typename std::basic_string_view<CharT, Traits>::iterator current_;
 
  947  typename std::basic_string_view<CharT, Traits>::iterator end_;
 
  950template <
typename CharT, 
typename Traits>
 
  951class StreamCharSequence {
 
  953  explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in)
 
  958    constexpr CharT kEof =
 
  959        std::basic_istream<CharT, Traits>::traits_type::eof();
 
  963    const CharT c = in_->peek();
 
  971  void Unget() { in_->unget(); }
 
  974  std::basic_istream<CharT, Traits>* in_;
 
  977enum class ParseOptions {
 
  982  kAllowSpaces = 1 << 0,
 
  986  kAllowTrailingJunk = 1 << 1,
 
  990  kAllowBoundaryDot = 1 << 2,
 
  994  kAllowRounding = 1 << 3
 
  997enum class ParseErrorCode : uint8_t {
 
 1021struct ParseUnpackedResult {
 
 1024  uint8_t decimal_digits{0};
 
 1025  bool is_negative{
false};
 
 1026  std::optional<ParseErrorCode> error;
 
 1027  uint32_t error_position{-1U};
 
 1030enum class ParseState {
 
 1054template <
typename CharSequence>
 
 1055[[nodiscard]] 
constexpr ParseUnpackedResult ParseUnpacked(
 
 1056    CharSequence input, utils::Flags<ParseOptions> options) {
 
 1057  constexpr char dec_point = 
'.';
 
 1061  bool is_negative = 
false;
 
 1063  ptrdiff_t position = -1;
 
 1064  auto state = ParseState::kSign;
 
 1065  std::optional<ParseErrorCode> error;
 
 1066  int before_digit_count = 0;
 
 1067  uint8_t after_digit_count = 0;
 
 1069  while (state != ParseState::kEnd) {
 
 1070    const auto c = input.Get();
 
 1071    if (c == 
'\0') 
break;
 
 1072    if (!error) ++position;
 
 1075      case ParseState::kSign:
 
 1078          state = ParseState::kBeforeFirstDig;
 
 1079        } 
else if (c == 
'+') {
 
 1080          state = ParseState::kBeforeFirstDig;
 
 1081        } 
else if (c == 
'0') {
 
 1082          state = ParseState::kLeadingZeros;
 
 1083          before_digit_count = 1;
 
 1084        } 
else if ((c >= 
'1') && (c <= 
'9')) {
 
 1085          state = ParseState::kBeforeDec;
 
 1086          before = 
static_cast<
int>(c - 
'0');
 
 1087          before_digit_count = 1;
 
 1088        } 
else if (c == dec_point) {
 
 1089          if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
 
 1090            error = ParseErrorCode::kBoundaryDot;  
 
 1092          state = ParseState::kAfterDec;
 
 1093        } 
else if (IsSpace(c)) {
 
 1094          if (!(options & ParseOptions::kAllowSpaces)) {
 
 1095            state = ParseState::kEnd;
 
 1096            error = ParseErrorCode::kSpace;
 
 1099          state = ParseState::kEnd;
 
 1100          error = ParseErrorCode::kWrongChar;
 
 1103      case ParseState::kBeforeFirstDig:
 
 1105          state = ParseState::kLeadingZeros;
 
 1106          before_digit_count = 1;
 
 1107        } 
else if ((c >= 
'1') && (c <= 
'9')) {
 
 1108          state = ParseState::kBeforeDec;
 
 1109          before = 
static_cast<
int>(c - 
'0');
 
 1110          before_digit_count = 1;
 
 1111        } 
else if (c == dec_point) {
 
 1112          if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
 
 1113            error = ParseErrorCode::kBoundaryDot;  
 
 1115          state = ParseState::kAfterDec;
 
 1117          state = ParseState::kEnd;
 
 1118          error = ParseErrorCode::kWrongChar;
 
 1121      case ParseState::kLeadingZeros:
 
 1124        } 
else if ((c >= 
'1') && (c <= 
'9')) {
 
 1125          state = ParseState::kBeforeDec;
 
 1126          before = 
static_cast<
int>(c - 
'0');
 
 1127        } 
else if (c == dec_point) {
 
 1128          state = ParseState::kAfterDec;
 
 1130          state = ParseState::kEnd;
 
 1133      case ParseState::kBeforeDec:
 
 1134        if ((c >= 
'0') && (c <= 
'9')) {
 
 1135          if (before_digit_count < kMaxDecimalDigits) {
 
 1136            before = 10 * before + 
static_cast<
int>(c - 
'0');
 
 1137            before_digit_count++;
 
 1138          } 
else if (!error) {
 
 1139            error = ParseErrorCode::kOverflow;  
 
 1141        } 
else if (c == dec_point) {
 
 1142          state = ParseState::kAfterDec;
 
 1144          state = ParseState::kEnd;
 
 1147      case ParseState::kAfterDec:
 
 1148        if ((c >= 
'0') && (c <= 
'9')) {
 
 1149          if (after_digit_count < kMaxDecimalDigits) {
 
 1150            after = 10 * after + 
static_cast<
int>(c - 
'0');
 
 1151            after_digit_count++;
 
 1153            if (!(options & ParseOptions::kAllowRounding) && !error) {
 
 1154              error = ParseErrorCode::kRounding;  
 
 1156            state = ParseState::kIgnoringAfterDec;
 
 1163          if (!(options & ParseOptions::kAllowBoundaryDot) &&
 
 1164              after_digit_count == 0 && !error) {
 
 1165            error = ParseErrorCode::kBoundaryDot;
 
 1167          state = ParseState::kEnd;
 
 1170      case ParseState::kIgnoringAfterDec:
 
 1171        if ((c >= 
'0') && (c <= 
'9')) {
 
 1174          state = ParseState::kEnd;
 
 1177      case ParseState::kEnd:
 
 1183  if (state == ParseState::kEnd) {
 
 1186    if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
 
 1187      if (!(options & ParseOptions::kAllowSpaces)) {
 
 1188        error = ParseErrorCode::kSpace;
 
 1193        const auto c = input.Get();
 
 1194        if (c == 
'\0') 
break;
 
 1197          error = ParseErrorCode::kTrailingJunk;
 
 1205  if (!error && before_digit_count == 0 && after_digit_count == 0) {
 
 1206    error = ParseErrorCode::kNoDigits;
 
 1209  if (!error && state == ParseState::kAfterDec &&
 
 1210      !(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0) {
 
 1211    error = ParseErrorCode::kBoundaryDot;
 
 1214  return {before,      after, after_digit_count,
 
 1215          is_negative, error, 
static_cast<uint32_t>(position)};
 
 1218template <
int Prec, 
typename RoundPolicy>
 
 1220  Decimal<Prec, RoundPolicy> decimal;
 
 1221  std::optional<ParseErrorCode> error;
 
 1222  uint32_t error_position{-1U};
 
 1226template <
int Prec, 
typename RoundPolicy, 
typename CharSequence>
 
 1227[[nodiscard]] 
constexpr ParseResult<Prec, RoundPolicy> Parse(
 
 1228    CharSequence input, utils::Flags<ParseOptions> options) {
 
 1229  ParseUnpackedResult parsed = ParseUnpacked(input, options);
 
 1232    return {{}, parsed.error, parsed.error_position};
 
 1235  if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
 
 1236    return {{}, ParseErrorCode::kOverflow, 0};
 
 1239  if (!(options & ParseOptions::kAllowRounding) &&
 
 1240      parsed.decimal_digits > Prec) {
 
 1241    return {{}, ParseErrorCode::kRounding, 0};
 
 1244  if (parsed.is_negative) {
 
 1245    parsed.before = -parsed.before;
 
 1246    parsed.after = -parsed.after;
 
 1249  return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after,
 
 1250                                          parsed.decimal_digits),
 
 1255std::string GetErrorMessage(std::string_view source, std::string_view path,
 
 1256                            size_t position, ParseErrorCode reason);
 
 1260void TrimTrailingZeros(int64_t& after, 
int& after_precision);
 
 1262std::string ToString(int64_t before, int64_t after, 
int precision,
 
 1267template <
int Prec, 
typename RoundPolicy>
 
 1269  const auto result = impl::Parse<Prec, RoundPolicy>(
 
 1270      impl::StringCharSequence(value), impl::ParseOptions::kNone);
 
 1273    throw ParseError(impl::GetErrorMessage(
 
 1274        value, 
"<string>", result.error_position, *result.error));
 
 1276  *
this = result.decimal;
 
 1279template <
int Prec, 
typename RoundPolicy>
 
 1280constexpr Decimal<Prec, RoundPolicy>
 
 1281Decimal<Prec, RoundPolicy>::FromStringPermissive(std::string_view input) {
 
 1282  const auto result = impl::Parse<Prec, RoundPolicy>(
 
 1283      impl::StringCharSequence(input),
 
 1284      {impl::ParseOptions::kAllowSpaces, impl::ParseOptions::kAllowBoundaryDot,
 
 1285       impl::ParseOptions::kAllowRounding});
 
 1288    throw ParseError(impl::GetErrorMessage(
 
 1289        input, 
"<string>", result.error_position, *result.error));
 
 1291  return result.decimal;
 
 1302template <
int Prec, 
typename RoundPolicy>
 
 1304  return fmt::to_string(dec);
 
 1318template <
int Prec, 
typename RoundPolicy>
 
 1321  auto precision = format_options.precision.value_or(Prec);
 
 1323    precision = std::min(precision, Prec);
 
 1325  auto [before, after] = impl::AsUnpacked(dec, precision);
 
 1326  return impl::ToString(before, after, precision, format_options);
 
 1337template <
int Prec, 
typename RoundPolicy>
 
 1339  return fmt::format(FMT_COMPILE(
"{:f}"), dec);
 
 1350template <
int NewPrec, 
int Prec, 
typename RoundPolicy>
 
 1352  return ToStringTrailingZeros(
 
 1370template <
typename CharT, 
typename Traits, 
int Prec, 
typename RoundPolicy>
 
 1372    std::basic_istream<CharT, Traits>& is, 
Decimal<Prec, RoundPolicy>& d) {
 
 1373  if (is.flags() & std::ios_base::skipws) {
 
 1376  const auto result = impl::Parse<Prec, RoundPolicy>(
 
 1377      impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk});
 
 1380    is.setstate(std::ios_base::failbit);
 
 1389template <
typename CharT, 
typename Traits, 
int Prec, 
typename RoundPolicy>
 
 1390std::basic_ostream<CharT, Traits>& operator<<(
 
 1391    std::basic_ostream<CharT, Traits>& os,
 
 1392    const Decimal<Prec, RoundPolicy>& d) {
 
 1399template <
int Prec, 
typename RoundPolicy>
 
 1401                               const Decimal<Prec, RoundPolicy>& d) {
 
 1408template <
int Prec, 
typename RoundPolicy, 
typename Value>
 
 1412  const std::string input = value.
template As<std::string>();
 
 1414  const auto result = impl::Parse<Prec, RoundPolicy>(
 
 1415      impl::StringCharSequence(std::string_view{input}),
 
 1416      impl::ParseOptions::kNone);
 
 1419    throw ParseError(impl::GetErrorMessage(
 
 1420        input, value.GetPath(), result.error_position, *result.error));
 
 1422  return result.decimal;
 
 1427template <
int Prec, 
typename RoundPolicy, 
typename TargetType>
 
 1430  return typename TargetType::Builder(ToString(object)).ExtractValue();
 
 1435template <
int Prec, 
typename RoundPolicy, 
typename StringBuilder>
 
 1437                   StringBuilder& sw) {
 
 1438  WriteToStream(ToString(object), sw);
 
 1443USERVER_NAMESPACE_END
 
 1446template <
int Prec, 
typename RoundPolicy>
 
 1450  std::size_t operator()(
const Decimal& v) 
const noexcept {
 
 1451    return std::hash<int64_t>{}(v.AsUnbiased());
 
 1463template <
int Prec, 
typename RoundPolicy, 
typename Char>
 
 1464class fmt::formatter<USERVER_NAMESPACE::decimal64::Decimal<Prec, RoundPolicy>,
 
 1467  constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
 
 1468    const auto* it = ctx.begin();
 
 1469    const auto* end = ctx.end();
 
 1471    if (it != end && *it == 
'.') {
 
 1472      remove_trailing_zeros_ = 
false;
 
 1473      custom_precision_ = 0;
 
 1475      while (it != end && *it >= 
'0' && *it <= 
'9') {
 
 1476        *custom_precision_ = *custom_precision_ * 10 + (*it - 
'0');
 
 1481    if (!custom_precision_ && it != end && *it == 
'f') {
 
 1482      remove_trailing_zeros_ = 
false;
 
 1486    if (it != end && *it != 
'}') {
 
 1487      throw format_error(
"invalid format");
 
 1493  template <
typename FormatContext>
 
 1496      FormatContext& ctx) 
const {
 
 1497    int after_digits = custom_precision_.value_or(Prec);
 
 1498    auto [before, after] =
 
 1499        USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
 
 1500    if (remove_trailing_zeros_) {
 
 1501      USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after,
 
 1505    if (after_digits > 0) {
 
 1506      if (dec.Sign() == -1) {
 
 1507        return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before,
 
 1508                              -after, after_digits);
 
 1510        return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before,
 
 1511                              after, after_digits);
 
 1514      return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
 
 1519  bool remove_trailing_zeros_ = 
true;
 
 1520  std::optional<
int> custom_precision_;