aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchertus <azuikov@ydb.tech>2022-11-23 16:16:44 +0300
committerchertus <azuikov@ydb.tech>2022-11-23 16:16:44 +0300
commit742ef5b7c36c67b958b5eac7ddfd019ddae39ec1 (patch)
treeddcb8c275fbfb70b6d944e72883ec8973cf45772
parent837e447ad5eca521acde76ff230a27a8d406acaa (diff)
downloadydb-742ef5b7c36c67b958b5eac7ddfd019ddae39ec1.tar.gz
remove UDF dependency
-rw-r--r--ydb/library/arrow_clickhouse/CMakeLists.txt5
-rw-r--r--ydb/library/arrow_clickhouse/Columns/CMakeLists.txt2
-rw-r--r--ydb/library/arrow_clickhouse/Common/Allocator.cpp6
-rw-r--r--ydb/library/arrow_clickhouse/Common/Allocator.h7
-rw-r--r--ydb/library/arrow_clickhouse/Common/Arena.h2
-rw-r--r--ydb/library/arrow_clickhouse/Common/CMakeLists.txt2
-rw-r--r--ydb/library/arrow_clickhouse/Common/PODArray.h3
-rw-r--r--ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt2
-rw-r--r--ydb/library/arrow_clickhouse/arrow_clickhouse_types.h4
-rw-r--r--ydb/library/arrow_clickhouse/base/common/StringRef.h204
-rw-r--r--ydb/library/arrow_clickhouse/base/common/defines.h124
-rw-r--r--ydb/library/arrow_clickhouse/base/common/extended_types.h118
-rw-r--r--ydb/library/arrow_clickhouse/base/common/mremap.cpp40
-rw-r--r--ydb/library/arrow_clickhouse/base/common/mremap.h85
-rw-r--r--ydb/library/arrow_clickhouse/base/common/types.h31
-rw-r--r--ydb/library/arrow_clickhouse/base/common/unaligned.h29
-rw-r--r--ydb/library/arrow_clickhouse/base/common/wide_integer.h260
-rw-r--r--ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h1455
-rw-r--r--ydb/library/yql/public/udf/udfs_exports.exports4
19 files changed, 2368 insertions, 15 deletions
diff --git a/ydb/library/arrow_clickhouse/CMakeLists.txt b/ydb/library/arrow_clickhouse/CMakeLists.txt
index afe895bcb27..b625dac455e 100644
--- a/ydb/library/arrow_clickhouse/CMakeLists.txt
+++ b/ydb/library/arrow_clickhouse/CMakeLists.txt
@@ -13,7 +13,7 @@ add_subdirectory(ut)
add_library(ydb-library-arrow_clickhouse)
target_include_directories(ydb-library-arrow_clickhouse PUBLIC
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base
+ ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base
)
target_include_directories(ydb-library-arrow_clickhouse PRIVATE
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse
@@ -29,6 +29,5 @@ target_link_libraries(ydb-library-arrow_clickhouse PUBLIC
target_sources(ydb-library-arrow_clickhouse PRIVATE
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/AggregateFunctions/IAggregateFunction.cpp
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/Aggregator.cpp
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base/common/mremap.cpp
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base/common/getPageSize.cpp
+ ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base/common/mremap.cpp
)
diff --git a/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt b/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt
index 0f4c153a983..7fb88cc9cce 100644
--- a/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt
+++ b/ydb/library/arrow_clickhouse/Columns/CMakeLists.txt
@@ -9,7 +9,7 @@
add_library(library-arrow_clickhouse-Columns)
target_include_directories(library-arrow_clickhouse-Columns PRIVATE
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base
+ ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse
)
target_link_libraries(library-arrow_clickhouse-Columns PUBLIC
diff --git a/ydb/library/arrow_clickhouse/Common/Allocator.cpp b/ydb/library/arrow_clickhouse/Common/Allocator.cpp
index 96a2ae6ad0a..39478230c36 100644
--- a/ydb/library/arrow_clickhouse/Common/Allocator.cpp
+++ b/ydb/library/arrow_clickhouse/Common/Allocator.cpp
@@ -3,10 +3,16 @@
// See: https://github.com/ClickHouse/ClickHouse/
#include <Common/Allocator.h>
+#include <unistd.h>
namespace CH
{
+int64_t getPageSize()
+{
+ return sysconf(_SC_PAGESIZE);
+}
+
/** Keep definition of this constant in cpp file; otherwise its value
* is inlined into allocator code making it impossible to override it
* in third-party code.
diff --git a/ydb/library/arrow_clickhouse/Common/Allocator.h b/ydb/library/arrow_clickhouse/Common/Allocator.h
index 46fcc75719a..f9902cee159 100644
--- a/ydb/library/arrow_clickhouse/Common/Allocator.h
+++ b/ydb/library/arrow_clickhouse/Common/Allocator.h
@@ -24,7 +24,7 @@
#include <common/mremap.h>
-#include <common/getPageSize.h>
+//#include <common/getPageSize.h>
#include <Common/Allocator_fwd.h>
@@ -36,6 +36,9 @@
namespace CH
{
+/// Get memory page size
+int64_t getPageSize();
+
/**
* Many modern allocators (for example, tcmalloc) do not do a mremap for
* realloc, even in case of large enough chunks of memory. Although this allows
@@ -175,7 +178,7 @@ private:
void * allocNoTrack(size_t size, size_t alignment)
{
void * buf;
- size_t mmap_min_alignment = ::getPageSize();
+ size_t mmap_min_alignment = getPageSize();
if (size >= MMAP_THRESHOLD)
{
diff --git a/ydb/library/arrow_clickhouse/Common/Arena.h b/ydb/library/arrow_clickhouse/Common/Arena.h
index 7f86e70e5ae..1633a29ad7a 100644
--- a/ydb/library/arrow_clickhouse/Common/Arena.h
+++ b/ydb/library/arrow_clickhouse/Common/Arena.h
@@ -119,7 +119,7 @@ public:
explicit Arena(size_t initial_size_ = 4096, size_t growth_factor_ = 2, size_t linear_growth_threshold_ = 128 * 1024 * 1024)
: growth_factor(growth_factor_), linear_growth_threshold(linear_growth_threshold_),
head(new MemoryChunk(initial_size_, nullptr)), size_in_bytes(head->size()),
- page_size(static_cast<size_t>(::getPageSize()))
+ page_size(static_cast<size_t>(getPageSize()))
{
}
diff --git a/ydb/library/arrow_clickhouse/Common/CMakeLists.txt b/ydb/library/arrow_clickhouse/Common/CMakeLists.txt
index a455b526c0b..d792835d45e 100644
--- a/ydb/library/arrow_clickhouse/Common/CMakeLists.txt
+++ b/ydb/library/arrow_clickhouse/Common/CMakeLists.txt
@@ -9,7 +9,7 @@
add_library(library-arrow_clickhouse-Common)
target_include_directories(library-arrow_clickhouse-Common PRIVATE
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base
+ ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse
)
target_link_libraries(library-arrow_clickhouse-Common PUBLIC
diff --git a/ydb/library/arrow_clickhouse/Common/PODArray.h b/ydb/library/arrow_clickhouse/Common/PODArray.h
index 45924b02225..cc398c67cd6 100644
--- a/ydb/library/arrow_clickhouse/Common/PODArray.h
+++ b/ydb/library/arrow_clickhouse/Common/PODArray.h
@@ -12,8 +12,7 @@
#include <memory>
//#include <boost/noncopyable.hpp>
-
-#include <common/strong_typedef.h>
+//#include <common/strong_typedef.h>
#include <Common/Allocator.h>
#include <Common/memcpySmall.h>
diff --git a/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt b/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt
index a150b18b741..e4db05eb18e 100644
--- a/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt
+++ b/ydb/library/arrow_clickhouse/DataStreams/CMakeLists.txt
@@ -9,7 +9,7 @@
add_library(library-arrow_clickhouse-DataStreams)
target_include_directories(library-arrow_clickhouse-DataStreams PRIVATE
- ${CMAKE_SOURCE_DIR}/ydb/library/yql/udfs/common/clickhouse/client/base
+ ${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse/base
${CMAKE_SOURCE_DIR}/ydb/library/arrow_clickhouse
)
target_link_libraries(library-arrow_clickhouse-DataStreams PUBLIC
diff --git a/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h b/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h
index f88eac64dd8..9142899fb14 100644
--- a/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h
+++ b/ydb/library/arrow_clickhouse/arrow_clickhouse_types.h
@@ -20,10 +20,6 @@
namespace CH
{
-using NDB::StringRef;
-using NDB::StringRefHash;
-using NDB::StringRefs;
-
/// What to do if the limit is exceeded.
enum class OverflowMode
{
diff --git a/ydb/library/arrow_clickhouse/base/common/StringRef.h b/ydb/library/arrow_clickhouse/base/common/StringRef.h
new file mode 100644
index 00000000000..d6fccd2522e
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/StringRef.h
@@ -0,0 +1,204 @@
+#pragma once
+
+#include <cassert>
+#include <stdexcept> // for std::logic_error
+#include <string>
+#include <vector>
+#include <functional>
+#include <iosfwd>
+
+#include <common/types.h>
+#include <common/unaligned.h>
+
+#if defined(__SSE2__)
+ #include <emmintrin.h>
+#endif
+
+#if defined(__SSE4_2__)
+ #include <smmintrin.h>
+ #include <nmmintrin.h>
+#endif
+
+namespace CH
+{
+
+/**
+ * The std::string_view-like container to avoid creating strings to find substrings in the hash table.
+ */
+struct StringRef
+{
+ const char * data = nullptr;
+ size_t size = 0;
+
+ /// Non-constexpr due to reinterpret_cast.
+ template <typename CharT, typename = std::enable_if_t<sizeof(CharT) == 1>>
+ StringRef(const CharT * data_, size_t size_) : data(reinterpret_cast<const char *>(data_)), size(size_)
+ {
+ /// Sanity check for overflowed values.
+ assert(size < 0x8000000000000000ULL);
+ }
+
+ constexpr StringRef(const char * data_, size_t size_) : data(data_), size(size_) {}
+
+ StringRef(const std::string & s) : data(s.data()), size(s.size()) {}
+ constexpr explicit StringRef(std::string_view s) : data(s.data()), size(s.size()) {}
+ constexpr StringRef(const char * data_) : StringRef(std::string_view{data_}) {}
+ constexpr StringRef() = default;
+
+ std::string toString() const { return std::string(data, size); }
+
+ explicit operator std::string() const { return toString(); }
+ constexpr explicit operator std::string_view() const { return {data, size}; }
+};
+
+/// Here constexpr doesn't implicate inline, see https://www.viva64.com/en/w/v1043/
+/// nullptr can't be used because the StringRef values are used in SipHash's pointer arithmetic
+/// and the UBSan thinks that something like nullptr + 8 is UB.
+constexpr const inline char empty_string_ref_addr{};
+constexpr const inline StringRef EMPTY_STRING_REF{&empty_string_ref_addr, 0};
+
+using StringRefs = std::vector<StringRef>;
+
+
+#if defined(__SSE2__)
+
+/** Compare strings for equality.
+ * The approach is controversial and does not win in all cases.
+ * For more information, see hash_map_string_2.cpp
+ */
+
+inline bool compareSSE2(const char * p1, const char * p2)
+{
+ return 0xFFFF == _mm_movemask_epi8(_mm_cmpeq_epi8(
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1)),
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2))));
+}
+
+inline bool compareSSE2x4(const char * p1, const char * p2)
+{
+ return 0xFFFF == _mm_movemask_epi8(
+ _mm_and_si128(
+ _mm_and_si128(
+ _mm_cmpeq_epi8(
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1)),
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2))),
+ _mm_cmpeq_epi8(
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 1),
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 1))),
+ _mm_and_si128(
+ _mm_cmpeq_epi8(
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 2),
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 2)),
+ _mm_cmpeq_epi8(
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p1) + 3),
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(p2) + 3)))));
+}
+
+inline bool memequalSSE2Wide(const char * p1, const char * p2, size_t size)
+{
+ while (size >= 64)
+ {
+ if (compareSSE2x4(p1, p2))
+ {
+ p1 += 64;
+ p2 += 64;
+ size -= 64;
+ }
+ else
+ return false;
+ }
+
+ switch ((size % 64) / 16)
+ {
+ case 3: if (!compareSSE2(p1 + 32, p2 + 32)) return false; [[fallthrough]];
+ case 2: if (!compareSSE2(p1 + 16, p2 + 16)) return false; [[fallthrough]];
+ case 1: if (!compareSSE2(p1 , p2 )) return false; [[fallthrough]];
+ case 0: break;
+ }
+
+ p1 += (size % 64) / 16 * 16;
+ p2 += (size % 64) / 16 * 16;
+
+ switch (size % 16)
+ {
+ case 15: if (p1[14] != p2[14]) return false; [[fallthrough]];
+ case 14: if (p1[13] != p2[13]) return false; [[fallthrough]];
+ case 13: if (p1[12] != p2[12]) return false; [[fallthrough]];
+ case 12: if (unalignedLoad<uint32_t>(p1 + 8) == unalignedLoad<uint32_t>(p2 + 8)) goto l8; else return false;
+ case 11: if (p1[10] != p2[10]) return false; [[fallthrough]];
+ case 10: if (p1[9] != p2[9]) return false; [[fallthrough]];
+ case 9: if (p1[8] != p2[8]) return false;
+ l8: [[fallthrough]];
+ case 8: return unalignedLoad<uint64_t>(p1) == unalignedLoad<uint64_t>(p2);
+ case 7: if (p1[6] != p2[6]) return false; [[fallthrough]];
+ case 6: if (p1[5] != p2[5]) return false; [[fallthrough]];
+ case 5: if (p1[4] != p2[4]) return false; [[fallthrough]];
+ case 4: return unalignedLoad<uint32_t>(p1) == unalignedLoad<uint32_t>(p2);
+ case 3: if (p1[2] != p2[2]) return false; [[fallthrough]];
+ case 2: return unalignedLoad<uint16_t>(p1) == unalignedLoad<uint16_t>(p2);
+ case 1: if (p1[0] != p2[0]) return false; [[fallthrough]];
+ case 0: break;
+ }
+
+ return true;
+}
+
+#endif
+
+
+inline bool operator== (StringRef lhs, StringRef rhs)
+{
+ if (lhs.size != rhs.size)
+ return false;
+
+ if (lhs.size == 0)
+ return true;
+
+#if defined(__SSE2__)
+ return memequalSSE2Wide(lhs.data, rhs.data, lhs.size);
+#else
+ return 0 == memcmp(lhs.data, rhs.data, lhs.size);
+#endif
+}
+
+inline bool operator!= (StringRef lhs, StringRef rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator< (StringRef lhs, StringRef rhs)
+{
+ int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size));
+ return cmp < 0 || (cmp == 0 && lhs.size < rhs.size);
+}
+
+inline bool operator> (StringRef lhs, StringRef rhs)
+{
+ int cmp = memcmp(lhs.data, rhs.data, std::min(lhs.size, rhs.size));
+ return cmp > 0 || (cmp == 0 && lhs.size > rhs.size);
+}
+
+struct StringRefHash {
+ size_t operator() (StringRef x) const
+ {
+ return std::hash<std::string_view>()(std::string_view(x));
+ }
+};
+
+}
+
+namespace std
+{
+ template <>
+ struct hash<CH::StringRef> : public CH::StringRefHash {};
+}
+
+
+namespace ZeroTraits
+{
+ inline bool check(const CH::StringRef & x) { return 0 == x.size; }
+ inline void set(CH::StringRef & x) { x.size = 0; }
+}
+
+
+std::ostream & operator<<(std::ostream & os, const CH::StringRef & str);
diff --git a/ydb/library/arrow_clickhouse/base/common/defines.h b/ydb/library/arrow_clickhouse/base/common/defines.h
new file mode 100644
index 00000000000..ada8245f494
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/defines.h
@@ -0,0 +1,124 @@
+#pragma once
+
+/// __has_feature supported only by clang.
+///
+/// But libcxx/libcxxabi overrides it to 0,
+/// thus the checks for __has_feature will be wrong.
+///
+/// NOTE:
+/// - __has_feature cannot be simply undefined,
+/// since this will be broken if some C++ header will be included after
+/// including <common/defines.h>
+/// - it should not have fallback to 0,
+/// since this may create false-positive detection (common problem)
+#if defined(__clang__) && defined(__has_feature)
+# define ch_has_feature __has_feature
+#endif
+
+#if defined(_MSC_VER)
+# if !defined(likely)
+# define likely(x) (x)
+# endif
+# if !defined(unlikely)
+# define unlikely(x) (x)
+# endif
+#else
+# if !defined(likely)
+# define likely(x) (__builtin_expect(!!(x), 1))
+# endif
+# if !defined(unlikely)
+# define unlikely(x) (__builtin_expect(!!(x), 0))
+# endif
+#endif
+
+#if defined(_MSC_VER)
+# define ALWAYS_INLINE __forceinline
+# define NO_INLINE static __declspec(noinline)
+# define MAY_ALIAS
+#else
+# define ALWAYS_INLINE __attribute__((__always_inline__))
+# define NO_INLINE __attribute__((__noinline__))
+# define MAY_ALIAS __attribute__((__may_alias__))
+#endif
+
+#if !defined(__x86_64__) && !defined(__aarch64__) && !defined(__PPC__)
+# error "The only supported platforms are x86_64 and AArch64, PowerPC (work in progress)"
+#endif
+
+/// Check for presence of address sanitizer
+#if !defined(ADDRESS_SANITIZER)
+# if defined(ch_has_feature)
+# if ch_has_feature(address_sanitizer)
+# define ADDRESS_SANITIZER 1
+# endif
+# elif defined(__SANITIZE_ADDRESS__)
+# define ADDRESS_SANITIZER 1
+# endif
+#endif
+
+#if !defined(THREAD_SANITIZER)
+# if defined(ch_has_feature)
+# if ch_has_feature(thread_sanitizer)
+# define THREAD_SANITIZER 1
+# endif
+# elif defined(__SANITIZE_THREAD__)
+# define THREAD_SANITIZER 1
+# endif
+#endif
+
+#if !defined(MEMORY_SANITIZER)
+# if defined(ch_has_feature)
+# if ch_has_feature(memory_sanitizer)
+# define MEMORY_SANITIZER 1
+# endif
+# elif defined(__MEMORY_SANITIZER__)
+# define MEMORY_SANITIZER 1
+# endif
+#endif
+
+#if !defined(UNDEFINED_BEHAVIOR_SANITIZER)
+# if defined(__has_feature)
+# if __has_feature(undefined_behavior_sanitizer)
+# define UNDEFINED_BEHAVIOR_SANITIZER 1
+# endif
+# elif defined(__UNDEFINED_BEHAVIOR_SANITIZER__)
+# define UNDEFINED_BEHAVIOR_SANITIZER 1
+# endif
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+# define BOOST_USE_ASAN 1
+# define BOOST_USE_UCONTEXT 1
+#endif
+
+#if defined(THREAD_SANITIZER)
+# define BOOST_USE_TSAN 1
+# define BOOST_USE_UCONTEXT 1
+#endif
+
+#if defined(ARCADIA_BUILD) && defined(BOOST_USE_UCONTEXT)
+# undef BOOST_USE_UCONTEXT
+#endif
+
+/// TODO: Strange enough, there is no way to detect UB sanitizer.
+
+/// Explicitly allow undefined behaviour for certain functions. Use it as a function attribute.
+/// It is useful in case when compiler cannot see (and exploit) it, but UBSan can.
+/// Example: multiplication of signed integers with possibility of overflow when both sides are from user input.
+#if defined(__clang__)
+# define NO_SANITIZE_UNDEFINED __attribute__((__no_sanitize__("undefined")))
+# define NO_SANITIZE_ADDRESS __attribute__((__no_sanitize__("address")))
+# define NO_SANITIZE_THREAD __attribute__((__no_sanitize__("thread")))
+# define ALWAYS_INLINE_NO_SANITIZE_UNDEFINED __attribute__((__always_inline__, __no_sanitize__("undefined")))
+#else /// It does not work in GCC. GCC 7 cannot recognize this attribute and GCC 8 simply ignores it.
+# define NO_SANITIZE_UNDEFINED
+# define NO_SANITIZE_ADDRESS
+# define NO_SANITIZE_THREAD
+# define ALWAYS_INLINE_NO_SANITIZE_UNDEFINED ALWAYS_INLINE
+#endif
+
+/// A template function for suppressing warnings about unused variables or function results.
+template <typename... Args>
+constexpr void UNUSED(Args &&... args [[maybe_unused]])
+{
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/extended_types.h b/ydb/library/arrow_clickhouse/base/common/extended_types.h
new file mode 100644
index 00000000000..e7b2cd7f352
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/extended_types.h
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <type_traits>
+
+#include <common/types.h>
+#include <common/wide_integer.h>
+
+namespace CH
+{
+
+using Int128 = wide::integer<128, signed>;
+using UInt128 = wide::integer<128, unsigned>;
+using Int256 = wide::integer<256, signed>;
+using UInt256 = wide::integer<256, unsigned>;
+
+static_assert(sizeof(Int256) == 32);
+static_assert(sizeof(UInt256) == 32);
+
+/// The standard library type traits, such as std::is_arithmetic, with one exception
+/// (std::common_type), are "set in stone". Attempting to specialize them causes undefined behavior.
+/// So instead of using the std type_traits, we use our own version which allows extension.
+template <typename T>
+struct is_signed
+{
+ static constexpr bool value = std::is_signed_v<T>;
+};
+
+template <> struct is_signed<Int128> { static constexpr bool value = true; };
+template <> struct is_signed<Int256> { static constexpr bool value = true; };
+
+template <typename T>
+inline constexpr bool is_signed_v = is_signed<T>::value;
+
+template <typename T>
+struct is_unsigned
+{
+ static constexpr bool value = std::is_unsigned_v<T>;
+};
+
+template <> struct is_unsigned<UInt128> { static constexpr bool value = true; };
+template <> struct is_unsigned<UInt256> { static constexpr bool value = true; };
+
+template <typename T>
+inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
+
+
+/// TODO: is_integral includes char, char8_t and wchar_t.
+template <typename T>
+struct is_integer
+{
+ static constexpr bool value = std::is_integral_v<T>;
+};
+
+template <> struct is_integer<Int128> { static constexpr bool value = true; };
+template <> struct is_integer<UInt128> { static constexpr bool value = true; };
+template <> struct is_integer<Int256> { static constexpr bool value = true; };
+template <> struct is_integer<UInt256> { static constexpr bool value = true; };
+
+template <typename T>
+inline constexpr bool is_integer_v = is_integer<T>::value;
+
+
+template <typename T>
+struct is_arithmetic
+{
+ static constexpr bool value = std::is_arithmetic_v<T>;
+};
+
+template <> struct is_arithmetic<Int128> { static constexpr bool value = true; };
+template <> struct is_arithmetic<UInt128> { static constexpr bool value = true; };
+template <> struct is_arithmetic<Int256> { static constexpr bool value = true; };
+template <> struct is_arithmetic<UInt256> { static constexpr bool value = true; };
+
+
+template <typename T>
+inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
+
+template <typename T>
+struct make_unsigned
+{
+ typedef std::make_unsigned_t<T> type;
+};
+
+template <> struct make_unsigned<Int128> { using type = UInt128; };
+template <> struct make_unsigned<UInt128> { using type = UInt128; };
+template <> struct make_unsigned<Int256> { using type = UInt256; };
+template <> struct make_unsigned<UInt256> { using type = UInt256; };
+
+template <typename T> using make_unsigned_t = typename make_unsigned<T>::type;
+
+template <typename T>
+struct make_signed
+{
+ typedef std::make_signed_t<T> type;
+};
+
+template <> struct make_signed<Int128> { using type = Int128; };
+template <> struct make_signed<UInt128> { using type = Int128; };
+template <> struct make_signed<Int256> { using type = Int256; };
+template <> struct make_signed<UInt256> { using type = Int256; };
+
+template <typename T> using make_signed_t = typename make_signed<T>::type;
+
+template <typename T>
+struct is_big_int
+{
+ static constexpr bool value = false;
+};
+
+template <> struct is_big_int<Int128> { static constexpr bool value = true; };
+template <> struct is_big_int<UInt128> { static constexpr bool value = true; };
+template <> struct is_big_int<Int256> { static constexpr bool value = true; };
+template <> struct is_big_int<UInt256> { static constexpr bool value = true; };
+
+template <typename T>
+inline constexpr bool is_big_int_v = is_big_int<T>::value;
+
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/mremap.cpp b/ydb/library/arrow_clickhouse/base/common/mremap.cpp
new file mode 100644
index 00000000000..d2d8d7fde4f
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/mremap.cpp
@@ -0,0 +1,40 @@
+#include <common/mremap.h>
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <errno.h>
+
+
+void * mremap_fallback(
+ void * old_address, size_t old_size, size_t new_size, int flags, int mmap_prot, int mmap_flags, int mmap_fd, off_t mmap_offset)
+{
+ /// No actual shrink
+ if (new_size < old_size)
+ return old_address;
+
+ if (!(flags & MREMAP_MAYMOVE))
+ {
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+#if defined(_MSC_VER)
+ void * new_address = ::operator new(new_size);
+#else
+ void * new_address = mmap(nullptr, new_size, mmap_prot, mmap_flags, mmap_fd, mmap_offset);
+ if (MAP_FAILED == new_address)
+ return MAP_FAILED;
+#endif
+
+ memcpy(new_address, old_address, old_size);
+
+#if defined(_MSC_VER)
+ delete old_address;
+#else
+ if (munmap(old_address, old_size))
+ abort();
+#endif
+
+ return new_address;
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/mremap.h b/ydb/library/arrow_clickhouse/base/common/mremap.h
new file mode 100644
index 00000000000..f488393278a
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/mremap.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include <cstddef>
+#include <sys/types.h>
+#if !defined(_MSC_VER)
+#include <sys/mman.h>
+#endif
+
+
+#ifdef MREMAP_MAYMOVE
+ #define HAS_MREMAP 1
+#else
+ #define HAS_MREMAP 0
+#endif
+
+
+/// You can forcely disable mremap by defining DISABLE_MREMAP to 1 before including this file.
+#ifndef DISABLE_MREMAP
+ #if HAS_MREMAP
+ #define DISABLE_MREMAP 0
+ #else
+ #define DISABLE_MREMAP 1
+ #endif
+#endif
+
+
+/// Implement mremap with mmap/memcpy/munmap.
+void * mremap_fallback(
+ void * old_address,
+ size_t old_size,
+ size_t new_size,
+ int flags,
+ int mmap_prot,
+ int mmap_flags,
+ int mmap_fd,
+ off_t mmap_offset);
+
+
+#if !HAS_MREMAP
+ #define MREMAP_MAYMOVE 1
+
+ inline void * mremap(
+ void * old_address,
+ size_t old_size,
+ size_t new_size,
+ int flags = 0,
+ int mmap_prot = 0,
+ int mmap_flags = 0,
+ int mmap_fd = -1,
+ off_t mmap_offset = 0)
+ {
+ return mremap_fallback(old_address, old_size, new_size, flags, mmap_prot, mmap_flags, mmap_fd, mmap_offset);
+ }
+#endif
+
+
+inline void * clickhouse_mremap(
+ void * old_address,
+ size_t old_size,
+ size_t new_size,
+ int flags = 0,
+ [[maybe_unused]] int mmap_prot = 0,
+ [[maybe_unused]] int mmap_flags = 0,
+ [[maybe_unused]] int mmap_fd = -1,
+ [[maybe_unused]] off_t mmap_offset = 0)
+{
+#if DISABLE_MREMAP
+ return mremap_fallback(old_address, old_size, new_size, flags, mmap_prot, mmap_flags, mmap_fd, mmap_offset);
+#else
+
+ return mremap(
+ old_address,
+ old_size,
+ new_size,
+ flags
+#if !defined(MREMAP_FIXED)
+ ,
+ mmap_prot,
+ mmap_flags,
+ mmap_fd,
+ mmap_offset
+#endif
+ );
+#endif
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/types.h b/ydb/library/arrow_clickhouse/base/common/types.h
new file mode 100644
index 00000000000..ff04c566797
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/types.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace CH
+{
+
+using Int8 = int8_t;
+using Int16 = int16_t;
+using Int32 = int32_t;
+using Int64 = int64_t;
+
+#ifndef __cpp_char8_t
+using char8_t = unsigned char;
+#endif
+
+/// This is needed for more strict aliasing. https://godbolt.org/z/xpJBSb https://stackoverflow.com/a/57453713
+#if !defined(PVS_STUDIO) /// But PVS-Studio does not treat it correctly.
+using UInt8 = char8_t;
+#else
+using UInt8 = uint8_t;
+#endif
+
+using UInt16 = uint16_t;
+using UInt32 = uint32_t;
+using UInt64 = uint64_t;
+
+using String = std::string;
+
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/unaligned.h b/ydb/library/arrow_clickhouse/base/common/unaligned.h
new file mode 100644
index 00000000000..9119153e2e0
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/unaligned.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <string.h>
+#include <type_traits>
+
+namespace CH
+{
+
+template <typename T>
+inline T unalignedLoad(const void * address)
+{
+ T res {};
+ memcpy(&res, address, sizeof(res));
+ return res;
+}
+
+/// We've had troubles before with wrong store size due to integral promotions
+/// (e.g., unalignedStore(dest, uint16_t + uint16_t) stores an uint32_t).
+/// To prevent this, make the caller specify the stored type explicitly.
+/// To disable deduction of T, wrap the argument type with std::enable_if.
+template <typename T>
+inline void unalignedStore(void * address,
+ const typename std::enable_if<true, T>::type & src)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ memcpy(address, &src, sizeof(src));
+}
+
+}
diff --git a/ydb/library/arrow_clickhouse/base/common/wide_integer.h b/ydb/library/arrow_clickhouse/base/common/wide_integer.h
new file mode 100644
index 00000000000..27f8152923c
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/wide_integer.h
@@ -0,0 +1,260 @@
+#pragma once
+
+///////////////////////////////////////////////////////////////
+// Distributed under the Boost Software License, Version 1.0.
+// (See at http://www.boost.org/LICENSE_1_0.txt)
+///////////////////////////////////////////////////////////////
+
+/* Divide and multiply
+ *
+ *
+ * Copyright (c) 2008
+ * Evan Teran
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appears in all copies and that both the
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the same name not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. We make no representations about the
+ * suitability this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <initializer_list>
+
+namespace CH::wide
+{
+template <size_t Bits, typename Signed>
+class integer;
+}
+
+namespace std
+{
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+struct common_type<CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>>;
+
+template <size_t Bits, typename Signed, typename Arithmetic>
+struct common_type<CH::wide::integer<Bits, Signed>, Arithmetic>;
+
+template <typename Arithmetic, size_t Bits, typename Signed>
+struct common_type<Arithmetic, CH::wide::integer<Bits, Signed>>;
+
+}
+
+namespace CH::wide
+{
+
+template <size_t Bits, typename Signed>
+class integer
+{
+public:
+ using base_type = uint64_t;
+ using signed_base_type = int64_t;
+
+ // ctors
+ constexpr integer() noexcept = default;
+
+ template <typename T>
+ constexpr integer(T rhs) noexcept;
+
+ template <typename T>
+ constexpr integer(std::initializer_list<T> il) noexcept;
+
+ // assignment
+ template <size_t Bits2, typename Signed2>
+ constexpr integer<Bits, Signed> & operator=(const integer<Bits2, Signed2> & rhs) noexcept;
+
+ template <typename Arithmetic>
+ constexpr integer<Bits, Signed> & operator=(Arithmetic rhs) noexcept;
+
+ template <typename Arithmetic>
+ constexpr integer<Bits, Signed> & operator*=(const Arithmetic & rhs);
+
+ template <typename Arithmetic>
+ constexpr integer<Bits, Signed> & operator/=(const Arithmetic & rhs);
+
+ template <typename Arithmetic>
+ constexpr integer<Bits, Signed> & operator+=(const Arithmetic & rhs) noexcept(std::is_same_v<Signed, unsigned>);
+
+ template <typename Arithmetic>
+ constexpr integer<Bits, Signed> & operator-=(const Arithmetic & rhs) noexcept(std::is_same_v<Signed, unsigned>);
+
+ template <typename Integral>
+ constexpr integer<Bits, Signed> & operator%=(const Integral & rhs);
+
+ template <typename Integral>
+ constexpr integer<Bits, Signed> & operator&=(const Integral & rhs) noexcept;
+
+ template <typename Integral>
+ constexpr integer<Bits, Signed> & operator|=(const Integral & rhs) noexcept;
+
+ template <typename Integral>
+ constexpr integer<Bits, Signed> & operator^=(const Integral & rhs) noexcept;
+
+ constexpr integer<Bits, Signed> & operator<<=(int n) noexcept;
+ constexpr integer<Bits, Signed> & operator>>=(int n) noexcept;
+
+ constexpr integer<Bits, Signed> & operator++() noexcept(std::is_same_v<Signed, unsigned>);
+ constexpr integer<Bits, Signed> operator++(int) noexcept(std::is_same_v<Signed, unsigned>);
+ constexpr integer<Bits, Signed> & operator--() noexcept(std::is_same_v<Signed, unsigned>);
+ constexpr integer<Bits, Signed> operator--(int) noexcept(std::is_same_v<Signed, unsigned>);
+
+ // observers
+
+ constexpr explicit operator bool() const noexcept;
+
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>, T>>
+ constexpr operator T() const noexcept;
+
+ constexpr operator long double() const noexcept;
+ constexpr operator double() const noexcept;
+ constexpr operator float() const noexcept;
+
+ struct _impl;
+
+ base_type items[_impl::item_count];
+
+private:
+ template <size_t Bits2, typename Signed2>
+ friend class integer;
+
+ friend class std::numeric_limits<integer<Bits, signed>>;
+ friend class std::numeric_limits<integer<Bits, unsigned>>;
+};
+
+template <typename T>
+static constexpr bool ArithmeticConcept() noexcept;
+
+template <class T1, class T2>
+using _only_arithmetic = typename std::enable_if<ArithmeticConcept<T1>() && ArithmeticConcept<T2>()>::type;
+
+template <typename T>
+static constexpr bool IntegralConcept() noexcept;
+
+template <class T, class T2>
+using _only_integer = typename std::enable_if<IntegralConcept<T>() && IntegralConcept<T2>()>::type;
+
+// Unary operators
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator~(const integer<Bits, Signed> & lhs) noexcept;
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator-(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>);
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator+(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>);
+
+// Binary operators
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator*(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator*(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator/(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator/(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator+(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator+(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator-(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator-(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator%(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>>
+std::common_type_t<Integral, Integral2> constexpr operator%(const Integral & rhs, const Integral2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator&(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>>
+std::common_type_t<Integral, Integral2> constexpr operator&(const Integral & rhs, const Integral2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator|(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>>
+std::common_type_t<Integral, Integral2> constexpr operator|(const Integral & rhs, const Integral2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator^(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Integral, typename Integral2, class = _only_integer<Integral, Integral2>>
+std::common_type_t<Integral, Integral2> constexpr operator^(const Integral & rhs, const Integral2 & lhs);
+
+// TODO: Integral
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, int n) noexcept;
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, int n) noexcept;
+
+template <size_t Bits, typename Signed, typename Int, typename = std::enable_if_t<!std::is_same_v<Int, int>>>
+constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, Int n) noexcept
+{
+ return lhs << int(n);
+}
+template <size_t Bits, typename Signed, typename Int, typename = std::enable_if_t<!std::is_same_v<Int, int>>>
+constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, Int n) noexcept
+{
+ return lhs >> int(n);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator<(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator<(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator>(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator>(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator<=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator<=(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator>=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator>=(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator==(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator==(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator!=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs);
+template <typename Arithmetic, typename Arithmetic2, class = _only_arithmetic<Arithmetic, Arithmetic2>>
+constexpr bool operator!=(const Arithmetic & rhs, const Arithmetic2 & lhs);
+
+}
+
+namespace std
+{
+
+template <size_t Bits, typename Signed>
+struct hash<CH::wide::integer<Bits, Signed>>;
+
+}
+
+#include "wide_integer_impl.h"
diff --git a/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h b/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h
new file mode 100644
index 00000000000..d3ddaf1431e
--- /dev/null
+++ b/ydb/library/arrow_clickhouse/base/common/wide_integer_impl.h
@@ -0,0 +1,1455 @@
+#pragma once
+
+/// Original is here https://github.com/cerevra/int
+/// Distributed under the Boost Software License, Version 1.0.
+/// (See at http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cmath>
+#include <cfloat>
+#include <cassert>
+#include <tuple>
+#include <limits>
+
+
+namespace CH::wide
+{
+
+template <typename T>
+struct IsWideInteger
+{
+ static const constexpr bool value = false;
+};
+
+template <size_t Bits, typename Signed>
+struct IsWideInteger<wide::integer<Bits, Signed>>
+{
+ static const constexpr bool value = true;
+};
+
+template <typename T>
+static constexpr bool ArithmeticConcept() noexcept
+{
+ return std::is_arithmetic_v<T> || IsWideInteger<T>::value;
+}
+
+template <typename T>
+static constexpr bool IntegralConcept() noexcept
+{
+ return std::is_integral_v<T> || IsWideInteger<T>::value;
+}
+
+template <typename T>
+class IsTupleLike
+{
+ 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;
+};
+
+}
+
+namespace std
+{
+
+// numeric limits
+template <size_t Bits, typename Signed>
+class numeric_limits<CH::wide::integer<Bits, Signed>>
+{
+public:
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = is_same<Signed, signed>::value;
+ static constexpr bool is_integer = true;
+ static constexpr bool is_exact = true;
+ static constexpr bool has_infinity = false;
+ static constexpr bool has_quiet_NaN = false;
+ static constexpr bool has_signaling_NaN = true;
+ static constexpr std::float_denorm_style has_denorm = std::denorm_absent;
+ static constexpr bool has_denorm_loss = false;
+ static constexpr std::float_round_style round_style = std::round_toward_zero;
+ static constexpr bool is_iec559 = false;
+ static constexpr bool is_bounded = true;
+ static constexpr bool is_modulo = true;
+ static constexpr int digits = Bits - (is_same<Signed, signed>::value ? 1 : 0);
+ static constexpr int digits10 = digits * 0.30103 /*std::log10(2)*/;
+ static constexpr int max_digits10 = 0;
+ static constexpr int radix = 2;
+ static constexpr int min_exponent = 0;
+ static constexpr int min_exponent10 = 0;
+ static constexpr int max_exponent = 0;
+ static constexpr int max_exponent10 = 0;
+ static constexpr bool traps = true;
+ static constexpr bool tinyness_before = false;
+
+ static constexpr CH::wide::integer<Bits, Signed> min() noexcept
+ {
+ if (is_same<Signed, signed>::value)
+ {
+ using T = CH::wide::integer<Bits, signed>;
+ T res{};
+ res.items[T::_impl::big(0)] = std::numeric_limits<typename CH::wide::integer<Bits, Signed>::signed_base_type>::min();
+ return res;
+ }
+ return CH::wide::integer<Bits, Signed>(0);
+ }
+
+ static constexpr CH::wide::integer<Bits, Signed> max() noexcept
+ {
+ using T = CH::wide::integer<Bits, Signed>;
+ T res{};
+ res.items[T::_impl::big(0)] = is_same<Signed, signed>::value
+ ? std::numeric_limits<typename CH::wide::integer<Bits, Signed>::signed_base_type>::max()
+ : std::numeric_limits<typename CH::wide::integer<Bits, Signed>::base_type>::max();
+ for (unsigned i = 1; i < CH::wide::integer<Bits, Signed>::_impl::item_count; ++i)
+ {
+ res.items[T::_impl::big(i)] = std::numeric_limits<typename CH::wide::integer<Bits, Signed>::base_type>::max();
+ }
+ return res;
+ }
+
+ static constexpr CH::wide::integer<Bits, Signed> lowest() noexcept { return min(); }
+ static constexpr CH::wide::integer<Bits, Signed> epsilon() noexcept { return 0; }
+ static constexpr CH::wide::integer<Bits, Signed> round_error() noexcept { return 0; }
+ static constexpr CH::wide::integer<Bits, Signed> infinity() noexcept { return 0; }
+ static constexpr CH::wide::integer<Bits, Signed> quiet_NaN() noexcept { return 0; }
+ static constexpr CH::wide::integer<Bits, Signed> signaling_NaN() noexcept { return 0; }
+ static constexpr CH::wide::integer<Bits, Signed> denorm_min() noexcept { return 0; }
+};
+
+// type traits
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+struct common_type<CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>>
+{
+ using type = std::conditional_t < Bits == Bits2,
+ CH::wide::integer<
+ Bits,
+ std::conditional_t<(std::is_same_v<Signed, Signed2> && std::is_same_v<Signed2, signed>), signed, unsigned>>,
+ std::conditional_t<Bits2<Bits, CH::wide::integer<Bits, Signed>, CH::wide::integer<Bits2, Signed2>>>;
+};
+
+template <size_t Bits, typename Signed, typename Arithmetic>
+struct common_type<CH::wide::integer<Bits, Signed>, Arithmetic>
+{
+ static_assert(CH::wide::ArithmeticConcept<Arithmetic>());
+
+ using type = std::conditional_t<
+ std::is_floating_point_v<Arithmetic>,
+ Arithmetic,
+ std::conditional_t<
+ sizeof(Arithmetic) < Bits * sizeof(long),
+ CH::wide::integer<Bits, Signed>,
+ std::conditional_t<
+ Bits * sizeof(long) < sizeof(Arithmetic),
+ Arithmetic,
+ std::conditional_t<
+ Bits * sizeof(long) == sizeof(Arithmetic) && (std::is_same_v<Signed, signed> || std::is_signed_v<Arithmetic>),
+ Arithmetic,
+ CH::wide::integer<Bits, Signed>>>>>;
+};
+
+template <typename Arithmetic, size_t Bits, typename Signed>
+struct common_type<Arithmetic, CH::wide::integer<Bits, Signed>> : common_type<CH::wide::integer<Bits, Signed>, Arithmetic>
+{
+};
+
+}
+
+namespace CH::wide
+{
+
+template <size_t Bits, typename Signed>
+struct integer<Bits, Signed>::_impl
+{
+ static constexpr size_t _bits = Bits;
+ static constexpr const unsigned byte_count = Bits / 8;
+ static constexpr const unsigned item_count = byte_count / sizeof(base_type);
+ static constexpr const unsigned base_bits = sizeof(base_type) * 8;
+
+ static_assert(Bits % base_bits == 0);
+
+ /// Simple iteration in both directions
+ static constexpr unsigned little(unsigned idx) { return idx; }
+ static constexpr unsigned big(unsigned idx) { return item_count - 1 - idx; }
+ static constexpr unsigned any(unsigned idx) { return idx; }
+
+ template <class T>
+ constexpr static bool is_negative(const T & n) noexcept
+ {
+ if constexpr (std::is_signed_v<T>)
+ return n < 0;
+ else
+ return false;
+ }
+
+ template <size_t B, class T>
+ constexpr static bool is_negative(const integer<B, T> & n) noexcept
+ {
+ if constexpr (std::is_same_v<T, signed>)
+ return static_cast<signed_base_type>(n.items[integer<B, T>::_impl::big(0)]) < 0;
+ else
+ return false;
+ }
+
+ template <typename T>
+ constexpr static auto make_positive(const T & n) noexcept
+ {
+ if constexpr (std::is_signed_v<T>)
+ return n < 0 ? -n : n;
+ else
+ return n;
+ }
+
+ template <size_t B, class S>
+ constexpr static integer<B, S> make_positive(const integer<B, S> & n) noexcept
+ {
+ return is_negative(n) ? integer<B, S>(operator_unary_minus(n)) : n;
+ }
+
+ template <typename T>
+ __attribute__((no_sanitize("undefined"))) constexpr static auto to_Integral(T f) noexcept
+ {
+ if constexpr (std::is_signed_v<T>)
+ return static_cast<int64_t>(f);
+ else
+ return static_cast<uint64_t>(f);
+ }
+
+ template <typename Integral>
+ constexpr static void wide_integer_from_builtin(integer<Bits, Signed> & self, Integral rhs) noexcept
+ {
+ static_assert(sizeof(Integral) <= sizeof(base_type));
+
+ self.items[0] = _impl::to_Integral(rhs);
+
+ if constexpr (std::is_signed_v<Integral>)
+ {
+ if (rhs < 0)
+ {
+ for (size_t i = 1; i < item_count; ++i)
+ self.items[i] = -1;
+ return;
+ }
+ }
+
+ for (size_t i = 1; i < item_count; ++i)
+ self.items[i] = 0;
+ }
+
+ template <typename TupleLike, size_t i = 0>
+ constexpr static void wide_integer_from_tuple_like(integer<Bits, Signed> & self, const TupleLike & tuple) noexcept
+ {
+ if constexpr (i < item_count)
+ {
+ if constexpr (i < std::tuple_size_v<TupleLike>)
+ self.items[i] = std::get<i>(tuple);
+ else
+ self.items[i] = 0;
+ wide_integer_from_tuple_like<TupleLike, i + 1>(self, tuple);
+ }
+ }
+
+ /**
+ * N.B. t is constructed from double, so max(t) = max(double) ~ 2^310
+ * the recursive call happens when t / 2^64 > 2^64, so there won't be more than 5 of them.
+ *
+ * t = a1 * max_int + b1, a1 > max_int, b1 < max_int
+ * a1 = a2 * max_int + b2, a2 > max_int, b2 < max_int
+ * a_(n - 1) = a_n * max_int + b2, a_n <= max_int <- base case.
+ */
+ template <class T>
+ constexpr static void set_multiplier(integer<Bits, Signed> & self, T t) noexcept
+ {
+ constexpr uint64_t max_int = std::numeric_limits<uint64_t>::max();
+
+ /// Implementation specific behaviour on overflow (if we don't check here, stack overflow will triggered in bigint_cast).
+ if (!std::isfinite(t))
+ {
+ self = 0;
+ return;
+ }
+
+ const T alpha = t / static_cast<T>(max_int);
+
+ if (alpha <= static_cast<T>(max_int))
+ self = static_cast<uint64_t>(alpha);
+ else // max(double) / 2^64 will surely contain less than 52 precision bits, so speed up computations.
+ set_multiplier<double>(self, alpha);
+
+ self *= max_int;
+ self += static_cast<uint64_t>(t - floor(alpha) * static_cast<T>(max_int)); // += b_i
+ }
+
+ constexpr static void wide_integer_from_builtin(integer<Bits, Signed> & self, double rhs) noexcept
+ {
+ constexpr int64_t max_int = std::numeric_limits<int64_t>::max();
+ constexpr int64_t min_int = std::numeric_limits<int64_t>::lowest();
+
+ /// There are values in int64 that have more than 53 significant bits (in terms of double
+ /// representation). Such values, being promoted to double, are rounded up or down. If they are rounded up,
+ /// the result may not fit in 64 bits.
+ /// The example of such a number is 9.22337e+18.
+ /// As to_Integral does a static_cast to int64_t, it may result in UB.
+ /// The necessary check here is that long double has enough significant (mantissa) bits to store the
+ /// int64_t max value precisely.
+
+ // TODO Be compatible with Apple aarch64
+#if not (defined(__APPLE__) && defined(__aarch64__))
+ static_assert(LDBL_MANT_DIG >= 64,
+ "On your system long double has less than 64 precision bits, "
+ "which may result in UB when initializing double from int64_t");
+#endif
+
+ if (rhs > static_cast<long double>(min_int) && rhs < static_cast<long double>(max_int))
+ {
+ self = static_cast<int64_t>(rhs);
+ return;
+ }
+
+ const long double rhs_long_double = (static_cast<long double>(rhs) < 0)
+ ? -static_cast<long double>(rhs)
+ : rhs;
+
+ set_multiplier(self, rhs_long_double);
+
+ if (rhs < 0)
+ self = -self;
+ }
+
+ template <size_t Bits2, typename Signed2>
+ constexpr static void
+ wide_integer_from_wide_integer(integer<Bits, Signed> & self, const integer<Bits2, Signed2> & rhs) noexcept
+ {
+ constexpr const unsigned min_bits = (Bits < Bits2) ? Bits : Bits2;
+ constexpr const unsigned to_copy = min_bits / base_bits;
+
+ for (unsigned i = 0; i < to_copy; ++i)
+ self.items[i] = rhs.items[i];
+
+ if constexpr (Bits > Bits2)
+ {
+ if constexpr (std::is_signed_v<Signed2>)
+ {
+ if (rhs < 0)
+ {
+ for (unsigned i = to_copy; i < item_count; ++i)
+ self.items[i] = -1;
+ return;
+ }
+ }
+
+ for (unsigned i = to_copy; i < item_count; ++i)
+ self.items[i] = 0;
+ }
+ }
+
+ template <typename T>
+ constexpr static bool should_keep_size()
+ {
+ return sizeof(T) <= byte_count;
+ }
+
+ constexpr static integer<Bits, Signed> shift_left(const integer<Bits, Signed> & rhs, unsigned n) noexcept
+ {
+ integer<Bits, Signed> lhs;
+ unsigned items_shift = n / base_bits;
+
+ if (unsigned bit_shift = n % base_bits)
+ {
+ unsigned overflow_shift = base_bits - bit_shift;
+
+ lhs.items[big(0)] = rhs.items[big(items_shift)] << bit_shift;
+ for (unsigned i = 1; i < item_count - items_shift; ++i)
+ {
+ lhs.items[big(i - 1)] |= rhs.items[big(items_shift + i)] >> overflow_shift;
+ lhs.items[big(i)] = rhs.items[big(items_shift + i)] << bit_shift;
+ }
+ }
+ else
+ {
+ for (unsigned i = 0; i < item_count - items_shift; ++i)
+ lhs.items[big(i)] = rhs.items[big(items_shift + i)];
+ }
+
+ for (unsigned i = 0; i < items_shift; ++i)
+ lhs.items[little(i)] = 0;
+ return lhs;
+ }
+
+ constexpr static integer<Bits, Signed> shift_right(const integer<Bits, Signed> & rhs, unsigned n) noexcept
+ {
+ integer<Bits, Signed> lhs;
+ unsigned items_shift = n / base_bits;
+ unsigned bit_shift = n % base_bits;
+
+ if (bit_shift)
+ {
+ unsigned overflow_shift = base_bits - bit_shift;
+
+ lhs.items[little(0)] = rhs.items[little(items_shift)] >> bit_shift;
+ for (unsigned i = 1; i < item_count - items_shift; ++i)
+ {
+ lhs.items[little(i - 1)] |= rhs.items[little(items_shift + i)] << overflow_shift;
+ lhs.items[little(i)] = rhs.items[little(items_shift + i)] >> bit_shift;
+ }
+ }
+ else
+ {
+ for (unsigned i = 0; i < item_count - items_shift; ++i)
+ lhs.items[little(i)] = rhs.items[little(items_shift + i)];
+ }
+
+ if (is_negative(rhs))
+ {
+ if (bit_shift)
+ lhs.items[big(items_shift)] |= std::numeric_limits<base_type>::max() << (base_bits - bit_shift);
+
+ for (unsigned i = 0; i < items_shift; ++i)
+ lhs.items[big(i)] = std::numeric_limits<base_type>::max();
+ }
+ else
+ {
+ for (unsigned i = 0; i < items_shift; ++i)
+ lhs.items[big(i)] = 0;
+ }
+
+ return lhs;
+ }
+
+private:
+ template <typename T>
+ constexpr static base_type get_item(const T & x, unsigned idx)
+ {
+ if constexpr (IsWideInteger<T>::value)
+ {
+ if (idx < T::_impl::item_count)
+ return x.items[idx];
+ return 0;
+ }
+ else
+ {
+ if constexpr (sizeof(T) <= sizeof(base_type))
+ {
+ if (0 == idx)
+ return x;
+ }
+ else if (idx * sizeof(base_type) < sizeof(T))
+ return x >> (idx * base_bits); // & std::numeric_limits<base_type>::max()
+ return 0;
+ }
+ }
+
+ template <typename T>
+ constexpr static integer<Bits, Signed>
+ minus(const integer<Bits, Signed> & lhs, T rhs)
+ {
+ constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1;
+ constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items;
+
+ integer<Bits, Signed> res(lhs);
+ bool underflows[item_count] = {};
+
+ for (unsigned i = 0; i < op_items; ++i)
+ {
+ base_type rhs_item = get_item(rhs, i);
+ base_type & res_item = res.items[little(i)];
+
+ underflows[i] = res_item < rhs_item;
+ res_item -= rhs_item;
+ }
+
+ for (unsigned i = 1; i < item_count; ++i)
+ {
+ if (underflows[i - 1])
+ {
+ base_type & res_item = res.items[little(i)];
+ if (res_item == 0)
+ underflows[i] = true;
+ --res_item;
+ }
+ }
+
+ return res;
+ }
+
+ template <typename T>
+ constexpr static integer<Bits, Signed>
+ plus(const integer<Bits, Signed> & lhs, T rhs)
+ {
+ constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1;
+ constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items;
+
+ integer<Bits, Signed> res(lhs);
+ bool overflows[item_count] = {};
+
+ for (unsigned i = 0; i < op_items; ++i)
+ {
+ base_type rhs_item = get_item(rhs, i);
+ base_type & res_item = res.items[little(i)];
+
+ res_item += rhs_item;
+ overflows[i] = res_item < rhs_item;
+ }
+
+ for (unsigned i = 1; i < item_count; ++i)
+ {
+ if (overflows[i - 1])
+ {
+ base_type & res_item = res.items[little(i)];
+ ++res_item;
+ if (res_item == 0)
+ overflows[i] = true;
+ }
+ }
+
+ return res;
+ }
+
+ template <typename T>
+ constexpr static integer<Bits, Signed>
+ multiply(const integer<Bits, Signed> & lhs, const T & rhs)
+ {
+ if constexpr (Bits == 256 && sizeof(base_type) == 8)
+ {
+ /// @sa https://github.com/abseil/abseil-cpp/blob/master/absl/numeric/int128.h
+ using HalfType = unsigned __int128;
+
+ HalfType a01 = (HalfType(lhs.items[little(1)]) << 64) + lhs.items[little(0)];
+ HalfType a23 = (HalfType(lhs.items[little(3)]) << 64) + lhs.items[little(2)];
+ HalfType a0 = lhs.items[little(0)];
+ HalfType a1 = lhs.items[little(1)];
+
+ HalfType b01 = rhs;
+ uint64_t b0 = b01;
+ uint64_t b1 = 0;
+ HalfType b23 = 0;
+ if constexpr (sizeof(T) > 8)
+ b1 = b01 >> 64;
+ if constexpr (sizeof(T) > 16)
+ b23 = (HalfType(rhs.items[little(3)]) << 64) + rhs.items[little(2)];
+
+ HalfType r23 = a23 * b01 + a01 * b23 + a1 * b1;
+ HalfType r01 = a0 * b0;
+ HalfType r12 = (r01 >> 64) + (r23 << 64);
+ HalfType r12_x = a1 * b0;
+
+ integer<Bits, Signed> res;
+ res.items[little(0)] = r01;
+ res.items[little(3)] = r23 >> 64;
+
+ if constexpr (sizeof(T) > 8)
+ {
+ HalfType r12_y = a0 * b1;
+ r12_x += r12_y;
+ if (r12_x < r12_y)
+ ++res.items[little(3)];
+ }
+
+ r12 += r12_x;
+ if (r12 < r12_x)
+ ++res.items[little(3)];
+
+ res.items[little(1)] = r12;
+ res.items[little(2)] = r12 >> 64;
+ return res;
+ }
+ else if constexpr (Bits == 128 && sizeof(base_type) == 8)
+ {
+ using CompilerUInt128 = unsigned __int128;
+ CompilerUInt128 a = (CompilerUInt128(lhs.items[1]) << 64) + lhs.items[0];
+ CompilerUInt128 b = (CompilerUInt128(rhs.items[1]) << 64) + rhs.items[0];
+ CompilerUInt128 c = a * b;
+ integer<Bits, Signed> res;
+ res.items[0] = c;
+ res.items[1] = c >> 64;
+ return res;
+ }
+ else
+ {
+ integer<Bits, Signed> res{};
+#if 1
+ integer<Bits, Signed> lhs2 = plus(lhs, shift_left(lhs, 1));
+ integer<Bits, Signed> lhs3 = plus(lhs2, shift_left(lhs, 2));
+#endif
+ for (unsigned i = 0; i < item_count; ++i)
+ {
+ base_type rhs_item = get_item(rhs, i);
+ unsigned pos = i * base_bits;
+
+ while (rhs_item)
+ {
+#if 1 /// optimization
+ if ((rhs_item & 0x7) == 0x7)
+ {
+ res = plus(res, shift_left(lhs3, pos));
+ rhs_item >>= 3;
+ pos += 3;
+ continue;
+ }
+
+ if ((rhs_item & 0x3) == 0x3)
+ {
+ res = plus(res, shift_left(lhs2, pos));
+ rhs_item >>= 2;
+ pos += 2;
+ continue;
+ }
+#endif
+ if (rhs_item & 1)
+ res = plus(res, shift_left(lhs, pos));
+
+ rhs_item >>= 1;
+ ++pos;
+ }
+ }
+
+ return res;
+ }
+ }
+
+public:
+ constexpr static integer<Bits, Signed> operator_unary_tilda(const integer<Bits, Signed> & lhs) noexcept
+ {
+ integer<Bits, Signed> res;
+
+ for (unsigned i = 0; i < item_count; ++i)
+ res.items[any(i)] = ~lhs.items[any(i)];
+ return res;
+ }
+
+ constexpr static integer<Bits, Signed>
+ operator_unary_minus(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>)
+ {
+ return plus(operator_unary_tilda(lhs), 1);
+ }
+
+ template <typename T>
+ constexpr static auto operator_plus(const integer<Bits, Signed> & lhs, const T & rhs) noexcept(std::is_same_v<Signed, unsigned>)
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ if (is_negative(rhs))
+ return minus(lhs, -rhs);
+ else
+ return plus(lhs, rhs);
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::_impl::operator_plus(
+ integer<T::_impl::_bits, Signed>(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static auto operator_minus(const integer<Bits, Signed> & lhs, const T & rhs) noexcept(std::is_same_v<Signed, unsigned>)
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ if (is_negative(rhs))
+ return plus(lhs, -rhs);
+ else
+ return minus(lhs, rhs);
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::_impl::operator_minus(
+ integer<T::_impl::_bits, Signed>(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static auto operator_star(const integer<Bits, Signed> & lhs, const T & rhs)
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, Signed> res;
+
+ if constexpr (std::is_signed_v<Signed>)
+ {
+ res = multiply((is_negative(lhs) ? make_positive(lhs) : lhs),
+ (is_negative(rhs) ? make_positive(rhs) : rhs));
+ }
+ else
+ {
+ res = multiply(lhs, (is_negative(rhs) ? make_positive(rhs) : rhs));
+ }
+
+ if (std::is_same_v<Signed, signed> && is_negative(lhs) != is_negative(rhs))
+ res = operator_unary_minus(res);
+
+ return res;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_star(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static bool operator_greater(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ if (std::numeric_limits<T>::is_signed && (is_negative(lhs) != is_negative(rhs)))
+ return is_negative(rhs);
+
+ for (unsigned i = 0; i < item_count; ++i)
+ {
+ base_type rhs_item = get_item(rhs, big(i));
+
+ if (lhs.items[big(i)] != rhs_item)
+ return lhs.items[big(i)] > rhs_item;
+ }
+
+ return false;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_greater(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static bool operator_less(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ if (std::numeric_limits<T>::is_signed && (is_negative(lhs) != is_negative(rhs)))
+ return is_negative(lhs);
+
+ for (unsigned i = 0; i < item_count; ++i)
+ {
+ base_type rhs_item = get_item(rhs, big(i));
+
+ if (lhs.items[big(i)] != rhs_item)
+ return lhs.items[big(i)] < rhs_item;
+ }
+
+ return false;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_less(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static bool operator_eq(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ for (unsigned i = 0; i < item_count; ++i)
+ {
+ base_type rhs_item = get_item(rhs, any(i));
+
+ if (lhs.items[any(i)] != rhs_item)
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_eq(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static auto operator_pipe(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, Signed> res;
+
+ for (unsigned i = 0; i < item_count; ++i)
+ res.items[little(i)] = lhs.items[little(i)] | get_item(rhs, i);
+ return res;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_pipe(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static auto operator_amp(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, Signed> res;
+
+ for (unsigned i = 0; i < item_count; ++i)
+ res.items[little(i)] = lhs.items[little(i)] & get_item(rhs, i);
+ return res;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, T>::_impl::operator_amp(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static bool is_zero(const T & x)
+ {
+ bool is_zero = true;
+ for (auto item : x.items)
+ {
+ if (item != 0)
+ {
+ is_zero = false;
+ break;
+ }
+ }
+ return is_zero;
+ }
+
+ /// returns quotient as result and remainder in numerator.
+ template <size_t Bits2>
+ constexpr static integer<Bits2, unsigned> divide(integer<Bits2, unsigned> & numerator, integer<Bits2, unsigned> denominator)
+ {
+ static_assert(std::is_unsigned_v<Signed>);
+
+ if constexpr (Bits == 128 && sizeof(base_type) == 8)
+ {
+ using CompilerUInt128 = unsigned __int128;
+
+ CompilerUInt128 a = (CompilerUInt128(numerator.items[1]) << 64) + numerator.items[0];
+ CompilerUInt128 b = (CompilerUInt128(denominator.items[1]) << 64) + denominator.items[0];
+ CompilerUInt128 c = a / b;
+
+ integer<Bits, Signed> res;
+ res.items[0] = c;
+ res.items[1] = c >> 64;
+
+ CompilerUInt128 remainder = a - b * c;
+ numerator.items[0] = remainder;
+ numerator.items[1] = remainder >> 64;
+
+ return res;
+ }
+
+ if (is_zero(denominator))
+ throw std::runtime_error("Division by zero");
+
+ integer<Bits2, unsigned> x = 1;
+ integer<Bits2, unsigned> quotient = 0;
+
+ while (!operator_greater(denominator, numerator) && is_zero(operator_amp(shift_right(denominator, Bits2 - 1), 1)))
+ {
+ x = shift_left(x, 1);
+ denominator = shift_left(denominator, 1);
+ }
+
+ while (!is_zero(x))
+ {
+ if (!operator_greater(denominator, numerator))
+ {
+ numerator = operator_minus(numerator, denominator);
+ quotient = operator_pipe(quotient, x);
+ }
+
+ x = shift_right(x, 1);
+ denominator = shift_right(denominator, 1);
+ }
+
+ return quotient;
+ }
+
+ template <typename T>
+ constexpr static auto operator_slash(const integer<Bits, Signed> & lhs, const T & rhs)
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, unsigned> numerator = make_positive(lhs);
+ integer<Bits, unsigned> denominator = make_positive(integer<Bits, Signed>(rhs));
+ integer<Bits, unsigned> quotient = integer<Bits, unsigned>::_impl::divide(numerator, std::move(denominator));
+
+ if (std::is_same_v<Signed, signed> && is_negative(rhs) != is_negative(lhs))
+ quotient = operator_unary_minus(quotient);
+ return quotient;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::operator_slash(T(lhs), rhs);
+ }
+ }
+
+ template <typename T>
+ constexpr static auto operator_percent(const integer<Bits, Signed> & lhs, const T & rhs)
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, unsigned> remainder = make_positive(lhs);
+ integer<Bits, unsigned> denominator = make_positive(integer<Bits, Signed>(rhs));
+ integer<Bits, unsigned>::_impl::divide(remainder, std::move(denominator));
+
+ if (std::is_same_v<Signed, signed> && is_negative(lhs))
+ remainder = operator_unary_minus(remainder);
+ return remainder;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return std::common_type_t<integer<Bits, Signed>, integer<T::_impl::_bits, Signed>>::operator_percent(T(lhs), rhs);
+ }
+ }
+
+ // ^
+ template <typename T>
+ constexpr static auto operator_circumflex(const integer<Bits, Signed> & lhs, const T & rhs) noexcept
+ {
+ if constexpr (should_keep_size<T>())
+ {
+ integer<Bits, Signed> t(rhs);
+ integer<Bits, Signed> res = lhs;
+
+ for (unsigned i = 0; i < item_count; ++i)
+ res.items[any(i)] ^= t.items[any(i)];
+ return res;
+ }
+ else
+ {
+ static_assert(IsWideInteger<T>::value);
+ return T::operator_circumflex(T(lhs), rhs);
+ }
+ }
+
+ constexpr static integer<Bits, Signed> from_str(const char * c)
+ {
+ integer<Bits, Signed> res = 0;
+
+ bool is_neg = std::is_same_v<Signed, signed> && *c == '-';
+ if (is_neg)
+ ++c;
+
+ if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X'))
+ { // hex
+ ++c;
+ ++c;
+ while (*c)
+ {
+ if (*c >= '0' && *c <= '9')
+ {
+ res = multiply(res, 16U);
+ res = plus(res, *c - '0');
+ ++c;
+ }
+ else if (*c >= 'a' && *c <= 'f')
+ {
+ res = multiply(res, 16U);
+ res = plus(res, *c - 'a' + 10U);
+ ++c;
+ }
+ else if (*c >= 'A' && *c <= 'F')
+ { // tolower must be used, but it is not constexpr
+ res = multiply(res, 16U);
+ res = plus(res, *c - 'A' + 10U);
+ ++c;
+ }
+ else
+ throw std::runtime_error("Invalid char from");
+ }
+ }
+ else
+ { // dec
+ while (*c)
+ {
+ if (*c < '0' || *c > '9')
+ throw std::runtime_error("Invalid char from");
+
+ res = multiply(res, 10U);
+ res = plus(res, *c - '0');
+ ++c;
+ }
+ }
+
+ if (is_neg)
+ res = operator_unary_minus(res);
+
+ return res;
+ }
+};
+
+// Members
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed>::integer(T rhs) noexcept
+ : items{}
+{
+ if constexpr (IsWideInteger<T>::value)
+ _impl::wide_integer_from_wide_integer(*this, rhs);
+ else if constexpr (IsTupleLike<T>::value)
+ _impl::wide_integer_from_tuple_like(*this, rhs);
+ else
+ _impl::wide_integer_from_builtin(*this, rhs);
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed>::integer(std::initializer_list<T> il) noexcept
+ : items{}
+{
+ if (il.size() == 1)
+ {
+ if constexpr (IsWideInteger<T>::value)
+ _impl::wide_integer_from_wide_integer(*this, *il.begin());
+ else if constexpr (IsTupleLike<T>::value)
+ _impl::wide_integer_from_tuple_like(*this, *il.begin());
+ else
+ _impl::wide_integer_from_builtin(*this, *il.begin());
+ }
+ else if (il.size() == 0)
+ {
+ _impl::wide_integer_from_builtin(*this, 0);
+ }
+ else
+ {
+ auto it = il.begin();
+ for (size_t i = 0; i < _impl::item_count; ++i)
+ if (it < il.end())
+ items[i] = *it;
+ }
+}
+
+template <size_t Bits, typename Signed>
+template <size_t Bits2, typename Signed2>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator=(const integer<Bits2, Signed2> & rhs) noexcept
+{
+ _impl::wide_integer_from_wide_integer(*this, rhs);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator=(T rhs) noexcept
+{
+ if constexpr (IsTupleLike<T>::value)
+ _impl::wide_integer_from_tuple_like(*this, rhs);
+ else
+ _impl::wide_integer_from_builtin(*this, rhs);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator*=(const T & rhs)
+{
+ *this = *this * rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator/=(const T & rhs)
+{
+ *this = *this / rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator+=(const T & rhs) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ *this = *this + rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator-=(const T & rhs) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ *this = *this - rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator%=(const T & rhs)
+{
+ *this = *this % rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator&=(const T & rhs) noexcept
+{
+ *this = *this & rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator|=(const T & rhs) noexcept
+{
+ *this = *this | rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+template <typename T>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator^=(const T & rhs) noexcept
+{
+ *this = *this ^ rhs;
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator<<=(int n) noexcept
+{
+ if (static_cast<size_t>(n) >= Bits)
+ *this = 0;
+ else if (n > 0)
+ *this = _impl::shift_left(*this, n);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator>>=(int n) noexcept
+{
+ if (static_cast<size_t>(n) >= Bits)
+ {
+ if (_impl::is_negative(*this))
+ *this = -1;
+ else
+ *this = 0;
+ }
+ else if (n > 0)
+ *this = _impl::shift_right(*this, n);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator++() noexcept(std::is_same_v<Signed, unsigned>)
+{
+ *this = _impl::operator_plus(*this, 1);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> integer<Bits, Signed>::operator++(int) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ auto tmp = *this;
+ *this = _impl::operator_plus(*this, 1);
+ return tmp;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> & integer<Bits, Signed>::operator--() noexcept(std::is_same_v<Signed, unsigned>)
+{
+ *this = _impl::operator_minus(*this, 1);
+ return *this;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> integer<Bits, Signed>::operator--(int) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ auto tmp = *this;
+ *this = _impl::operator_minus(*this, 1);
+ return tmp;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed>::operator bool() const noexcept
+{
+ return !_impl::operator_eq(*this, 0);
+}
+
+template <size_t Bits, typename Signed>
+template <class T, class>
+constexpr integer<Bits, Signed>::operator T() const noexcept
+{
+ static_assert(std::numeric_limits<T>::is_integer);
+
+ /// NOTE: memcpy will suffice, but unfortunately, this function is constexpr.
+
+ using UnsignedT = std::make_unsigned_t<T>;
+
+ UnsignedT res{};
+ for (unsigned i = 0; i < _impl::item_count && i < (sizeof(T) + sizeof(base_type) - 1) / sizeof(base_type); ++i)
+ res += UnsignedT(items[i]) << (sizeof(base_type) * 8 * i);
+
+ return res;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed>::operator long double() const noexcept
+{
+ if (_impl::operator_eq(*this, 0))
+ return 0;
+
+ integer<Bits, Signed> tmp = *this;
+ if (_impl::is_negative(*this))
+ tmp = -tmp;
+
+ long double res = 0;
+ for (unsigned i = 0; i < _impl::item_count; ++i)
+ {
+ long double t = res;
+ res *= std::numeric_limits<base_type>::max();
+ res += t;
+ res += tmp.items[_impl::big(i)];
+ }
+
+ if (_impl::is_negative(*this))
+ res = -res;
+
+ return res;
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed>::operator double() const noexcept
+{
+ return static_cast<long double>(*this);
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed>::operator float() const noexcept
+{
+ return static_cast<long double>(*this);
+}
+
+// Unary operators
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator~(const integer<Bits, Signed> & lhs) noexcept
+{
+ return integer<Bits, Signed>::_impl::operator_unary_tilda(lhs);
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator-(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ return integer<Bits, Signed>::_impl::operator_unary_minus(lhs);
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator+(const integer<Bits, Signed> & lhs) noexcept(std::is_same_v<Signed, unsigned>)
+{
+ return lhs;
+}
+
+#define CT(x) \
+ std::common_type_t<std::decay_t<decltype(rhs)>, std::decay_t<decltype(lhs)>> { x }
+
+// Binary operators
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator*(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_star(lhs, rhs);
+}
+
+template <typename Arithmetic, typename Arithmetic2, class>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator*(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) * CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator/(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_slash(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator/(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) / CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator+(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_plus(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator+(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) + CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator-(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_minus(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+std::common_type_t<Arithmetic, Arithmetic2> constexpr operator-(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) - CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator%(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_percent(lhs, rhs);
+}
+template <typename Integral, typename Integral2, class>
+std::common_type_t<Integral, Integral2> constexpr operator%(const Integral & lhs, const Integral2 & rhs)
+{
+ return CT(lhs) % CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator&(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_amp(lhs, rhs);
+}
+template <typename Integral, typename Integral2, class>
+std::common_type_t<Integral, Integral2> constexpr operator&(const Integral & lhs, const Integral2 & rhs)
+{
+ return CT(lhs) & CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator|(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_pipe(lhs, rhs);
+}
+template <typename Integral, typename Integral2, class>
+std::common_type_t<Integral, Integral2> constexpr operator|(const Integral & lhs, const Integral2 & rhs)
+{
+ return CT(lhs) | CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>> constexpr
+operator^(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_circumflex(lhs, rhs);
+}
+template <typename Integral, typename Integral2, class>
+std::common_type_t<Integral, Integral2> constexpr operator^(const Integral & lhs, const Integral2 & rhs)
+{
+ return CT(lhs) ^ CT(rhs);
+}
+
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator<<(const integer<Bits, Signed> & lhs, int n) noexcept
+{
+ if (static_cast<size_t>(n) >= Bits)
+ return integer<Bits, Signed>(0);
+ if (n <= 0)
+ return lhs;
+ return integer<Bits, Signed>::_impl::shift_left(lhs, n);
+}
+template <size_t Bits, typename Signed>
+constexpr integer<Bits, Signed> operator>>(const integer<Bits, Signed> & lhs, int n) noexcept
+{
+ if (static_cast<size_t>(n) >= Bits)
+ return integer<Bits, Signed>(0);
+ if (n <= 0)
+ return lhs;
+ return integer<Bits, Signed>::_impl::shift_right(lhs, n);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator<(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_less(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator<(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) < CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator>(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_greater(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator>(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) > CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator<=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_less(lhs, rhs)
+ || std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator<=(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) <= CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator>=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_greater(lhs, rhs)
+ || std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator>=(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) >= CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator==(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator==(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) == CT(rhs);
+}
+
+template <size_t Bits, typename Signed, size_t Bits2, typename Signed2>
+constexpr bool operator!=(const integer<Bits, Signed> & lhs, const integer<Bits2, Signed2> & rhs)
+{
+ return !std::common_type_t<integer<Bits, Signed>, integer<Bits2, Signed2>>::_impl::operator_eq(lhs, rhs);
+}
+template <typename Arithmetic, typename Arithmetic2, class>
+constexpr bool operator!=(const Arithmetic & lhs, const Arithmetic2 & rhs)
+{
+ return CT(lhs) != CT(rhs);
+}
+
+#undef CT
+
+}
+
+namespace std
+{
+
+template <size_t Bits, typename Signed>
+struct hash<CH::wide::integer<Bits, Signed>>
+{
+ std::size_t operator()(const CH::wide::integer<Bits, Signed> & lhs) const
+ {
+ static_assert(Bits % (sizeof(size_t) * 8) == 0);
+
+ const auto * ptr = reinterpret_cast<const size_t *>(lhs.items);
+ unsigned count = Bits / (sizeof(size_t) * 8);
+
+ size_t res = 0;
+ for (unsigned i = 0; i < count; ++i)
+ res ^= ptr[i];
+ return res;
+ }
+};
+
+}
diff --git a/ydb/library/yql/public/udf/udfs_exports.exports b/ydb/library/yql/public/udf/udfs_exports.exports
new file mode 100644
index 00000000000..c3af1d6045d
--- /dev/null
+++ b/ydb/library/yql/public/udf/udfs_exports.exports
@@ -0,0 +1,4 @@
+C Register
+C AbiVersion
+C SetBackTraceCallback
+C BindSymbols