Introduce Int128 library

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2025-08-27 10:54:37 +09:00 committed by MuHong Byun
commit 06e356f15a
14 changed files with 1103 additions and 97 deletions

View file

@ -86,8 +86,9 @@ static Value builtinTemporalInstantConstructor(ExecutionState& state, Value this
// Let epochNanoseconds be ? ToBigInt(epochNanoseconds).
BigInt* epochNanoseconds = argv[0].toBigInt(state);
auto mayInt128 = epochNanoseconds->toInt128();
// If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!Temporal::isValidEpochNanoseconds(epochNanoseconds)) {
if (!mayInt128 || !Temporal::isValidEpochNanoseconds(mayInt128.value())) {
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid epoch value");
}
@ -96,7 +97,7 @@ static Value builtinTemporalInstantConstructor(ExecutionState& state, Value this
});
// Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).
return new TemporalInstantObject(state, proto, epochNanoseconds);
return new TemporalInstantObject(state, proto, mayInt128.value());
}
#define RESOLVE_THIS_BINDING_TO_INSTANT(NAME, BUILT_IN_METHOD) \
@ -114,7 +115,8 @@ static Value builtinTemporalInstantGetEpochMilliseconds(ExecutionState& state, V
static Value builtinTemporalInstantGetEpochNanoseconds(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_INSTANT(instant, GetEpochNanoseconds);
return instant->epochNanoseconds();
auto s = std::to_string(instant->epochNanoseconds());
return BigInt::parseString(s.data(), s.length()).value();
}
#define RESOLVE_THIS_BINDING_TO_PLAINDATE(NAME, BUILT_IN_METHOD) \

View file

@ -404,6 +404,54 @@ static bool isValidDurationWork(double v, int& sign)
return true;
}
static BigIntData totalNanoseconds(const DurationRecord& record)
{
BigIntData resultNs;
constexpr int64_t nanoMultiplier = 1000000000ULL;
constexpr int64_t milliMultiplier = 1000000ULL;
constexpr int64_t microMultiplier = 1000ULL;
{
BigIntData s(record.days());
s = s.multiply(86400);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.hours());
s = s.multiply(3600);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.minutes());
s = s.multiply(60);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.seconds());
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.milliseconds());
s = s.multiply(milliMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.microseconds());
s = s.multiply(microMultiplier);
resultNs = resultNs.addition(s);
}
{
BigIntData s(record.nanoseconds());
resultNs = resultNs.addition(s);
}
return resultNs;
}
// https://tc39.es/ecma402/#sec-isvalidduration
static bool isValidDuration(const DurationRecord& record)
{
@ -431,15 +479,14 @@ static bool isValidDuration(const DurationRecord& record)
// Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + (𝔽(milliseconds)) × 10**-3 + (𝔽(microseconds)) × 10**-6 + (𝔽(nanoseconds)) × 10**-9.
// NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3, 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo() with sufficient bits in the quotient. String manipulation will also give an exact result, since the multiplication is by a power of 10.
BigIntData normalizedNanoSeconds = record.totalNanoseconds(DurationRecord::Type::Years);
BigIntData normalizedNanoSeconds = totalNanoseconds(record);
// If abs(normalizedSeconds) ≥ 2**53, return false.
BigIntData limit(int64_t(1ULL << 53));
limit = limit.multiply(1000000000ULL);
if (normalizedNanoSeconds.greaterThanEqual(limit)) {
return false;
}
limit = BigIntData(int64_t(1ULL << 53));
limit = limit.multiply(-1000000000ULL);
limit = limit.multiply(-1);
if (normalizedNanoSeconds.lessThanEqual(limit)) {
return false;
}
@ -552,7 +599,7 @@ inline double purifyNaN(double value)
return value;
}
static std::string buildDecimalFormat(DurationRecord::Type unit, BigIntData ns)
static std::string buildDecimalFormat(DurationRecord::Type unit, Int128 ns)
{
ASSERT(unit == DurationRecord::Type::Seconds || unit == DurationRecord::Type::Milliseconds || unit == DurationRecord::Type::Microseconds);
@ -570,19 +617,19 @@ static std::string buildDecimalFormat(DurationRecord::Type unit, BigIntData ns)
exponent = 1000;
}
BigIntData integerPart = ns;
integerPart = integerPart.division(exponent);
Int128 integerPart = ns;
integerPart /= exponent;
BigIntData fractionalPart(ns);
fractionalPart = fractionalPart.remainder(exponent);
Int128 fractionalPart = ns;
fractionalPart %= exponent;
StringBuilder builder;
auto integerPartString = integerPart.toNonGCStdString();
auto integerPartString = std::to_string(integerPart);
builder.appendString(integerPartString.data(), integerPartString.size());
builder.appendChar('.');
auto fractionalString = fractionalPart.toNonGCStdString();
auto fractionalString = std::to_string(fractionalPart);
if (fractionalString.size() && fractionalString[0] == '-') {
fractionalString.erase(fractionalString.begin());
}
@ -648,7 +695,7 @@ std::vector<IntlDurationFormatObject::Element> IntlDurationFormatObject::collect
DurationRecord::Type unit = static_cast<DurationRecord::Type>(index);
auto unitData = durationFormat->data(index);
double value = duration[unit];
Optional<BigIntData> totalNanosecondsValue;
Optional<Int128> totalNanosecondsValue;
UTF16StringDataNonGCStd skeletonBuilder;

View file

@ -765,4 +765,39 @@ bool BigInt::isNegative() const
return m_bf.sign;
}
Optional<Int128> BigInt::toInt128()
{
auto s = std::to_string(std::numeric_limits<Int128>::min());
BigIntData test(s.data(), s.length());
if (lessThan(test)) {
return NullOption;
}
s = std::to_string(std::numeric_limits<Int128>::max());
BigIntData test2(s.data(), s.length());
if (greaterThan(test2)) {
return NullOption;
}
BigIntData big(this);
s = big.toNonGCStdString();
bool sign = s.length() && s[0] == '-';
if (sign) {
s.erase(s.begin());
}
Int128 ret = 0;
for (char c : s) {
ret *= 10;
ret += c - '0';
}
if (sign) {
ret *= -1;
}
return ret;
}
} // namespace Escargot

View file

@ -21,6 +21,7 @@
#define __EscargotBigInt__
#include "runtime/PointerValue.h"
#include "util/Int128.h"
namespace Escargot {
@ -127,6 +128,8 @@ public:
BigInt* negativeValue(ExecutionState& state);
BigInt* negativeValue();
Optional<Int128> toInt128();
bool isZero() const;
bool isNaN() const;
bool isInfinity() const;

View file

@ -23,17 +23,17 @@
namespace Escargot {
TemporalInstantObject::TemporalInstantObject(ExecutionState& state, Object* proto, BigInt* n)
TemporalInstantObject::TemporalInstantObject(ExecutionState& state, Object* proto, Int128 n)
: DerivedObject(state, proto)
, m_nanoseconds(n)
, m_nanoseconds(new(PointerFreeGC) Int128(n))
{
}
Value TemporalInstantObject::epochMilliseconds() const
{
BigIntData s(m_nanoseconds);
s = s.division(1000000);
return Value(s.toInt64());
Int128 s = *m_nanoseconds;
s /= 1000000;
return Value(static_cast<int64_t>(s));
}
TemporalInstantObject* TemporalInstantObject::addDurationToInstant(AddDurationOperation operation, const Value& temporalDurationLike)

View file

@ -27,7 +27,7 @@ namespace Escargot {
class TemporalInstantObject : public DerivedObject {
public:
TemporalInstantObject(ExecutionState& state, Object* proto, BigInt* nanoseconds);
TemporalInstantObject(ExecutionState& state, Object* proto, Int128 nanoseconds);
virtual bool isTemporalInstantObject() const override
{
@ -35,9 +35,9 @@ public:
}
Value epochMilliseconds() const;
BigInt* epochNanoseconds() const
Int128 epochNanoseconds() const
{
return m_nanoseconds;
return *m_nanoseconds;
}
private:
@ -48,7 +48,7 @@ private:
};
TemporalInstantObject* addDurationToInstant(AddDurationOperation operation, const Value& temporalDurationLike);
BigInt* m_nanoseconds; // [[EpochNanoseconds]]
Int128* m_nanoseconds; // [[EpochNanoseconds]]
};
} // namespace Escargot

View file

@ -24,6 +24,7 @@
#include "runtime/BigInt.h"
#include "runtime/DateObject.h"
#include "runtime/TemporalDurationObject.h"
#include "runtime/TemporalInstantObject.h"
namespace Escargot {
@ -338,46 +339,16 @@ bool Temporal::ISODateTimeWithinLimits(ExecutionState& state, const ISODateTime&
// Z(R(ms) × 10**6 + isoDateTime.[[Time]].[[Microsecond]] × 10**3 + isoDateTime.[[Time]].[[Nanosecond]]).
int64_t ns = static_cast<int64_t>(ms) * 1000000 + dateTime.time.microsecond * 1000 + dateTime.time.nanosecond;
BigIntData nsData(ns);
if (nsData.lessThanEqual(Temporal::nsMinConstant())) {
if (ns <= Temporal::nsMinConstant()) {
return false;
}
if (nsData.greaterThanEqual(Temporal::nsMaxConstant())) {
if (ns >= Temporal::nsMaxConstant()) {
return false;
}
return true;
}
const BigIntData& Temporal::nsMaxInstant()
{
const char* str = "8640000000000000000000";
static MAY_THREAD_LOCAL BigIntData constant(str, strlen(str), 10);
return constant;
}
const BigIntData& Temporal::nsMinInstant()
{
const char* str = "-8640000000000000000000";
static MAY_THREAD_LOCAL BigIntData constant(str, strlen(str), 10);
return constant;
}
const BigIntData& Temporal::nsMaxConstant()
{
const char* str = "8640000086400000000000";
static MAY_THREAD_LOCAL BigIntData constant(str, strlen(str), 10);
return constant;
}
const BigIntData& Temporal::nsMinConstant()
{
const char* str = "-8640000086400000000000";
static MAY_THREAD_LOCAL BigIntData constant(str, strlen(str), 10);
return constant;
}
int64_t Temporal::nsPerDay()
{
return 86400000000000;
@ -417,25 +388,24 @@ Value Temporal::toTemporalDate(ExecutionState& state, Value item, Value options)
return Value();
}
BigInt* Temporal::systemUTCEpochNanoseconds()
Int128 Temporal::systemUTCEpochNanoseconds()
{
std::chrono::system_clock::duration d = std::chrono::system_clock::now().time_since_epoch();
auto microSecondsSinceEpoch = std::chrono::duration_cast<std::chrono::microseconds>(d).count();
unsigned long nano = std::chrono::duration_cast<std::chrono::nanoseconds>(d - std::chrono::duration_cast<std::chrono::microseconds>(d)).count();
BigIntData ret = BigIntData(microSecondsSinceEpoch);
ret = ret.multiply(1000);
ret = ret.addition(nano);
Int128 ret = Int128(microSecondsSinceEpoch);
ret *= 1000;
ret += nano;
return new BigInt(std::move(ret));
return ret;
}
bool Temporal::isValidEpochNanoseconds(BigInt* s)
bool Temporal::isValidEpochNanoseconds(Int128 epochNanoseconds)
{
BigIntData epochNanoseconds(s);
// If (epochNanoseconds) < nsMinInstant or (epochNanoseconds) > nsMaxInstant, then
if (epochNanoseconds.lessThan(Temporal::nsMinInstant()) || epochNanoseconds.greaterThan(Temporal::nsMaxInstant())) {
if (epochNanoseconds < Temporal::nsMinInstant() || epochNanoseconds > Temporal::nsMaxInstant()) {
// Return false.
return false;
}
@ -452,7 +422,7 @@ TemporalDurationObject* Temporal::toTemporalDuration(ExecutionState& state, cons
return new TemporalDurationObject(state, item.asPointerValue()->asTemporalDurationObject()->duration());
}
constexpr auto msg = "The value you gave for toTemporalDuration is invalid";
constexpr auto msg = "The value you gave for ToTemporalDuration is invalid";
if (!item.isObject()) {
// If item is not an Object, then
// If item is not a String, throw a TypeError exception.
@ -493,7 +463,41 @@ TemporalDurationObject* Temporal::toTemporalDuration(ExecutionState& state, cons
// Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
return new TemporalDurationObject(state, duration);
}
/*
// https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
TemporalInstantObject* Temporal::toTemporalInstant(ExecutionState& state, Value item)
{
// If item is an Object, then
if (item.isObject()) {
// If item has an [[InitializedTemporalInstant]] or [[InitializedTemporalZonedDateTime]] internal slot, then
// TODO [[InitializedTemporalZonedDateTime]]
if (item.asObject()->isTemporalInstantObject()) {
// Return ! CreateTemporalInstant(item.[[EpochNanoseconds]]).
return new TemporalInstantObject(state, state.context()->globalObject()->temporalInstantPrototype(),
item.asObject()->asTemporalInstantObject()->epochNanoseconds());
}
// NOTE: This use of ToPrimitive allows Instant-like objects to be converted.
// Set item to ? ToPrimitive(item, string).
item = item.toPrimitive(state, Value::PrimitiveTypeHint::PreferString);
}
constexpr auto msg = "The value you gave for ToTemporalInstant is invalid";
if (!item.isString()) {
// If item is not a String, throw a TypeError exception.
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, msg);
}
// Let parsed be ? ParseISODateTime(item, « TemporalInstantString »).
// Assert: Either parsed.[[TimeZone]].[[OffsetString]] is not empty or parsed.[[TimeZone]].[[Z]] is true, but not both.
// If parsed.[[TimeZone]].[[Z]] is true, let offsetNanoseconds be 0; otherwise, let offsetNanoseconds be ! ParseDateTimeUTCOffset(parsed.[[TimeZone]].[[OffsetString]]).
// If parsed.[[Time]] is start-of-day, let time be MidnightTimeRecord(); else let time be parsed.[[Time]].
// Let balanced be BalanceISODateTime(parsed.[[Year]], parsed.[[Month]], parsed.[[Day]], time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], time.[[Microsecond]], time.[[Nanosecond]] - offsetNanoseconds).
// Perform ? CheckISODaysRange(balanced.[[ISODate]]).
// Let epochNanoseconds be GetUTCEpochNanoseconds(balanced).
// If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
// Return ! CreateTemporalInstant(epochNanoseconds).
return nullptr;
}
*/
TemporalObject::TemporalObject(ExecutionState& state)
: TemporalObject(state, state.context()->globalObject()->objectPrototype())
{

View file

@ -26,6 +26,7 @@
#include "runtime/GlobalObject.h"
#include "runtime/DateObject.h"
#include "intl/Intl.h"
#include "util/Int128.h"
namespace Escargot {
@ -210,22 +211,45 @@ public:
static bool ISODateWithinLimits(ExecutionState& state, const ISODate& date);
static bool ISODateTimeWithinLimits(ExecutionState& state, const ISODateTime& dateTime);
static const BigIntData& nsMaxInstant();
static const BigIntData& nsMinInstant();
static const BigIntData& nsMaxConstant();
static const BigIntData& nsMinConstant();
static Int128 nsMaxInstant()
{
Int128 ret = 864000000;
ret *= 10000000000000;
return ret;
}
static Int128 nsMinInstant()
{
Int128 ret = -864000000;
ret *= 10000000000000;
return ret;
}
static Int128 nsMaxConstant()
{
Int128 ret = 86400000864;
ret *= 100000000000;
return ret;
}
static Int128 nsMinConstant()
{
Int128 ret = -86400000864;
ret *= 100000000000;
return ret;
}
static int64_t nsPerDay();
static Value createTemporalDate(ExecutionState& state, const ISODate& isoDate, String* calendar, Optional<Object*> newTarget);
static Value toTemporalDate(ExecutionState& state, Value item, Value options = Value());
// https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds
static BigInt* systemUTCEpochNanoseconds();
static Int128 systemUTCEpochNanoseconds();
// https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
static bool isValidEpochNanoseconds(BigInt* s);
static bool isValidEpochNanoseconds(Int128 s);
// https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration
static TemporalDurationObject* toTemporalDuration(ExecutionState& state, const Value& item);
// https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
static TemporalInstantObject* toTemporalInstant(ExecutionState& state, Value item);
};
class TemporalObject : public DerivedObject {

View file

@ -274,50 +274,54 @@ String* Duration::typeName(ExecutionState& state, Type t)
return String::emptyString();
}
BigIntData Duration::totalNanoseconds(Duration::Type unit) const
Int128 Duration::totalNanoseconds(Duration::Type unit) const
{
BigIntData resultNs;
ASSERT(unit != Duration::Type::Years);
ASSERT(unit != Duration::Type::Months);
ASSERT(unit != Duration::Type::Weeks);
Int128 resultNs = 0;
constexpr int64_t nanoMultiplier = 1000000000ULL;
constexpr int64_t milliMultiplier = 1000000ULL;
constexpr int64_t microMultiplier = 1000ULL;
if (unit <= Duration::Type::Days) {
BigIntData s(days());
s = s.multiply(86400);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
Int128 s(days());
s *= 86400;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Hours) {
BigIntData s(hours());
s = s.multiply(3600);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
Int128 s(hours());
s *= 3600;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Minutes) {
BigIntData s(minutes());
s = s.multiply(60);
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
Int128 s(minutes());
s *= 60;
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Seconds) {
BigIntData s(seconds());
s = s.multiply(nanoMultiplier);
resultNs = resultNs.addition(s);
Int128 s(seconds());
s *= nanoMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Milliseconds) {
BigIntData s(milliseconds());
s = s.multiply(milliMultiplier);
resultNs = resultNs.addition(s);
Int128 s(milliseconds());
s *= milliMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Microseconds) {
BigIntData s(microseconds());
s = s.multiply(microMultiplier);
resultNs = resultNs.addition(s);
Int128 s(microseconds());
s *= microMultiplier;
resultNs += s;
}
if (unit <= Duration::Type::Nanoseconds) {
BigIntData s(nanoseconds());
resultNs = resultNs.addition(s);
Int128 s(nanoseconds());
resultNs += s;
}
return resultNs;

View file

@ -23,6 +23,7 @@
#include "runtime/BigInt.h"
#include "runtime/String.h"
#include "util/Int128.h"
namespace Escargot {
class ExecutionState;
@ -64,7 +65,7 @@ public:
}
static String* typeName(ExecutionState& state, Type t);
BigIntData totalNanoseconds(Duration::Type type) const;
Int128 totalNanoseconds(Duration::Type type) const;
double operator[](size_t idx) const
{

43
src/util/Int128.h Normal file
View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2025-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef __EscargotInt128__
#define __EscargotInt128__
#define INT128_SPECIALIZATION
#include "int128.h"
#undef INT128_SPECIALIZATION
namespace Escargot {
using Int128 = large_int::int128_t;
} // namespace Escargot
namespace std {
inline string to_string(const Escargot::Int128& val)
{
std::stringstream ss;
ss << val;
return ss.str();
}
} // namespace std
#endif

21
third_party/int128/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 zhanhb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
third_party/int128/README.md vendored Normal file
View file

@ -0,0 +1,13 @@
https://github.com/zhanhb/int128/
# int128
int128 with divide/stream support, require C++ 11
```cpp
uint128_t a = 2'000'000'000'000'000'000'000_U128; // single quotes requires C++ 14
int128_t b = 1'000'000'000'000'000'000'000_L128;
cout << setw(40) << setfill('0') << hex << uppercase << a / b << endl;
setlocale(LC_ALL, "zh_CN.UTF-8");
cout.imbue(locale());
cout << a / b << endl;
```

809
third_party/int128/int128.h vendored Normal file
View file

@ -0,0 +1,809 @@
#pragma once
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <limits>
#include <locale>
#include <string>
#include <type_traits>
#ifndef __BYTE_ORDER__
#error __BYTE_ORDER__ not defined
#endif
namespace large_int {
template<class, class>
class int128_base;
typedef int128_base<int64_t, uint64_t> int128_t;
typedef int128_base<uint64_t, uint64_t> uint128_t;
template<class _Tp>
struct half_mask : std::integral_constant<_Tp, (_Tp(1) << (4 * sizeof(_Tp))) - _Tp(1)> {
};
template<bool= true>
struct detail_delegate;
constexpr bool operator<(int128_t, int128_t);
constexpr bool operator<(uint128_t, uint128_t);
constexpr uint128_t operator>>(uint128_t, int);
constexpr int128_t operator>>(int128_t, int);
constexpr int128_t operator*(int128_t, int128_t);
constexpr uint128_t operator*(uint128_t, uint128_t);
constexpr uint128_t operator<<(uint128_t, int);
constexpr int128_t operator<<(int128_t, int);
inline uint128_t operator/(uint128_t, uint128_t);
inline int128_t operator/(int128_t, int128_t);
inline uint128_t operator%(uint128_t, uint128_t);
inline int128_t operator%(int128_t, int128_t);
template<class _Hi, class _Low>
class alignas(sizeof(_Hi) * 2) int128_base final {
static_assert(sizeof(_Hi) == sizeof(_Low), "low type, high type should have same size");
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
_Low low_{};
_Hi high_{};
constexpr int128_base(_Hi high, _Low low) : low_(low), high_(high) {}
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
_Hi high_{};
_Low low_{};
constexpr int128_base(_Hi high, _Low low) : high_(high), low_(low) {}
#else
#error endian not support
#endif
struct integral_tag {
};
struct signed_integral_tag : integral_tag {
};
struct unsigned_integral_tag : integral_tag {
};
struct float_tag {
};
template<size_t>
struct size_constant {
};
private:
template<class _Tp>
constexpr int128_base(_Tp value_, signed_integral_tag, size_constant<8>) :
int128_base(-(value_ < 0), value_) {}
template<class _Tp>
constexpr int128_base(_Tp value_, unsigned_integral_tag, size_constant<8>) : int128_base(0, _Low(value_)) {}
template<class _Tp>
constexpr int128_base(_Tp value_, integral_tag, size_constant<16>) : // NOLINT explicit
int128_base(_Hi(value_ >> 64U), _Low(value_)) {} // NOLINT signed shift
public:
constexpr int128_base() noexcept = default;
constexpr int128_base(const int128_base &) noexcept = default;
constexpr int128_base(int128_base &&) noexcept = default;
int128_base &operator=(const int128_base &) noexcept = default;
int128_base &operator=(int128_base &&) noexcept = default;
template<class _Tp>
constexpr explicit int128_base(int128_base<_Tp, _Low> val_) : int128_base(val_.high_, val_.low_) {}
template<class _Tp>
constexpr int128_base(_Tp val_, float_tag) :
int128_base(_Hi(std::ldexp(val_, -64)) - (val_ < 0), _Low(val_)) {}
constexpr explicit int128_base(float val_) : int128_base(val_, float_tag()) {}
constexpr explicit int128_base(double val_) : int128_base(val_, float_tag()) {}
constexpr explicit int128_base(long double val_) : int128_base(val_, float_tag()) {}
constexpr int128_base(long long val_) : // NOLINT explicit
int128_base(val_, signed_integral_tag(), size_constant<sizeof(val_)>()) {}
constexpr int128_base(long val_) : int128_base(static_cast<long long>(val_)) {} // NOLINT explicit
constexpr int128_base(int val_) : int128_base(long(val_)) {} // NOLINT explicit
constexpr int128_base(unsigned long long val_) : // NOLINT explicit
int128_base(val_, unsigned_integral_tag(), size_constant<sizeof(val_)>()) {}
constexpr int128_base(unsigned long val_) : // NOLINT explicit
int128_base(static_cast<unsigned long long>(val_)) {}
constexpr int128_base(unsigned val_) : int128_base(static_cast<unsigned long>(val_)) {} // NOLINT explicit
constexpr explicit operator bool() const { return high_ || low_; }
constexpr explicit operator char() const { return char(low_); }
constexpr explicit operator signed char() const { return static_cast<signed char>(low_); }
constexpr explicit operator unsigned char() const { return static_cast<unsigned char>(low_); }
constexpr explicit operator short() const { return short(low_); }
constexpr explicit operator unsigned short() const { return static_cast<unsigned short>(low_); }
constexpr explicit operator int() const { return int(low_); }
constexpr explicit operator unsigned() const { return unsigned(low_); }
constexpr explicit operator long() const { return long(low_); }
constexpr explicit operator unsigned long() const { return static_cast<unsigned long>(low_); }
constexpr explicit operator long long() const { return static_cast<long long>(low_); }
constexpr explicit operator unsigned long long() const { return static_cast<unsigned long long>(low_); }
constexpr explicit operator wchar_t() const { return wchar_t(low_); }
constexpr explicit operator char16_t() const { return char16_t(low_); }
constexpr explicit operator char32_t() const { return char32_t(low_); }
#if __SIZEOF_INT128__ == 16
constexpr explicit int128_base(__int128 val_) :
int128_base(val_, signed_integral_tag(), size_constant<sizeof(val_)>()) {}
constexpr explicit int128_base(unsigned __int128 val_) :
int128_base(val_, unsigned_integral_tag(), size_constant<sizeof(val_)>()) {}
constexpr explicit operator unsigned __int128() const {
return static_cast<unsigned __int128>(high_) << 64U | static_cast<unsigned __int128>(low_);
}
constexpr explicit operator __int128() const {
return static_cast<__int128>(static_cast<unsigned __int128>(*this));
}
#endif
private:
template<class _Tp>
constexpr _Tp cast_to_float() const;
public:
constexpr explicit operator float() const { return cast_to_float<float>(); }
constexpr explicit operator double() const { return cast_to_float<double>(); }
constexpr explicit operator long double() const { return cast_to_float<long double>(); }
constexpr int128_base operator+() const { return *this; }
constexpr int128_base operator-() const { return int128_base(-high_ - (low_ != 0), -low_); }
constexpr int128_base operator~() const { return int128_base(~high_, ~low_); }
constexpr bool operator!() const { return !high_ && !low_; }
// avoid self plus on rvalue
int128_base &operator++() &{ return *this = *this + int128_base(1); }
int128_base &operator--() &{ return *this = *this - int128_base(1); }
int128_base operator++(int) &{ // NOLINT returns non constant
int128_base tmp = *this;
++*this;
return tmp;
}
int128_base operator--(int) &{ // NOLINT returns non constant
int128_base tmp = *this;
--*this;
return tmp;
}
friend constexpr int128_base operator+(int128_base lhs_, int128_base rhs_) {
// no worry for unsigned type, won't be optimized if overflow
return {_Hi(lhs_.high_ + rhs_.high_ + (lhs_.low_ + rhs_.low_ < lhs_.low_)), lhs_.low_ + rhs_.low_};
}
friend constexpr int128_base operator-(int128_base lhs_, int128_base rhs_) {
return {_Hi(lhs_.high_ - rhs_.high_ - (lhs_.low_ < rhs_.low_)), lhs_.low_ - rhs_.low_};
}
friend constexpr int128_base operator&(int128_base lhs_, int128_base rhs_) {
return {lhs_.high_ & rhs_.high_, lhs_.low_ & rhs_.low_};
}
friend constexpr int128_base operator|(int128_base lhs_, int128_base rhs_) {
return {lhs_.high_ | rhs_.high_, lhs_.low_ | rhs_.low_};
}
friend constexpr int128_base operator^(int128_base lhs_, int128_base rhs_) {
return {lhs_.high_ ^ rhs_.high_, lhs_.low_ ^ rhs_.low_};
}
friend constexpr bool operator==(int128_base lhs_, int128_base rhs_) {
return lhs_.high_ == rhs_.high_ && lhs_.low_ == rhs_.low_;
}
friend constexpr bool operator>(int128_base lhs_, int128_base rhs_) { return rhs_ < lhs_; }
friend constexpr bool operator>=(int128_base lhs_, int128_base rhs_) { return !(lhs_ < rhs_); }
friend constexpr bool operator<=(int128_base lhs_, int128_base rhs_) { return !(rhs_ < lhs_); }
friend constexpr bool operator!=(int128_base lhs_, int128_base rhs_) { return !(lhs_ == rhs_); }
friend constexpr int128_base operator<<(int128_base lhs_, int128_base rhs_) { return lhs_ << (int) rhs_.low_; }
friend constexpr int128_base operator>>(int128_base lhs_, int128_base rhs_) { return lhs_ >> (int) rhs_.low_; }
int128_base &operator+=(int128_base rhs_) &{ return *this = *this + rhs_; }
int128_base &operator-=(int128_base rhs_) &{ return *this = *this - rhs_; }
int128_base &operator*=(int128_base rhs_) &{ return *this = *this * rhs_; }
int128_base &operator/=(int128_base rhs_) &{ return *this = *this / rhs_; }
int128_base &operator%=(int128_base rhs_) &{ return *this = *this % rhs_; }
int128_base &operator<<=(int128_base rhs_) &{ return *this = *this << rhs_; }
int128_base &operator>>=(int128_base rhs_) &{ return *this = *this >> rhs_; }
int128_base &operator<<=(int rhs_) &{ return *this = *this << rhs_; }
int128_base &operator>>=(int rhs_) &{ return *this = *this >> rhs_; }
int128_base &operator&=(int128_base rhs_) &{ return *this = *this & rhs_; }
int128_base &operator|=(int128_base rhs_) &{ return *this = *this | rhs_; }
int128_base &operator^=(int128_base rhs_) &{ return *this = *this ^ rhs_; }
template<class, class>
friend
class int128_base;
template<class>
friend
struct clz_helper;
template<bool>
friend
struct detail_delegate;
};
inline namespace literals {
namespace impl_ {
template<char _Ch, int _Rad>
struct static_digit : std::integral_constant<int,
'0' <= _Ch && _Ch <= '9' ? _Ch - '0' :
'a' <= _Ch && _Ch <= 'z' ? _Ch - 'a' + 10 :
'A' <= _Ch && _Ch <= 'Z' ? _Ch - 'A' + 10 : _Rad> {
static_assert(_Rad > static_digit::value, "character not a digit");
};
template<class, int, char ...>
struct int128_literal_radix;
template<class _Tp, int _Rad, char _Ch>
struct int128_literal_radix<_Tp, _Rad, _Ch> {
constexpr operator _Tp() const { return _Tp(static_digit<_Ch, _Rad>::value); } // NOLINT explicit
constexpr _Tp operator()(_Tp v) const { return v * _Tp(_Rad) + *this; }
};
template<class _Tp, int _Rad, char _Ch, char ..._Args>
struct int128_literal_radix<_Tp, _Rad, _Ch, _Args...> {
int128_literal_radix<_Tp, _Rad, _Ch> _Cur;
int128_literal_radix<_Tp, _Rad, _Args...> _Tgt;
constexpr operator _Tp() const { return _Tgt(_Cur); }; // NOLINT explicit
constexpr _Tp operator()(_Tp v) const { return _Tgt(_Cur(v)); };
};
template<class _Tp, char ..._Args>
struct int128_literal : int128_literal_radix<_Tp, 10, _Args...> {
};
template<class _Tp>
struct int128_literal<_Tp, '0'> : int128_literal_radix<_Tp, 10, '0'> {
};
template<class _Tp, char ..._Args>
struct int128_literal<_Tp, '0', _Args...> : int128_literal_radix<_Tp, 8, _Args...> {
};
template<class _Tp, char ..._Args>
struct int128_literal<_Tp, '0', 'x', _Args...> : int128_literal_radix<_Tp, 16, _Args...> {
};
template<class _Tp, char ..._Args>
struct int128_literal<_Tp, '0', 'X', _Args...> : int128_literal_radix<_Tp, 16, _Args...> {
};
template<class _Tp, char ..._Args>
struct int128_literal<_Tp, '0', 'b', _Args...> : int128_literal_radix<_Tp, 2, _Args...> {
};
template<class _Tp, char ..._Args>
struct int128_literal<_Tp, '0', 'B', _Args...> : int128_literal_radix<_Tp, 2, _Args...> {
};
}
template<char ..._Args>
constexpr uint128_t operator "" _u128() { return impl_::int128_literal<uint128_t, _Args...>(); }
template<char ..._Args>
constexpr int128_t operator "" _l128() { return impl_::int128_literal<int128_t, _Args...>(); }
template<char ..._Args>
constexpr uint128_t operator "" _U128() { return impl_::int128_literal<uint128_t, _Args...>(); }
template<char ..._Args>
constexpr int128_t operator "" _L128() { return impl_::int128_literal<int128_t, _Args...>(); }
}
template<class>
struct clz_helper;
template<>
struct clz_helper<unsigned long> {
static constexpr int clz(unsigned long val_) { return __builtin_clzl(val_); }
};
template<>
struct clz_helper<unsigned long long> {
static constexpr int clz(unsigned long long val_) { return __builtin_clzll(val_); }
};
template<class _High, class _Low>
struct clz_helper<int128_base<_High, _Low> > {
static constexpr int clz(int128_base<_High, _Low> val_) {
return val_.high_ ? clz_helper<_Low>::clz(val_.high_) : 4 * sizeof(val_) + clz_helper<_Low>::clz(val_.low_);
}
};
template<bool>
struct detail_delegate {
template<class _Hi, class _Low>
static constexpr bool cmp(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return lhs_.high_ < rhs_.high_ || (lhs_.high_ == rhs_.high_ && lhs_.low_ < rhs_.low_);
}
static constexpr uint128_t shr(uint128_t lhs_, unsigned rhs_) {
return rhs_ & 64U ? uint128_t(0, lhs_.high_ >> (rhs_ & 63U)) :
rhs_ & 63U ? uint128_t(lhs_.high_ >> (rhs_ & 63U),
(lhs_.high_ << (64 - (rhs_ & 63U)) | (lhs_.low_ >> (rhs_ & 63U)))) : lhs_;
}
static constexpr int128_t sar(int128_t lhs_, unsigned rhs_) {
return rhs_ & 64U ? int128_t(-(lhs_.high_ < 0), uint64_t(lhs_.high_ >> (rhs_ & 63U))) : // NOLINT
rhs_ & 63U ? int128_t(
lhs_.high_ >> (rhs_ & 63U), // NOLINT signed shift
(uint64_t(lhs_.high_) << (64 - (rhs_ & 63U)) | (lhs_.low_ >> (rhs_ & 63U)))) : lhs_;
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> imul(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return int128_base<_Hi, _Low>(
_Hi(lhs_.low_ * rhs_.high_ + rhs_.low_ * lhs_.high_) + (lhs_.low_ >> 32U) * (rhs_.low_ >> 32U),
(lhs_.low_ & half_mask<_Low>::value) * (rhs_.low_ & half_mask<_Low>::value))
+ (int128_base<_Hi, _Low>((lhs_.low_ >> 32U) * (rhs_.low_ & half_mask<_Low>::value)) << 32U)
+ (int128_base<_Hi, _Low>((rhs_.low_ >> 32U) * (lhs_.low_ & half_mask<_Low>::value)) << 32U);
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> shl(int128_base<_Hi, _Low> lhs_, unsigned rhs_) {
// [64,127], 64 {low_ << 0, 0}
return rhs_ & 64U ? int128_base<_Hi, _Low>(_Hi(lhs_.low_ << (rhs_ & 63U)), _Low(0)) :
rhs_ & 63U ? int128_base<_Hi, _Low>(
_Hi((_Low(lhs_.high_) << (rhs_ & 63U)) | (lhs_.low_ >> (64U - (rhs_ & 63U)))),
lhs_.low_ << (rhs_ & 63U)) : lhs_;
}
static uint128_t &slow_div_(uint128_t &dividend_, uint128_t divisor_, uint128_t &quot_) {
// assert(divisor != uint128_t(0));
quot_ = uint128_t(0);
if (cmp(dividend_, divisor_)) return dividend_;
if (dividend_.high_ == 0) { // (0,x) / ???
quot_.low_ = dividend_.low_ / divisor_.low_;
dividend_.low_ %= divisor_.low_;
return dividend_;
}
auto zend_ = clz_helper<uint128_t>::clz(dividend_), zsor_ = clz_helper<uint128_t>::clz(divisor_);
if (zend_ > zsor_) return dividend_;
for (zsor_ -= zend_, divisor_ <<= zsor_;; divisor_ >>= 1, quot_ <<= 1) {
if (dividend_ >= divisor_) {
dividend_ -= divisor_;
quot_ |= uint128_t(1);
}
if (!zsor_--) return dividend_;
}
}
static uint128_t div(uint128_t dividend_, uint128_t divisor_) {
if (!divisor_) return {!!dividend_ / !!divisor_}; // raise signal SIGFPE
uint128_t quot_(0);
slow_div_(dividend_, divisor_, quot_);
return quot_;
}
static int128_t div(int128_t dividend_, int128_t divisor_) {
bool nneg_ = dividend_.high_ < 0, dneg_ = divisor_.high_ < 0;
auto res_ = div(uint128_t(nneg_ ? -dividend_ : dividend_), uint128_t(dneg_ ? -divisor_ : divisor_));
return int128_t(nneg_ ^ dneg_ ? -res_ : res_);
}
static uint128_t mod(uint128_t dividend_, uint128_t divisor_) {
if (!divisor_) return {!!dividend_ % !!divisor_}; // raise signal SIGFPE
uint128_t quot_(0);
return slow_div_(dividend_, divisor_, quot_);
}
static int128_t mod(int128_t dividend_, int128_t divisor_) {
bool neg_ = dividend_.high_ < 0;
auto res_ = mod(uint128_t(neg_ ? -dividend_ : dividend_),
uint128_t(divisor_.high_ < 0 ? -divisor_ : divisor_));
return int128_t(neg_ ? -res_ : res_);
}
static void part_div(uint128_t value_, uint64_t div_, uint64_t &high_, uint64_t &mid_, uint64_t &low_) {
uint128_t hh_(0), md_(0);
low_ = static_cast<uint64_t>(slow_div_(value_, div_, md_));
mid_ = static_cast<uint64_t>(slow_div_(md_, div_, hh_));
high_ = static_cast<uint64_t>(hh_);
}
template<class _Tp>
constexpr static _Tp cast_to_float(uint128_t val_) { return std::ldexp(_Tp(val_.high_), 64) + _Tp(val_.low_); }
template<class _Tp>
constexpr static _Tp cast_to_float(int128_t val_) {
return val_.high_ < 0 ? -cast_to_float<_Tp>(uint128_t(-val_)) : cast_to_float<_Tp>(uint128_t(val_));
}
};
#if __SIZEOF_INT128__ == 16
template<>
struct detail_delegate<true> {
typedef __int128 ti_int_;
typedef unsigned __int128 tu_int_;
static constexpr ti_int_ to_native(int128_t val_) { return static_cast<ti_int_>(val_); }
static constexpr tu_int_ to_native(uint128_t val_) { return static_cast<tu_int_>(val_); }
static constexpr int128_t from_native(ti_int_ val_) { return int128_t(val_); }
static constexpr uint128_t from_native(tu_int_ val_) { return uint128_t(val_); }
template<class _Hi, class _Low>
static constexpr bool cmp(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return to_native(lhs_) < to_native(rhs_);
}
static constexpr uint128_t shr(uint128_t lhs_, unsigned rhs_) {
return from_native(to_native(lhs_) >> static_cast<decltype(to_native(lhs_))>(rhs_));
}
static constexpr int128_t sar(int128_t lhs_, unsigned rhs_) {
return from_native(to_native(lhs_) >> static_cast<decltype(to_native(lhs_))>(rhs_)); // NOLINT signed shift
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> imul(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return from_native(to_native(lhs_) * to_native(rhs_));
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> shl(int128_base<_Hi, _Low> lhs_, unsigned rhs_) {
return from_native(to_native(lhs_) << static_cast<decltype(to_native(lhs_))>(rhs_)); // NOLINT signed shift
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> div(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return from_native(to_native(lhs_) / to_native(rhs_));
}
template<class _Hi, class _Low>
static constexpr int128_base<_Hi, _Low> mod(int128_base<_Hi, _Low> lhs_, int128_base<_Hi, _Low> rhs_) {
return from_native(to_native(lhs_) % to_native(rhs_));
}
static void part_div(uint128_t value_, uint64_t div_, uint64_t &high_, uint64_t &mid_, uint64_t &low_) {
// on some cpu, compiler won't do optimize for us
auto vv_ = to_native(value_);
auto rest_ = vv_ / div_;
low_ = static_cast<uint64_t>(vv_) - div_ * static_cast<uint64_t>(rest_);
high_ = static_cast<uint64_t>(rest_ / div_);
mid_ = static_cast<uint64_t>(rest_) - div_ * high_;
}
template<class _Tp, class _Hi, class _Low>
static constexpr _Tp cast_to_float(int128_base<_Hi, _Low> value_) {
return static_cast<_Tp>(to_native(value_));
};
};
#endif
constexpr bool operator<(int128_t lhs_, int128_t rhs_) { return detail_delegate<>::cmp(lhs_, rhs_); }
constexpr bool operator<(uint128_t lhs_, uint128_t rhs_) { return detail_delegate<>::cmp(lhs_, rhs_); }
constexpr uint128_t operator>>(uint128_t lhs_, int rhs_) {
return detail_delegate<>::shr(lhs_, static_cast<unsigned>(rhs_));
}
constexpr int128_t operator>>(int128_t lhs_, int rhs_) {
return detail_delegate<>::sar(lhs_, static_cast<unsigned>(rhs_));
}
constexpr int128_t operator*(int128_t lhs_, int128_t rhs_) { return detail_delegate<>::imul(lhs_, rhs_); }
constexpr uint128_t operator*(uint128_t lhs_, uint128_t rhs_) { return detail_delegate<>::imul(lhs_, rhs_); }
constexpr uint128_t operator<<(uint128_t lhs_, int rhs_) {
return detail_delegate<>::shl(lhs_, static_cast<unsigned>(rhs_));
}
constexpr int128_t operator<<(int128_t lhs_, int rhs_) {
return detail_delegate<>::shl(lhs_, static_cast<unsigned>(rhs_));
}
inline uint128_t operator/(uint128_t lhs_, uint128_t rhs_) { return detail_delegate<>::div(lhs_, rhs_); };
inline int128_t operator/(int128_t lhs_, int128_t rhs_) { return detail_delegate<>::div(lhs_, rhs_); };
inline uint128_t operator%(uint128_t lhs_, uint128_t rhs_) { return detail_delegate<>::mod(lhs_, rhs_); };
inline int128_t operator%(int128_t lhs_, int128_t rhs_) { return detail_delegate<>::mod(lhs_, rhs_); }
template<class _Hi, class _Low>
template<class _Tp>
constexpr _Tp int128_base<_Hi, _Low>::cast_to_float() const {
return detail_delegate<>::cast_to_float<_Tp>(*this);
}
template<class _CharT, class _Traits>
inline std::basic_ostream<_CharT, _Traits> &
print_value(std::basic_ostream<_CharT, _Traits> &out_, bool signed_integral_, uint128_t value_) {
constexpr std::size_t buf_size_ = 45;
typename std::basic_ostream<_CharT, _Traits>::sentry sentry_(out_);
if (!sentry_) return out_;
auto flags_ = out_.flags(), base_flag_ = flags_ & std::ios::basefield;
auto adjust_field_ = flags_ & std::ios::adjustfield;
auto show_base_ = bool(flags_ & std::ios::showbase); // work not dec
auto show_pos_ = bool(flags_ & std::ios::showpos); // work only dec
auto upper_case_ = bool(flags_ & std::ios::uppercase); // work only hex
auto ns_ = out_.width(0);
auto fl_ = out_.fill();
char buf_[buf_size_];
char const *prefix_ = nullptr;
int offset_ = 0;
switch (base_flag_) {
case std::ios::hex: {
if (show_base_ && value_) prefix_ = upper_case_ ? "0X" : "0x";
if (value_ >> 64) {
offset_ = snprintf(buf_, buf_size_,
upper_case_ ? "%" PRIX64 "%016" PRIX64 : "%" PRIx64 "%016" PRIx64,
(uint64_t) (value_ >> 64), (uint64_t) value_);
} else {
offset_ = snprintf(buf_, buf_size_,
upper_case_ ? "%" PRIX64 : "%" PRIx64, (uint64_t) value_);
}
break;
}
case std::ios::oct: {
constexpr uint64_t mask_ = (UINT64_C(1) << 63U) - 1;
if (show_base_ && value_) buf_[offset_++] = '0';
auto x_ = (uint64_t) (value_ >> 126U);
auto y_ = (uint64_t) (value_ >> 63U) & mask_;
auto z_ = (uint64_t) (value_) & mask_;
if (x_) {
offset_ += snprintf(buf_ + offset_, buf_size_ - offset_, "%" PRIo64 "%021" PRIo64 "%021" PRIo64,
x_, y_, z_);
} else if (y_) {
offset_ += snprintf(buf_ + offset_, buf_size_ - offset_, "%" PRIo64 "%021" PRIo64, y_, z_);
} else {
offset_ += snprintf(buf_ + offset_, buf_size_ - offset_, "%" PRIo64, z_);
}
break;
}
default: {
if (signed_integral_) {
if (value_ >> 127) { // negative
prefix_ = "-";
value_ = -value_;
} else if (show_pos_) {
prefix_ = "+";
}
}
uint64_t high_, mid_, low_;
detail_delegate<>::part_div(value_, UINT64_C(10000000000000000000), high_, mid_, low_);
if (high_) {
offset_ = snprintf(buf_, buf_size_, "%" PRIu64 "%019" PRIu64 "%019" PRIu64,
high_, mid_, low_);
} else if (mid_) {
offset_ = snprintf(buf_, buf_size_, "%" PRIu64 "%019" PRIu64,
mid_, low_);
} else {
offset_ = snprintf(buf_, buf_size_, "%" PRIu64, low_);
}
break;
}
}
_CharT o_[2 * buf_size_ - 3];
_CharT *os_;
_CharT *op_; // prefix here
_CharT *oe_ = o_ + (sizeof(o_) / sizeof(o_[0])); // end of output
auto loc_ = out_.getloc();
auto &ct_ = std::use_facet<std::ctype<_CharT> >(loc_);
auto &npt_ = std::use_facet<std::numpunct<_CharT> >(loc_);
std::string grouping_ = npt_.grouping();
// no worry group is not empty
auto limit_ = grouping_.size();
if (limit_ == 0) {
op_ = oe_ - offset_;
ct_.widen(buf_, buf_ + offset_, op_);
} else {
auto thousands_sep_ = npt_.thousands_sep();
decltype(limit_) dg_ = 0;
auto cnt_ = static_cast<unsigned char>(grouping_[dg_]);
unsigned char dc_ = 0;
--limit_;
op_ = oe_;
for (char *p_ = buf_ + offset_; p_ != buf_; ++dc_) {
if (cnt_ > 0 && dc_ == cnt_) {
*--op_ = thousands_sep_;
dc_ = 0;
if (dg_ < limit_) cnt_ = static_cast<unsigned char>(grouping_[++dg_]);
}
*--op_ = ct_.widen(*--p_);
}
}
if (prefix_) {
auto prefix_len_ = strlen(prefix_);
os_ = op_ - prefix_len_;
ct_.widen(prefix_, prefix_ + prefix_len_, os_);
} else {
os_ = op_;
}
auto sz_ = static_cast<std::streamsize>(oe_ - os_);
// assert(sz_ <= (sizeof(o_) / sizeof(o_[0])));
if (ns_ > sz_) {
ns_ -= sz_;
std::basic_string<_CharT, _Traits> sp_(ns_, fl_);
switch (adjust_field_) {
case std::ios::left:
return out_.write(os_, sz_).write(sp_.data(), ns_);
case std::ios::internal:
return out_.write(os_, static_cast<std::streamsize>(op_ - os_))
.write(sp_.data(), ns_)
.write(op_, static_cast<std::streamsize>(oe_ - op_));
default:
return out_.write(sp_.data(), ns_).write(os_, sz_);
}
}
return out_.write(os_, sz_);
}
template<class _CharT, class _Traits>
inline std::basic_ostream<_CharT, _Traits> &operator<<(std::basic_ostream<_CharT, _Traits> &out, uint128_t _Val) {
return print_value(out, false, _Val);
}
template<class _CharT, class _Traits>
inline std::basic_ostream<_CharT, _Traits> &operator<<(std::basic_ostream<_CharT, _Traits> &out, int128_t _Val) {
return print_value(out, true, uint128_t(_Val));
}
}
#ifdef INT128_SPECIALIZATION
namespace std {
#pragma push_macro("MAKE_TYPE")
#define MAKE_TYPE(outter, inner, parent) \
template<> struct outter<large_int::inner> : std::parent {}; \
template<> struct outter<const large_int::inner> : std::parent {}; \
template<> struct outter<volatile large_int::inner> : std::parent {}; \
template<> struct outter<const volatile large_int::inner> : std::parent {};
MAKE_TYPE(is_integral, uint128_t, true_type)
MAKE_TYPE(is_integral, int128_t, true_type)
MAKE_TYPE(is_signed, uint128_t, false_type)
MAKE_TYPE(is_signed, int128_t, true_type)
#undef MAKE_TYPE
#define MAKE_TYPE(outter, inner, target) \
template<> struct outter<large_int::inner> { typedef large_int::target type; }; \
template<> struct outter<const large_int::inner> { typedef const large_int::target type; }; \
template<> struct outter<volatile large_int::inner> { typedef volatile large_int::target type; }; \
template<> struct outter<const volatile large_int::inner> { typedef const volatile large_int::target type; };
MAKE_TYPE(make_signed, uint128_t, int128_t)
MAKE_TYPE(make_unsigned, int128_t, uint128_t)
#pragma pop_macro("MAKE_TYPE")
template<class _Hi, class _Low>
struct numeric_limits<large_int::int128_base<_Hi, _Low> > {
private:
typedef large_int::int128_base<_Hi, _Low> _Tp;
public:
static constexpr const bool is_specialized = true;
static constexpr const bool is_signed = numeric_limits<_Hi>::is_signed;
static constexpr const bool is_integer = true;
static constexpr const bool is_exact = true;
static constexpr const bool has_infinity = false;
static constexpr const bool has_quiet_NaN = false;
static constexpr const bool has_signaling_NaN = false;
static constexpr const std::float_denorm_style has_denorm = std::denorm_absent;
static constexpr const bool has_denorm_loss = false;
static constexpr const std::float_round_style round_style = std::round_toward_zero;
static constexpr const bool is_iec559 = false;
static constexpr const bool is_bounded = true;
static constexpr const bool is_modulo = numeric_limits<_Hi>::is_modulo;
static constexpr const int digits = static_cast<int>(sizeof(_Tp) * 8 - is_signed);
static constexpr const int digits10 = digits * 3 / 10;
static constexpr const int max_digits10 = 0;
static constexpr const int radix = 2;
static constexpr const int min_exponent = 0;
static constexpr const int min_exponent10 = 0;
static constexpr const int max_exponent = 0;
static constexpr const int max_exponent10 = 0;
static constexpr const bool traps = numeric_limits<_Hi>::traps;
static constexpr const bool tinyness_before = false;
static constexpr _Tp min() { return is_signed ? _Tp(1) << digits : _Tp(0); }
static constexpr _Tp lowest() { return min(); }
static constexpr _Tp max() { return ~min(); }
static constexpr _Tp epsilon() { return _Tp(0); }
static constexpr _Tp round_error() { return _Tp(0); }
static constexpr _Tp infinity() { return _Tp(0); }
static constexpr _Tp quiet_NaN() { return _Tp(0); }
static constexpr _Tp signaling_NaN() { return _Tp(0); }
static constexpr _Tp denorm_min() { return _Tp(0); }
};
}
#endif /* INT128_SPECIALIZATION */
#ifndef INT128_NO_EXPORT
#define INT128_C(val) val##_L128
#define UINT128_C(val) val##_U128
// add space between "" and suffix identifier, or may compile failed
using namespace large_int::literals;
using large_int::uint128_t;
using large_int::int128_t;
#endif /* INT128_NO_EXPORT */