diff --git a/README.md b/README.md index bad00d8280f..5677837815c 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,6 @@ ClickHouse® is an open-source column-oriented database management system that a * [Code Browser](https://clickhouse.tech/codebrowser/html_report/ClickHouse/index.html) with syntax highlight and navigation. * [Contacts](https://clickhouse.tech/#contacts) can help to get your questions answered if there are any. * You can also [fill this form](https://clickhouse.tech/#meet) to meet Yandex ClickHouse team in person. + +## Upcoming Events +* [SF Bay Area ClickHouse Community Meetup (online)](https://www.meetup.com/San-Francisco-Bay-Area-ClickHouse-Meetup/events/278144089/) on 16 June 2021. diff --git a/docker/test/integration/runner/Dockerfile b/docker/test/integration/runner/Dockerfile index 783e689ed01..cff49a805fa 100644 --- a/docker/test/integration/runner/Dockerfile +++ b/docker/test/integration/runner/Dockerfile @@ -80,7 +80,8 @@ RUN python3 -m pip install \ redis \ tzlocal \ urllib3 \ - requests-kerberos + requests-kerberos \ + pyhdfs COPY modprobe.sh /usr/local/bin/modprobe COPY dockerd-entrypoint.sh /usr/local/bin/ diff --git a/docker/test/testflows/runner/Dockerfile b/docker/test/testflows/runner/Dockerfile index ae95c18bc14..9fa028fedca 100644 --- a/docker/test/testflows/runner/Dockerfile +++ b/docker/test/testflows/runner/Dockerfile @@ -73,4 +73,4 @@ RUN set -x \ VOLUME /var/lib/docker EXPOSE 2375 ENTRYPOINT ["dockerd-entrypoint.sh"] -CMD ["sh", "-c", "python3 regression.py --no-color -o classic --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json; /usr/local/bin/process_testflows_result.py || echo -e 'failure\tCannot parse results' > check_status.tsv"] +CMD ["sh", "-c", "python3 regression.py --no-color -o new-fails --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json; /usr/local/bin/process_testflows_result.py || echo -e 'failure\tCannot parse results' > check_status.tsv; find * -type f | grep _instances | grep clickhouse-server | xargs -n1 tar -rvf clickhouse_logs.tar; gzip -9 clickhouse_logs.tar"] diff --git a/docs/en/sql-reference/statements/system.md b/docs/en/sql-reference/statements/system.md index 7871894ccac..0d8d9de3306 100644 --- a/docs/en/sql-reference/statements/system.md +++ b/docs/en/sql-reference/statements/system.md @@ -199,7 +199,7 @@ Provides possibility to start background move data according to [TTL table expre Returns `Ok.` even if table doesn’t exist. Returns error when database doesn’t exist: ``` sql -SYSTEM STOP MOVES [[db.]merge_tree_family_table_name] +SYSTEM START MOVES [[db.]merge_tree_family_table_name] ``` ## Managing ReplicatedMergeTree Tables {#query-language-system-replicated} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a32817928fc..26a68fcbd14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,6 +101,7 @@ endif() if (USE_HDFS) add_headers_and_sources(dbms Storages/HDFS) + add_headers_and_sources(dbms Disks/HDFS) endif() list (APPEND clickhouse_common_io_sources ${CONFIG_BUILD}) diff --git a/src/Common/CurrentMemoryTracker.cpp b/src/Common/CurrentMemoryTracker.cpp index 737f2566efc..d38a5a9c70c 100644 --- a/src/Common/CurrentMemoryTracker.cpp +++ b/src/Common/CurrentMemoryTracker.cpp @@ -27,30 +27,45 @@ namespace CurrentMemoryTracker using DB::current_thread; -void alloc(Int64 size) +namespace { - if (auto * memory_tracker = getMemoryTracker()) + void allocImpl(Int64 size, bool throw_if_memory_exceeded) { - if (current_thread) + if (auto * memory_tracker = getMemoryTracker()) { - current_thread->untracked_memory += size; - if (current_thread->untracked_memory > current_thread->untracked_memory_limit) + if (current_thread) { - /// Zero untracked before track. If tracker throws out-of-limit we would be able to alloc up to untracked_memory_limit bytes - /// more. It could be useful to enlarge Exception message in rethrow logic. - Int64 tmp = current_thread->untracked_memory; - current_thread->untracked_memory = 0; - memory_tracker->alloc(tmp); + current_thread->untracked_memory += size; + if (current_thread->untracked_memory > current_thread->untracked_memory_limit) + { + /// Zero untracked before track. If tracker throws out-of-limit we would be able to alloc up to untracked_memory_limit bytes + /// more. It could be useful to enlarge Exception message in rethrow logic. + Int64 tmp = current_thread->untracked_memory; + current_thread->untracked_memory = 0; + memory_tracker->allocImpl(tmp, throw_if_memory_exceeded); + } + } + /// total_memory_tracker only, ignore untracked_memory + else + { + memory_tracker->allocImpl(size, throw_if_memory_exceeded); } } - /// total_memory_tracker only, ignore untracked_memory - else - { - memory_tracker->alloc(size); - } } } +void alloc(Int64 size) +{ + bool throw_if_memory_exceeded = true; + allocImpl(size, throw_if_memory_exceeded); +} + +void allocNoThrow(Int64 size) +{ + bool throw_if_memory_exceeded = false; + allocImpl(size, throw_if_memory_exceeded); +} + void realloc(Int64 old_size, Int64 new_size) { Int64 addition = new_size - old_size; diff --git a/src/Common/CurrentMemoryTracker.h b/src/Common/CurrentMemoryTracker.h index 3a9e8990d66..5090b7c3687 100644 --- a/src/Common/CurrentMemoryTracker.h +++ b/src/Common/CurrentMemoryTracker.h @@ -6,6 +6,7 @@ namespace CurrentMemoryTracker { void alloc(Int64 size); + void allocNoThrow(Int64 size); void realloc(Int64 old_size, Int64 new_size); void free(Int64 size); } diff --git a/src/Common/MemoryTracker.cpp b/src/Common/MemoryTracker.cpp index e4c4e0b0ab1..e9ad40075e6 100644 --- a/src/Common/MemoryTracker.cpp +++ b/src/Common/MemoryTracker.cpp @@ -128,7 +128,7 @@ void MemoryTracker::logMemoryUsage(Int64 current) const } -void MemoryTracker::alloc(Int64 size) +void MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceeded) { if (size < 0) throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Negative size ({}) is passed to MemoryTracker. It is a bug.", size); @@ -137,7 +137,7 @@ void MemoryTracker::alloc(Int64 size) { /// Since the BlockerInThread should respect the level, we should go to the next parent. if (auto * loaded_next = parent.load(std::memory_order_relaxed)) - loaded_next->alloc(size); + loaded_next->allocImpl(size, throw_if_memory_exceeded); return; } @@ -173,7 +173,7 @@ void MemoryTracker::alloc(Int64 size) } #ifdef MEMORY_TRACKER_DEBUG_CHECKS - if (unlikely(_memory_tracker_always_throw_logical_error_on_allocation)) + if (unlikely(_memory_tracker_always_throw_logical_error_on_allocation) && throw_if_memory_exceeded) { _memory_tracker_always_throw_logical_error_on_allocation = false; throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Memory tracker: allocations not allowed."); @@ -181,21 +181,28 @@ void MemoryTracker::alloc(Int64 size) #endif std::bernoulli_distribution fault(fault_probability); - if (unlikely(fault_probability && fault(thread_local_rng)) && memoryTrackerCanThrow(level, true)) + if (unlikely(fault_probability && fault(thread_local_rng)) && memoryTrackerCanThrow(level, true) && throw_if_memory_exceeded) { + ProfileEvents::increment(ProfileEvents::QueryMemoryLimitExceeded); + amount.fetch_sub(size, std::memory_order_relaxed); + /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc BlockerInThread untrack_lock(VariableContext::Global); ProfileEvents::increment(ProfileEvents::QueryMemoryLimitExceeded); const auto * description = description_ptr.load(std::memory_order_relaxed); amount.fetch_sub(size, std::memory_order_relaxed); - throw DB::Exception(DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED, - "Memory tracker{}{}: fault injected. Would use {} (attempt to allocate chunk of {} bytes), maximum: {}", - description ? " " : "", description ? description : "", - formatReadableSizeWithBinarySuffix(will_be), - size, formatReadableSizeWithBinarySuffix(current_hard_limit)); + throw DB::Exception( + DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED, + "Memory tracker{}{}: fault injected. Would use {} (attempt to allocate chunk of {} bytes), maximum: {}", + description ? " " : "", + description ? description : "", + formatReadableSizeWithBinarySuffix(will_be), + size, + formatReadableSizeWithBinarySuffix(current_hard_limit)); } + if (unlikely(current_profiler_limit && will_be > current_profiler_limit)) { BlockerInThread untrack_lock(VariableContext::Global); @@ -210,36 +217,59 @@ void MemoryTracker::alloc(Int64 size) DB::TraceCollector::collect(DB::TraceType::MemorySample, StackTrace(), size); } - if (unlikely(current_hard_limit && will_be > current_hard_limit) && memoryTrackerCanThrow(level, false)) + if (unlikely(current_hard_limit && will_be > current_hard_limit) && memoryTrackerCanThrow(level, false) && throw_if_memory_exceeded) { /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc BlockerInThread untrack_lock(VariableContext::Global); - ProfileEvents::increment(ProfileEvents::QueryMemoryLimitExceeded); const auto * description = description_ptr.load(std::memory_order_relaxed); - amount.fetch_sub(size, std::memory_order_relaxed); - throw DB::Exception(DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED, - "Memory limit{}{} exceeded: would use {} (attempt to allocate chunk of {} bytes), maximum: {}", - description ? " " : "", description ? description : "", - formatReadableSizeWithBinarySuffix(will_be), - size, formatReadableSizeWithBinarySuffix(current_hard_limit)); + throw DB::Exception( + DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED, + "Memory limit{}{} exceeded: would use {} (attempt to allocate chunk of {} bytes), maximum: {}", + description ? " " : "", + description ? description : "", + formatReadableSizeWithBinarySuffix(will_be), + size, + formatReadableSizeWithBinarySuffix(current_hard_limit)); } - updatePeak(will_be); + if (throw_if_memory_exceeded) + { + /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc + BlockerInThread untrack_lock(VariableContext::Global); + bool log_memory_usage = true; + updatePeak(will_be, log_memory_usage); + } + else + { + bool log_memory_usage = false; + updatePeak(will_be, log_memory_usage); + } if (auto * loaded_next = parent.load(std::memory_order_relaxed)) - loaded_next->alloc(size); + loaded_next->allocImpl(size, throw_if_memory_exceeded); } +void MemoryTracker::alloc(Int64 size) +{ + bool throw_if_memory_exceeded = true; + allocImpl(size, throw_if_memory_exceeded); +} -void MemoryTracker::updatePeak(Int64 will_be) +void MemoryTracker::allocNoThrow(Int64 size) +{ + bool throw_if_memory_exceeded = false; + allocImpl(size, throw_if_memory_exceeded); +} + +void MemoryTracker::updatePeak(Int64 will_be, bool log_memory_usage) { auto peak_old = peak.load(std::memory_order_relaxed); if (will_be > peak_old) /// Races doesn't matter. Could rewrite with CAS, but not worth. { peak.store(will_be, std::memory_order_relaxed); - if ((level == VariableContext::Process || level == VariableContext::Global) + if (log_memory_usage && (level == VariableContext::Process || level == VariableContext::Global) && will_be / log_peak_memory_usage_every > peak_old / log_peak_memory_usage_every) logMemoryUsage(will_be); } @@ -317,7 +347,9 @@ void MemoryTracker::reset() void MemoryTracker::set(Int64 to) { amount.store(to, std::memory_order_relaxed); - updatePeak(to); + + bool log_memory_usage = true; + updatePeak(to, log_memory_usage); } diff --git a/src/Common/MemoryTracker.h b/src/Common/MemoryTracker.h index 4dada6f4ef7..e57fc2e0b75 100644 --- a/src/Common/MemoryTracker.h +++ b/src/Common/MemoryTracker.h @@ -58,7 +58,7 @@ private: /// This description will be used as prefix into log messages (if isn't nullptr) std::atomic description_ptr = nullptr; - void updatePeak(Int64 will_be); + void updatePeak(Int64 will_be, bool log_memory_usage); void logMemoryUsage(Int64 current) const; public: @@ -73,6 +73,10 @@ public: */ void alloc(Int64 size); + void allocNoThrow(Int64 size); + + void allocImpl(Int64 size, bool throw_if_memory_exceeded); + void realloc(Int64 old_size, Int64 new_size) { Int64 addition = new_size - old_size; diff --git a/src/Common/ThreadPool.cpp b/src/Common/ThreadPool.cpp index 18d952c9f73..b9bd6c75708 100644 --- a/src/Common/ThreadPool.cpp +++ b/src/Common/ThreadPool.cpp @@ -113,12 +113,22 @@ ReturnType ThreadPoolImpl::scheduleImpl(Job job, int priority, std::opti if (shutdown) return on_error(); - jobs.emplace(std::move(job), priority); - ++scheduled_jobs; + /// We must not to allocate any memory after we emplaced a job in a queue. + /// Because if an exception would be thrown, we won't notify a thread about job occurrence. - if (threads.size() < std::min(max_threads, scheduled_jobs)) + /// Check if there are enough threads to process job. + if (threads.size() < std::min(max_threads, scheduled_jobs + 1)) { - threads.emplace_front(); + try + { + threads.emplace_front(); + } + catch (...) + { + /// Most likely this is a std::bad_alloc exception + return on_error(); + } + try { threads.front() = Thread([this, it = threads.begin()] { worker(it); }); @@ -126,19 +136,15 @@ ReturnType ThreadPoolImpl::scheduleImpl(Job job, int priority, std::opti catch (...) { threads.pop_front(); - - /// Remove the job and return error to caller. - /// Note that if we have allocated at least one thread, we may continue - /// (one thread is enough to process all jobs). - /// But this condition indicate an error nevertheless and better to refuse. - - jobs.pop(); - --scheduled_jobs; return on_error(); } } + + jobs.emplace(std::move(job), priority); + ++scheduled_jobs; + new_job_or_shutdown.notify_one(); } - new_job_or_shutdown.notify_one(); + return ReturnType(true); } @@ -165,6 +171,10 @@ void ThreadPoolImpl::wait() { { std::unique_lock lock(mutex); + /// Signal here just in case. + /// If threads are waiting on condition variables, but there are some jobs in the queue + /// then it will prevent us from deadlock. + new_job_or_shutdown.notify_all(); job_finished.wait(lock, [this] { return scheduled_jobs == 0; }); if (first_exception) diff --git a/src/Common/ZooKeeper/ZooKeeperCommon.h b/src/Common/ZooKeeper/ZooKeeperCommon.h index 2a317da2622..ced154133b5 100644 --- a/src/Common/ZooKeeper/ZooKeeperCommon.h +++ b/src/Common/ZooKeeper/ZooKeeperCommon.h @@ -227,7 +227,7 @@ struct ZooKeeperExistsRequest final : ExistsRequest, ZooKeeperRequest void readImpl(ReadBuffer & in) override; ZooKeeperResponsePtr makeResponse() const override; - bool isReadRequest() const override { return !has_watch; } + bool isReadRequest() const override { return true; } size_t bytesSize() const override { return ExistsRequest::bytesSize() + sizeof(xid) + sizeof(has_watch); } }; @@ -248,7 +248,7 @@ struct ZooKeeperGetRequest final : GetRequest, ZooKeeperRequest void readImpl(ReadBuffer & in) override; ZooKeeperResponsePtr makeResponse() const override; - bool isReadRequest() const override { return !has_watch; } + bool isReadRequest() const override { return true; } size_t bytesSize() const override { return GetRequest::bytesSize() + sizeof(xid) + sizeof(has_watch); } }; @@ -291,7 +291,7 @@ struct ZooKeeperListRequest : ListRequest, ZooKeeperRequest void writeImpl(WriteBuffer & out) const override; void readImpl(ReadBuffer & in) override; ZooKeeperResponsePtr makeResponse() const override; - bool isReadRequest() const override { return !has_watch; } + bool isReadRequest() const override { return true; } size_t bytesSize() const override { return ListRequest::bytesSize() + sizeof(xid) + sizeof(has_watch); } }; @@ -325,7 +325,7 @@ struct ZooKeeperCheckRequest final : CheckRequest, ZooKeeperRequest void readImpl(ReadBuffer & in) override; ZooKeeperResponsePtr makeResponse() const override; - bool isReadRequest() const override { return !has_watch; } + bool isReadRequest() const override { return true; } size_t bytesSize() const override { return CheckRequest::bytesSize() + sizeof(xid) + sizeof(has_watch); } }; diff --git a/src/Common/new_delete.cpp b/src/Common/new_delete.cpp index be3724666f8..56173fb108a 100644 --- a/src/Common/new_delete.cpp +++ b/src/Common/new_delete.cpp @@ -41,9 +41,9 @@ struct InitializeJemallocZoneAllocatorForOSX namespace Memory { -inline ALWAYS_INLINE void trackMemory(std::size_t size) +inline ALWAYS_INLINE size_t getActualAllocationSize(size_t size) { - std::size_t actual_size = size; + size_t actual_size = size; #if USE_JEMALLOC && JEMALLOC_VERSION_MAJOR >= 5 /// The nallocx() function allocates no memory, but it performs the same size computation as the mallocx() function @@ -52,21 +52,13 @@ inline ALWAYS_INLINE void trackMemory(std::size_t size) actual_size = nallocx(size, 0); #endif - CurrentMemoryTracker::alloc(actual_size); + return actual_size; } -inline ALWAYS_INLINE bool trackMemoryNoExcept(std::size_t size) noexcept +inline ALWAYS_INLINE void trackMemory(std::size_t size) { - try - { - trackMemory(size); - } - catch (...) - { - return false; - } - - return true; + std::size_t actual_size = getActualAllocationSize(size); + CurrentMemoryTracker::allocNoThrow(actual_size); } inline ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]] = 0) noexcept @@ -98,27 +90,29 @@ inline ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t void * operator new(std::size_t size) { Memory::trackMemory(size); + return Memory::newImpl(size); } void * operator new[](std::size_t size) { Memory::trackMemory(size); + return Memory::newImpl(size); } void * operator new(std::size_t size, const std::nothrow_t &) noexcept { - if (likely(Memory::trackMemoryNoExcept(size))) - return Memory::newNoExept(size); - return nullptr; + Memory::trackMemory(size); + + return Memory::newNoExept(size); } void * operator new[](std::size_t size, const std::nothrow_t &) noexcept { - if (likely(Memory::trackMemoryNoExcept(size))) - return Memory::newNoExept(size); - return nullptr; + Memory::trackMemory(size); + + return Memory::newNoExept(size); } /// delete diff --git a/src/Common/ya.make b/src/Common/ya.make index 6bc15da2cdb..57b60e9cce5 100644 --- a/src/Common/ya.make +++ b/src/Common/ya.make @@ -18,7 +18,6 @@ PEERDIR( contrib/libs/openssl contrib/libs/poco/NetSSL_OpenSSL contrib/libs/re2 - contrib/libs/cxxsupp/libcxxabi-parts contrib/restricted/dragonbox ) diff --git a/src/Common/ya.make.in b/src/Common/ya.make.in index 459266a54e7..fd6a805891e 100644 --- a/src/Common/ya.make.in +++ b/src/Common/ya.make.in @@ -17,7 +17,6 @@ PEERDIR( contrib/libs/openssl contrib/libs/poco/NetSSL_OpenSSL contrib/libs/re2 - contrib/libs/cxxsupp/libcxxabi-parts contrib/restricted/dragonbox ) diff --git a/src/Disks/DiskType.h b/src/Disks/DiskType.h index 4e0ae226af4..a5c23fe2c2c 100644 --- a/src/Disks/DiskType.h +++ b/src/Disks/DiskType.h @@ -11,7 +11,8 @@ struct DiskType { Local, RAM, - S3 + S3, + HDFS }; static String toString(Type disk_type) { @@ -23,10 +24,11 @@ struct DiskType return "memory"; case Type::S3: return "s3"; + case Type::HDFS: + return "hdfs"; } __builtin_unreachable(); } }; } - diff --git a/src/Disks/HDFS/DiskHDFS.cpp b/src/Disks/HDFS/DiskHDFS.cpp new file mode 100644 index 00000000000..0648fd9f08c --- /dev/null +++ b/src/Disks/HDFS/DiskHDFS.cpp @@ -0,0 +1,194 @@ +#include + +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int BAD_ARGUMENTS; + extern const int LOGICAL_ERROR; +} + + +class HDFSPathKeeper : public RemoteFSPathKeeper +{ +public: + using Chunk = std::vector; + using Chunks = std::list; + + explicit HDFSPathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} + + void addPath(const String & path) override + { + if (chunks.empty() || chunks.back().size() >= chunk_limit) + { + chunks.push_back(Chunks::value_type()); + chunks.back().reserve(chunk_limit); + } + chunks.back().push_back(path.data()); + } + + void removePaths(const std::function & remove_chunk_func) + { + for (auto & chunk : chunks) + remove_chunk_func(std::move(chunk)); + } + +private: + Chunks chunks; +}; + + +/// Reads data from HDFS using stored paths in metadata. +class ReadIndirectBufferFromHDFS final : public ReadIndirectBufferFromRemoteFS +{ +public: + ReadIndirectBufferFromHDFS( + const Poco::Util::AbstractConfiguration & config_, + const String & hdfs_uri_, + DiskHDFS::Metadata metadata_, + size_t buf_size_) + : ReadIndirectBufferFromRemoteFS(metadata_) + , config(config_) + , buf_size(buf_size_) + { + const size_t begin_of_path = hdfs_uri_.find('/', hdfs_uri_.find("//") + 2); + hdfs_directory = hdfs_uri_.substr(begin_of_path); + hdfs_uri = hdfs_uri_.substr(0, begin_of_path); + } + + std::unique_ptr createReadBuffer(const String & path) override + { + return std::make_unique(hdfs_uri, hdfs_directory + path, config, buf_size); + } + +private: + const Poco::Util::AbstractConfiguration & config; + String hdfs_uri; + String hdfs_directory; + size_t buf_size; +}; + + +DiskHDFS::DiskHDFS( + const String & disk_name_, + const String & hdfs_root_path_, + SettingsPtr settings_, + const String & metadata_path_, + const Poco::Util::AbstractConfiguration & config_) + : IDiskRemote(disk_name_, hdfs_root_path_, metadata_path_, "DiskHDFS", settings_->thread_pool_size) + , config(config_) + , hdfs_builder(createHDFSBuilder(hdfs_root_path_, config)) + , hdfs_fs(createHDFSFS(hdfs_builder.get())) + , settings(std::move(settings_)) +{ +} + + +std::unique_ptr DiskHDFS::readFile(const String & path, size_t buf_size, size_t, size_t, size_t, MMappedFileCache *) const +{ + auto metadata = readMeta(path); + + LOG_DEBUG(log, + "Read from file by path: {}. Existing HDFS objects: {}", + backQuote(metadata_path + path), metadata.remote_fs_objects.size()); + + auto reader = std::make_unique(config, remote_fs_root_path, metadata, buf_size); + return std::make_unique(std::move(reader), settings->min_bytes_for_seek); +} + + +std::unique_ptr DiskHDFS::writeFile(const String & path, size_t buf_size, WriteMode mode) +{ + auto metadata = readOrCreateMetaForWriting(path, mode); + + /// Path to store new HDFS object. + auto file_name = getRandomName(); + auto hdfs_path = remote_fs_root_path + file_name; + + LOG_DEBUG(log, "{} to file by path: {}. HDFS path: {}", mode == WriteMode::Rewrite ? "Write" : "Append", + backQuote(metadata_path + path), remote_fs_root_path + hdfs_path); + + /// Single O_WRONLY in libhdfs adds O_TRUNC + auto hdfs_buffer = std::make_unique(hdfs_path, + config, buf_size, + mode == WriteMode::Rewrite ? O_WRONLY : O_WRONLY | O_APPEND); + + return std::make_unique>(std::move(hdfs_buffer), + std::move(metadata), + file_name); +} + + +RemoteFSPathKeeperPtr DiskHDFS::createFSPathKeeper() const +{ + return std::make_shared(settings->objects_chunk_size_to_delete); +} + + +void DiskHDFS::removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) +{ + auto * hdfs_paths_keeper = dynamic_cast(fs_paths_keeper.get()); + if (hdfs_paths_keeper) + hdfs_paths_keeper->removePaths([&](std::vector && chunk) + { + for (const auto & hdfs_object_path : chunk) + { + const String & hdfs_path = hdfs_object_path; + const size_t begin_of_path = hdfs_path.find('/', hdfs_path.find("//") + 2); + + /// Add path from root to file name + int res = hdfsDelete(hdfs_fs.get(), hdfs_path.substr(begin_of_path).c_str(), 0); + if (res == -1) + throw Exception(ErrorCodes::LOGICAL_ERROR, "HDFSDelete failed with path: " + hdfs_path); + } + }); +} + + +namespace +{ +std::unique_ptr getSettings(const Poco::Util::AbstractConfiguration & config, const String & config_prefix) +{ + return std::make_unique( + config.getUInt64(config_prefix + ".min_bytes_for_seek", 1024 * 1024), + config.getInt(config_prefix + ".thread_pool_size", 16), + config.getInt(config_prefix + ".objects_chunk_size_to_delete", 1000)); +} +} + +void registerDiskHDFS(DiskFactory & factory) +{ + auto creator = [](const String & name, + const Poco::Util::AbstractConfiguration & config, + const String & config_prefix, + ContextConstPtr context_) -> DiskPtr + { + Poco::File disk{context_->getPath() + "disks/" + name}; + disk.createDirectories(); + + String uri{config.getString(config_prefix + ".endpoint")}; + + if (uri.back() != '/') + throw Exception(ErrorCodes::BAD_ARGUMENTS, "HDFS path must ends with '/', but '{}' doesn't.", uri); + + String metadata_path = context_->getPath() + "disks/" + name + "/"; + + return std::make_shared( + name, uri, + getSettings(config, config_prefix), + metadata_path, config); + }; + + factory.registerDiskType("hdfs", creator); +} + +} diff --git a/src/Disks/HDFS/DiskHDFS.h b/src/Disks/HDFS/DiskHDFS.h new file mode 100644 index 00000000000..49fdf44728b --- /dev/null +++ b/src/Disks/HDFS/DiskHDFS.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include + + +namespace DB +{ + +struct DiskHDFSSettings +{ + size_t min_bytes_for_seek; + int thread_pool_size; + int objects_chunk_size_to_delete; + + DiskHDFSSettings( + int min_bytes_for_seek_, + int thread_pool_size_, + int objects_chunk_size_to_delete_) + : min_bytes_for_seek(min_bytes_for_seek_) + , thread_pool_size(thread_pool_size_) + , objects_chunk_size_to_delete(objects_chunk_size_to_delete_) {} +}; + + +/** + * Storage for persisting data in HDFS and metadata on the local disk. + * Files are represented by file in local filesystem (clickhouse_root/disks/disk_name/path/to/file) + * that contains HDFS object key with actual data. + */ +class DiskHDFS final : public IDiskRemote +{ +public: + using SettingsPtr = std::unique_ptr; + + DiskHDFS( + const String & disk_name_, + const String & hdfs_root_path_, + SettingsPtr settings_, + const String & metadata_path_, + const Poco::Util::AbstractConfiguration & config_); + + DiskType::Type getType() const override { return DiskType::Type::HDFS; } + + std::unique_ptr readFile( + const String & path, + size_t buf_size, + size_t estimated_size, + size_t aio_threshold, + size_t mmap_threshold, + MMappedFileCache * mmap_cache) const override; + + std::unique_ptr writeFile(const String & path, size_t buf_size, WriteMode mode) override; + + void removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) override; + + RemoteFSPathKeeperPtr createFSPathKeeper() const override; + +private: + String getRandomName() { return toString(UUIDHelpers::generateV4()); } + + const Poco::Util::AbstractConfiguration & config; + + HDFSBuilderWrapper hdfs_builder; + HDFSFSPtr hdfs_fs; + + SettingsPtr settings; +}; + +} diff --git a/src/Disks/IDisk.h b/src/Disks/IDisk.h index 0b4e5779ea9..a42a60959c5 100644 --- a/src/Disks/IDisk.h +++ b/src/Disks/IDisk.h @@ -7,6 +7,7 @@ #include #include #include +#include "Disks/Executor.h" #include #include @@ -178,17 +179,17 @@ public: virtual void removeRecursive(const String & path) = 0; /// Remove file. Throws exception if file doesn't exists or if directory is not empty. - /// Differs from removeFile for S3 disks + /// Differs from removeFile for S3/HDFS disks /// Second bool param is a flag to remove (true) or keep (false) shared data on S3 virtual void removeSharedFile(const String & path, bool) { removeFile(path); } /// Remove file or directory with all children. Use with extra caution. Throws exception if file doesn't exists. - /// Differs from removeRecursive for S3 disks + /// Differs from removeRecursive for S3/HDFS disks /// Second bool param is a flag to remove (true) or keep (false) shared data on S3 virtual void removeSharedRecursive(const String & path, bool) { removeRecursive(path); } /// Remove file or directory if it exists. - /// Differs from removeFileIfExists for S3 disks + /// Differs from removeFileIfExists for S3/HDFS disks /// Second bool param is a flag to remove (true) or keep (false) shared data on S3 virtual void removeSharedFileIfExists(const String & path, bool) { removeFileIfExists(path); } diff --git a/src/Disks/IDiskRemote.cpp b/src/Disks/IDiskRemote.cpp new file mode 100644 index 00000000000..bcb399f5d07 --- /dev/null +++ b/src/Disks/IDiskRemote.cpp @@ -0,0 +1,487 @@ +#include + +#include "Disks/DiskFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int INCORRECT_DISK_INDEX; + extern const int UNKNOWN_FORMAT; + extern const int FILE_ALREADY_EXISTS; + extern const int PATH_ACCESS_DENIED;; + extern const int CANNOT_DELETE_DIRECTORY; +} + + +/// Load metadata by path or create empty if `create` flag is set. +IDiskRemote::Metadata::Metadata( + const String & remote_fs_root_path_, + const String & disk_path_, + const String & metadata_file_path_, + bool create) + : remote_fs_root_path(remote_fs_root_path_) + , disk_path(disk_path_) + , metadata_file_path(metadata_file_path_) + , total_size(0), remote_fs_objects(0), ref_count(0) +{ + if (create) + return; + + try + { + ReadBufferFromFile buf(disk_path + metadata_file_path, 1024); /* reasonable buffer size for small file */ + + UInt32 version; + readIntText(version, buf); + + if (version < VERSION_ABSOLUTE_PATHS || version > VERSION_READ_ONLY_FLAG) + throw Exception( + ErrorCodes::UNKNOWN_FORMAT, + "Unknown metadata file version. Path: {}. Version: {}. Maximum expected version: {}", + disk_path + metadata_file_path, toString(version), toString(VERSION_READ_ONLY_FLAG)); + + assertChar('\n', buf); + + UInt32 remote_fs_objects_count; + readIntText(remote_fs_objects_count, buf); + assertChar('\t', buf); + readIntText(total_size, buf); + assertChar('\n', buf); + remote_fs_objects.resize(remote_fs_objects_count); + + for (size_t i = 0; i < remote_fs_objects_count; ++i) + { + String remote_fs_object_path; + size_t remote_fs_object_size; + readIntText(remote_fs_object_size, buf); + assertChar('\t', buf); + readEscapedString(remote_fs_object_path, buf); + if (version == VERSION_ABSOLUTE_PATHS) + { + if (!boost::algorithm::starts_with(remote_fs_object_path, remote_fs_root_path)) + throw Exception( + ErrorCodes::UNKNOWN_FORMAT, + "Path in metadata does not correspond S3 root path. Path: {}, root path: {}, disk path: {}", + remote_fs_object_path, remote_fs_root_path, disk_path_); + + remote_fs_object_path = remote_fs_object_path.substr(remote_fs_root_path.size()); + } + assertChar('\n', buf); + remote_fs_objects[i] = {remote_fs_object_path, remote_fs_object_size}; + } + + readIntText(ref_count, buf); + assertChar('\n', buf); + + if (version >= VERSION_READ_ONLY_FLAG) + { + readBoolText(read_only, buf); + assertChar('\n', buf); + } + } + catch (Exception & e) + { + if (e.code() == ErrorCodes::UNKNOWN_FORMAT) + throw; + + throw Exception("Failed to read metadata file", e, ErrorCodes::UNKNOWN_FORMAT); + } +} + +void IDiskRemote::Metadata::addObject(const String & path, size_t size) +{ + total_size += size; + remote_fs_objects.emplace_back(path, size); +} + +/// Fsync metadata file if 'sync' flag is set. +void IDiskRemote::Metadata::save(bool sync) +{ + WriteBufferFromFile buf(disk_path + metadata_file_path, 1024); + + writeIntText(VERSION_RELATIVE_PATHS, buf); + writeChar('\n', buf); + + writeIntText(remote_fs_objects.size(), buf); + writeChar('\t', buf); + writeIntText(total_size, buf); + writeChar('\n', buf); + + for (const auto & [remote_fs_object_path, remote_fs_object_size] : remote_fs_objects) + { + writeIntText(remote_fs_object_size, buf); + writeChar('\t', buf); + writeEscapedString(remote_fs_object_path, buf); + writeChar('\n', buf); + } + + writeIntText(ref_count, buf); + writeChar('\n', buf); + + writeBoolText(read_only, buf); + writeChar('\n', buf); + + buf.finalize(); + if (sync) + buf.sync(); +} + +IDiskRemote::Metadata IDiskRemote::readOrCreateMetaForWriting(const String & path, WriteMode mode) +{ + bool exist = exists(path); + if (exist) + { + auto metadata = readMeta(path); + if (metadata.read_only) + throw Exception("File is read-only: " + path, ErrorCodes::PATH_ACCESS_DENIED); + + if (mode == WriteMode::Rewrite) + removeFile(path); /// Remove for re-write. + else + return metadata; + } + + auto metadata = createMeta(path); + /// Save empty metadata to disk to have ability to get file size while buffer is not finalized. + metadata.save(); + + return metadata; +} + + +IDiskRemote::Metadata IDiskRemote::readMeta(const String & path) const +{ + return Metadata(remote_fs_root_path, metadata_path, path); +} + + +IDiskRemote::Metadata IDiskRemote::createMeta(const String & path) const +{ + return Metadata(remote_fs_root_path, metadata_path, path, true); +} + + +void IDiskRemote::removeMeta(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper) +{ + LOG_DEBUG(log, "Remove file by path: {}", backQuote(metadata_path + path)); + + Poco::File file(metadata_path + path); + + if (!file.isFile()) + throw Exception(ErrorCodes::CANNOT_DELETE_DIRECTORY, "Path '{}' is a directory", path); + + try + { + auto metadata = readMeta(path); + + /// If there is no references - delete content from remote FS. + if (metadata.ref_count == 0) + { + file.remove(); + for (const auto & [remote_fs_object_path, _] : metadata.remote_fs_objects) + fs_paths_keeper->addPath(remote_fs_root_path + remote_fs_object_path); + } + else /// In other case decrement number of references, save metadata and delete file. + { + --metadata.ref_count; + metadata.save(); + file.remove(); + } + } + catch (const Exception & e) + { + /// If it's impossible to read meta - just remove it from FS. + if (e.code() == ErrorCodes::UNKNOWN_FORMAT) + { + LOG_WARNING(log, + "Metadata file {} can't be read by reason: {}. Removing it forcibly.", + backQuote(path), e.nested() ? e.nested()->message() : e.message()); + file.remove(); + } + else + throw; + } +} + + +void IDiskRemote::removeMetaRecursive(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper) +{ + checkStackSize(); /// This is needed to prevent stack overflow in case of cyclic symlinks. + + Poco::File file(metadata_path + path); + if (file.isFile()) + { + removeMeta(path, fs_paths_keeper); + } + else + { + for (auto it{iterateDirectory(path)}; it->isValid(); it->next()) + removeMetaRecursive(it->path(), fs_paths_keeper); + file.remove(); + } +} + +DiskPtr DiskRemoteReservation::getDisk(size_t i) const +{ + if (i != 0) + throw Exception("Can't use i != 0 with single disk reservation", ErrorCodes::INCORRECT_DISK_INDEX); + return disk; +} + + +void DiskRemoteReservation::update(UInt64 new_size) +{ + std::lock_guard lock(disk->reservation_mutex); + disk->reserved_bytes -= size; + size = new_size; + disk->reserved_bytes += size; +} + + +DiskRemoteReservation::~DiskRemoteReservation() +{ + try + { + std::lock_guard lock(disk->reservation_mutex); + if (disk->reserved_bytes < size) + { + disk->reserved_bytes = 0; + LOG_ERROR(disk->log, "Unbalanced reservations size for disk '{}'.", disk->getName()); + } + else + { + disk->reserved_bytes -= size; + } + + if (disk->reservation_count == 0) + LOG_ERROR(disk->log, "Unbalanced reservation count for disk '{}'.", disk->getName()); + else + --disk->reservation_count; + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } +} + + +IDiskRemote::IDiskRemote( + const String & name_, + const String & remote_fs_root_path_, + const String & metadata_path_, + const String & log_name_, + size_t thread_pool_size) + : IDisk(std::make_unique(log_name_, thread_pool_size)) + , log(&Poco::Logger::get(log_name_)) + , name(name_) + , remote_fs_root_path(remote_fs_root_path_) + , metadata_path(metadata_path_) +{ +} + + +bool IDiskRemote::exists(const String & path) const +{ + return Poco::File(metadata_path + path).exists(); +} + + +bool IDiskRemote::isFile(const String & path) const +{ + return Poco::File(metadata_path + path).isFile(); +} + + +void IDiskRemote::createFile(const String & path) +{ + /// Create empty metadata file. + auto metadata = createMeta(path); + metadata.save(); +} + + +size_t IDiskRemote::getFileSize(const String & path) const +{ + auto metadata = readMeta(path); + return metadata.total_size; +} + + +void IDiskRemote::moveFile(const String & from_path, const String & to_path) +{ + if (exists(to_path)) + throw Exception("File already exists: " + to_path, ErrorCodes::FILE_ALREADY_EXISTS); + + Poco::File(metadata_path + from_path).renameTo(metadata_path + to_path); +} + + +void IDiskRemote::replaceFile(const String & from_path, const String & to_path) +{ + if (exists(to_path)) + { + const String tmp_path = to_path + ".old"; + moveFile(to_path, tmp_path); + moveFile(from_path, to_path); + removeFile(tmp_path); + } + else + moveFile(from_path, to_path); +} + + +void IDiskRemote::removeFileIfExists(const String & path) +{ + RemoteFSPathKeeperPtr fs_paths_keeper = createFSPathKeeper(); + if (Poco::File(metadata_path + path).exists()) + { + removeMeta(path, fs_paths_keeper); + removeFromRemoteFS(fs_paths_keeper); + } +} + + +void IDiskRemote::removeSharedFile(const String & path, bool keep_in_remote_fs) +{ + RemoteFSPathKeeperPtr fs_paths_keeper = createFSPathKeeper(); + removeMeta(path, fs_paths_keeper); + if (!keep_in_remote_fs) + removeFromRemoteFS(fs_paths_keeper); +} + + +void IDiskRemote::removeSharedRecursive(const String & path, bool keep_in_remote_fs) +{ + RemoteFSPathKeeperPtr fs_paths_keeper = createFSPathKeeper(); + removeMetaRecursive(path, fs_paths_keeper); + if (!keep_in_remote_fs) + removeFromRemoteFS(fs_paths_keeper); +} + + +void IDiskRemote::setReadOnly(const String & path) +{ + /// We should store read only flag inside metadata file (instead of using FS flag), + /// because we modify metadata file when create hard-links from it. + auto metadata = readMeta(path); + metadata.read_only = true; + metadata.save(); +} + + +bool IDiskRemote::isDirectory(const String & path) const +{ + return Poco::File(metadata_path + path).isDirectory(); +} + + +void IDiskRemote::createDirectory(const String & path) +{ + Poco::File(metadata_path + path).createDirectory(); +} + + +void IDiskRemote::createDirectories(const String & path) +{ + Poco::File(metadata_path + path).createDirectories(); +} + + +void IDiskRemote::clearDirectory(const String & path) +{ + for (auto it{iterateDirectory(path)}; it->isValid(); it->next()) + if (isFile(it->path())) + removeFile(it->path()); +} + + +void IDiskRemote::removeDirectory(const String & path) +{ + Poco::File(metadata_path + path).remove(); +} + + +DiskDirectoryIteratorPtr IDiskRemote::iterateDirectory(const String & path) +{ + return std::make_unique(metadata_path + path, path); +} + + +void IDiskRemote::listFiles(const String & path, std::vector & file_names) +{ + for (auto it = iterateDirectory(path); it->isValid(); it->next()) + file_names.push_back(it->name()); +} + + +void IDiskRemote::setLastModified(const String & path, const Poco::Timestamp & timestamp) +{ + Poco::File(metadata_path + path).setLastModified(timestamp); +} + + +Poco::Timestamp IDiskRemote::getLastModified(const String & path) +{ + return Poco::File(metadata_path + path).getLastModified(); +} + + +void IDiskRemote::createHardLink(const String & src_path, const String & dst_path) +{ + /// Increment number of references. + auto src = readMeta(src_path); + ++src.ref_count; + src.save(); + + /// Create FS hardlink to metadata file. + DB::createHardLink(metadata_path + src_path, metadata_path + dst_path); +} + + +ReservationPtr IDiskRemote::reserve(UInt64 bytes) +{ + if (!tryReserve(bytes)) + return {}; + + return std::make_unique(std::static_pointer_cast(shared_from_this()), bytes); +} + + +bool IDiskRemote::tryReserve(UInt64 bytes) +{ + std::lock_guard lock(reservation_mutex); + if (bytes == 0) + { + LOG_DEBUG(log, "Reserving 0 bytes on remote_fs disk {}", backQuote(name)); + ++reservation_count; + return true; + } + + auto available_space = getAvailableSpace(); + UInt64 unreserved_space = available_space - std::min(available_space, reserved_bytes); + if (unreserved_space >= bytes) + { + LOG_DEBUG(log, "Reserving {} on disk {}, having unreserved {}.", + ReadableSize(bytes), backQuote(name), ReadableSize(unreserved_space)); + ++reservation_count; + reserved_bytes += bytes; + return true; + } + return false; +} + +} diff --git a/src/Disks/IDiskRemote.h b/src/Disks/IDiskRemote.h new file mode 100644 index 00000000000..b32258331a7 --- /dev/null +++ b/src/Disks/IDiskRemote.h @@ -0,0 +1,283 @@ +#pragma once +#include + +#include +#include "Disks/DiskFactory.h" +#include "Disks/Executor.h" +#include +#include +#include +#include + + +namespace DB +{ + +/// Helper class to collect paths into chunks of maximum size. +/// For s3 it is Aws::vector, for hdfs it is std::vector. +class RemoteFSPathKeeper +{ +public: + RemoteFSPathKeeper(size_t chunk_limit_) : chunk_limit(chunk_limit_) {} + + virtual ~RemoteFSPathKeeper() = default; + + virtual void addPath(const String & path) = 0; + +protected: + size_t chunk_limit; +}; + +using RemoteFSPathKeeperPtr = std::shared_ptr; + + +/// Base Disk class for remote FS's, which are not posix-compatible (DiskS3 and DiskHDFS) +class IDiskRemote : public IDisk +{ + +friend class DiskRemoteReservation; + +public: + IDiskRemote( + const String & name_, + const String & remote_fs_root_path_, + const String & metadata_path_, + const String & log_name_, + size_t thread_pool_size); + + struct Metadata; + + const String & getName() const final override { return name; } + + const String & getPath() const final override { return metadata_path; } + + Metadata readMeta(const String & path) const; + + Metadata createMeta(const String & path) const; + + Metadata readOrCreateMetaForWriting(const String & path, WriteMode mode); + + UInt64 getTotalSpace() const override { return std::numeric_limits::max(); } + + UInt64 getAvailableSpace() const override { return std::numeric_limits::max(); } + + UInt64 getUnreservedSpace() const override { return std::numeric_limits::max(); } + + UInt64 getKeepingFreeSpace() const override { return 0; } + + bool exists(const String & path) const override; + + bool isFile(const String & path) const override; + + void createFile(const String & path) override; + + size_t getFileSize(const String & path) const override; + + void moveFile(const String & from_path, const String & to_path) override; + + void replaceFile(const String & from_path, const String & to_path) override; + + void removeFile(const String & path) override { removeSharedFile(path, false); } + + void removeFileIfExists(const String & path) override; + + void removeRecursive(const String & path) override { removeSharedRecursive(path, false); } + + void removeSharedFile(const String & path, bool keep_in_remote_fs) override; + + void removeSharedRecursive(const String & path, bool keep_in_remote_fs) override; + + void listFiles(const String & path, std::vector & file_names) override; + + void setReadOnly(const String & path) override; + + bool isDirectory(const String & path) const override; + + void createDirectory(const String & path) override; + + void createDirectories(const String & path) override; + + void clearDirectory(const String & path) override; + + void moveDirectory(const String & from_path, const String & to_path) override { moveFile(from_path, to_path); } + + void removeDirectory(const String & path) override; + + DiskDirectoryIteratorPtr iterateDirectory(const String & path) override; + + void setLastModified(const String & path, const Poco::Timestamp & timestamp) override; + + Poco::Timestamp getLastModified(const String & path) override; + + void createHardLink(const String & src_path, const String & dst_path) override; + + ReservationPtr reserve(UInt64 bytes) override; + + virtual void removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) = 0; + + virtual RemoteFSPathKeeperPtr createFSPathKeeper() const = 0; + +protected: + Poco::Logger * log; + const String name; + const String remote_fs_root_path; + + const String metadata_path; + +private: + void removeMeta(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); + + void removeMetaRecursive(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); + + bool tryReserve(UInt64 bytes); + + UInt64 reserved_bytes = 0; + UInt64 reservation_count = 0; + std::mutex reservation_mutex; +}; + +using RemoteDiskPtr = std::shared_ptr; + +/// Remote FS (S3, HDFS) metadata file layout: +/// Number of FS objects, Total size of all FS objects. +/// Each FS object represents path where object located in FS and size of object. + +struct IDiskRemote::Metadata +{ + /// Metadata file version. + static constexpr UInt32 VERSION_ABSOLUTE_PATHS = 1; + static constexpr UInt32 VERSION_RELATIVE_PATHS = 2; + static constexpr UInt32 VERSION_READ_ONLY_FLAG = 3; + + using PathAndSize = std::pair; + + /// Remote FS (S3, HDFS) root path. + const String & remote_fs_root_path; + + /// Disk path. + const String & disk_path; + + /// Relative path to metadata file on local FS. + String metadata_file_path; + + /// Total size of all remote FS (S3, HDFS) objects. + size_t total_size = 0; + + /// Remote FS (S3, HDFS) objects paths and their sizes. + std::vector remote_fs_objects; + + /// Number of references (hardlinks) to this metadata file. + UInt32 ref_count = 0; + + /// Flag indicates that file is read only. + bool read_only = false; + + /// Load metadata by path or create empty if `create` flag is set. + Metadata(const String & remote_fs_root_path_, + const String & disk_path_, + const String & metadata_file_path_, + bool create = false); + + void addObject(const String & path, size_t size); + + /// Fsync metadata file if 'sync' flag is set. + void save(bool sync = false); + +}; + + +class RemoteDiskDirectoryIterator final : public IDiskDirectoryIterator +{ +public: + RemoteDiskDirectoryIterator(const String & full_path, const String & folder_path_) : iter(full_path), folder_path(folder_path_) {} + + void next() override { ++iter; } + + bool isValid() const override { return iter != Poco::DirectoryIterator(); } + + String path() const override + { + if (iter->isDirectory()) + return folder_path + iter.name() + '/'; + else + return folder_path + iter.name(); + } + + String name() const override { return iter.name(); } + +private: + Poco::DirectoryIterator iter; + String folder_path; +}; + + +class DiskRemoteReservation final : public IReservation +{ +public: + DiskRemoteReservation(const RemoteDiskPtr & disk_, UInt64 size_) + : disk(disk_), size(size_), metric_increment(CurrentMetrics::DiskSpaceReservedForMerge, size_) + { + } + + UInt64 getSize() const override { return size; } + + DiskPtr getDisk(size_t i) const override; + + Disks getDisks() const override { return {disk}; } + + void update(UInt64 new_size) override; + + ~DiskRemoteReservation() override; + +private: + RemoteDiskPtr disk; + UInt64 size; + CurrentMetrics::Increment metric_increment; +}; + + +/// Runs tasks asynchronously using thread pool. +class AsyncExecutor : public Executor +{ +public: + explicit AsyncExecutor(const String & name_, int thread_pool_size) + : name(name_) + , pool(ThreadPool(thread_pool_size)) {} + + std::future execute(std::function task) override + { + auto promise = std::make_shared>(); + pool.scheduleOrThrowOnError( + [promise, task]() + { + try + { + task(); + promise->set_value(); + } + catch (...) + { + tryLogCurrentException("Failed to run async task"); + + try + { + promise->set_exception(std::current_exception()); + } + catch (...) {} + } + }); + + return promise->get_future(); + } + + void setMaxThreads(size_t threads) + { + pool.setMaxThreads(threads); + } + +private: + String name; + ThreadPool pool; +}; + +} diff --git a/src/Disks/ReadIndirectBufferFromRemoteFS.cpp b/src/Disks/ReadIndirectBufferFromRemoteFS.cpp new file mode 100644 index 00000000000..955986e5259 --- /dev/null +++ b/src/Disks/ReadIndirectBufferFromRemoteFS.cpp @@ -0,0 +1,128 @@ +#include "ReadIndirectBufferFromRemoteFS.h" + +#if USE_AWS_S3 || USE_HDFS +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int CANNOT_SEEK_THROUGH_FILE; +} + + +template +ReadIndirectBufferFromRemoteFS::ReadIndirectBufferFromRemoteFS( + IDiskRemote::Metadata metadata_) + : metadata(std::move(metadata_)) +{ +} + +template +off_t ReadIndirectBufferFromRemoteFS::seek(off_t offset_, int whence) +{ + if (whence == SEEK_CUR) + { + /// If position within current working buffer - shift pos. + if (!working_buffer.empty() && size_t(getPosition() + offset_) < absolute_position) + { + pos += offset_; + return getPosition(); + } + else + { + absolute_position += offset_; + } + } + else if (whence == SEEK_SET) + { + /// If position within current working buffer - shift pos. + if (!working_buffer.empty() && size_t(offset_) >= absolute_position - working_buffer.size() + && size_t(offset_) < absolute_position) + { + pos = working_buffer.end() - (absolute_position - offset_); + return getPosition(); + } + else + { + absolute_position = offset_; + } + } + else + throw Exception("Only SEEK_SET or SEEK_CUR modes are allowed.", ErrorCodes::CANNOT_SEEK_THROUGH_FILE); + + current_buf = initialize(); + pos = working_buffer.end(); + + return absolute_position; +} + + +template +std::unique_ptr ReadIndirectBufferFromRemoteFS::initialize() +{ + size_t offset = absolute_position; + for (size_t i = 0; i < metadata.remote_fs_objects.size(); ++i) + { + current_buf_idx = i; + const auto & [file_path, size] = metadata.remote_fs_objects[i]; + if (size > offset) + { + auto buf = createReadBuffer(file_path); + buf->seek(offset, SEEK_SET); + return buf; + } + offset -= size; + } + return nullptr; +} + + +template +bool ReadIndirectBufferFromRemoteFS::nextImpl() +{ + /// Find first available buffer that fits to given offset. + if (!current_buf) + current_buf = initialize(); + + /// If current buffer has remaining data - use it. + if (current_buf && current_buf->next()) + { + working_buffer = current_buf->buffer(); + absolute_position += working_buffer.size(); + return true; + } + + /// If there is no available buffers - nothing to read. + if (current_buf_idx + 1 >= metadata.remote_fs_objects.size()) + return false; + + ++current_buf_idx; + const auto & path = metadata.remote_fs_objects[current_buf_idx].first; + + current_buf = createReadBuffer(path); + current_buf->next(); + + working_buffer = current_buf->buffer(); + absolute_position += working_buffer.size(); + + return true; +} + + +#if USE_AWS_S3 +template +class ReadIndirectBufferFromRemoteFS; +#endif + +#if USE_HDFS +template +class ReadIndirectBufferFromRemoteFS; +#endif + +} + +#endif diff --git a/src/Disks/ReadIndirectBufferFromRemoteFS.h b/src/Disks/ReadIndirectBufferFromRemoteFS.h new file mode 100644 index 00000000000..f80406b5354 --- /dev/null +++ b/src/Disks/ReadIndirectBufferFromRemoteFS.h @@ -0,0 +1,46 @@ +#pragma once +#include + +#if USE_AWS_S3 || USE_HDFS + +#include +#include +#include + + +namespace DB +{ + +/// Reads data from S3/HDFS using stored paths in metadata. +template +class ReadIndirectBufferFromRemoteFS : public ReadBufferFromFileBase +{ +public: + ReadIndirectBufferFromRemoteFS(IDiskRemote::Metadata metadata_); + + off_t seek(off_t offset_, int whence) override; + + off_t getPosition() override { return absolute_position - available(); } + + String getFileName() const override { return metadata.metadata_file_path; } + + virtual std::unique_ptr createReadBuffer(const String & path) = 0; + +protected: + IDiskRemote::Metadata metadata; + +private: + std::unique_ptr initialize(); + + bool nextImpl() override; + + size_t absolute_position = 0; + + size_t current_buf_idx = 0; + + std::unique_ptr current_buf; +}; + +} + +#endif diff --git a/src/Disks/S3/DiskS3.cpp b/src/Disks/S3/DiskS3.cpp index 1a347394012..a3f5fe89870 100644 --- a/src/Disks/S3/DiskS3.cpp +++ b/src/Disks/S3/DiskS3.cpp @@ -4,33 +4,28 @@ #include #include -#include #include #include #include -#include #include +#include +#include #include #include -#include -#include #include #include #include -#include #include #include #include -#include - +#include +#include #include #include #include #include #include -#include - namespace DB { @@ -39,55 +34,57 @@ namespace ErrorCodes { extern const int S3_ERROR; extern const int FILE_ALREADY_EXISTS; - extern const int CANNOT_SEEK_THROUGH_FILE; extern const int UNKNOWN_FORMAT; - extern const int INCORRECT_DISK_INDEX; extern const int BAD_ARGUMENTS; - extern const int PATH_ACCESS_DENIED; - extern const int CANNOT_DELETE_DIRECTORY; extern const int LOGICAL_ERROR; } - /// Helper class to collect keys into chunks of maximum size (to prepare batch requests to AWS API) -class DiskS3::AwsS3KeyKeeper : public std::list> +/// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html +class S3PathKeeper : public RemoteFSPathKeeper { public: - void addKey(const String & key); - static String getChunkKeys(const Aws::Vector & chunk); + using Chunk = Aws::Vector; + using Chunks = std::list; + + explicit S3PathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} + + void addPath(const String & path) override + { + if (chunks.empty() || chunks.back().size() >= chunk_limit) + { + /// add one more chunk + chunks.push_back(Chunks::value_type()); + chunks.back().reserve(chunk_limit); + } + Aws::S3::Model::ObjectIdentifier obj; + obj.SetKey(path); + chunks.back().push_back(obj); + } + + void removePaths(const std::function & remove_chunk_func) + { + for (auto & chunk : chunks) + remove_chunk_func(std::move(chunk)); + } + + static String getChunkKeys(const Chunk & chunk) + { + String res; + for (const auto & obj : chunk) + { + const auto & key = obj.GetKey(); + if (!res.empty()) + res.append(", "); + res.append(key.c_str(), key.size()); + } + return res; + } private: - /// limit for one DeleteObject request - /// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html - const static size_t chunk_limit = 1000; + Chunks chunks; }; -void DiskS3::AwsS3KeyKeeper::addKey(const String & key) -{ - if (empty() || back().size() >= chunk_limit) - { /// add one more chunk - push_back(value_type()); - back().reserve(chunk_limit); - } - - Aws::S3::Model::ObjectIdentifier obj; - obj.SetKey(key); - back().push_back(obj); -} - -String DiskS3::AwsS3KeyKeeper::getChunkKeys(const Aws::Vector & chunk) -{ - String res; - for (const auto & obj : chunk) - { - const auto & key = obj.GetKey(); - if (!res.empty()) - res.append(", "); - res.append(key.c_str(), key.size()); - } - return res; -} - String getRandomName() { std::uniform_int_distribution distribution('a', 'z'); @@ -117,174 +114,8 @@ void throwIfError(const Aws::Utils::Outcome & response) } } -/** - * S3 metadata file layout: - * Number of S3 objects, Total size of all S3 objects. - * Each S3 object represents path where object located in S3 and size of object. - */ -struct DiskS3::Metadata -{ - /// Metadata file version. - static constexpr UInt32 VERSION_ABSOLUTE_PATHS = 1; - static constexpr UInt32 VERSION_RELATIVE_PATHS = 2; - static constexpr UInt32 VERSION_READ_ONLY_FLAG = 3; - - using PathAndSize = std::pair; - - /// S3 root path. - const String & s3_root_path; - - /// Disk path. - const String & disk_path; - /// Relative path to metadata file on local FS. - String metadata_file_path; - /// Total size of all S3 objects. - size_t total_size; - /// S3 objects paths and their sizes. - std::vector s3_objects; - /// Number of references (hardlinks) to this metadata file. - UInt32 ref_count; - /// Flag indicates that file is read only. - bool read_only = false; - - /// Load metadata by path or create empty if `create` flag is set. - explicit Metadata(const String & s3_root_path_, const String & disk_path_, const String & metadata_file_path_, bool create = false) - : s3_root_path(s3_root_path_), disk_path(disk_path_), metadata_file_path(metadata_file_path_), total_size(0), s3_objects(0), ref_count(0) - { - if (create) - return; - - try - { - ReadBufferFromFile buf(disk_path + metadata_file_path, 1024); /* reasonable buffer size for small file */ - - UInt32 version; - readIntText(version, buf); - - if (version < VERSION_ABSOLUTE_PATHS || version > VERSION_READ_ONLY_FLAG) - throw Exception( - "Unknown metadata file version. Path: " + disk_path + metadata_file_path - + " Version: " + std::to_string(version) + ", Maximum expected version: " + std::to_string(VERSION_READ_ONLY_FLAG), - ErrorCodes::UNKNOWN_FORMAT); - - assertChar('\n', buf); - - UInt32 s3_objects_count; - readIntText(s3_objects_count, buf); - assertChar('\t', buf); - readIntText(total_size, buf); - assertChar('\n', buf); - s3_objects.resize(s3_objects_count); - for (UInt32 i = 0; i < s3_objects_count; ++i) - { - String s3_object_path; - size_t s3_object_size; - readIntText(s3_object_size, buf); - assertChar('\t', buf); - readEscapedString(s3_object_path, buf); - if (version == VERSION_ABSOLUTE_PATHS) - { - if (!boost::algorithm::starts_with(s3_object_path, s3_root_path)) - throw Exception( - "Path in metadata does not correspond S3 root path. Path: " + s3_object_path - + ", root path: " + s3_root_path + ", disk path: " + disk_path_, - ErrorCodes::UNKNOWN_FORMAT); - s3_object_path = s3_object_path.substr(s3_root_path.size()); - } - assertChar('\n', buf); - s3_objects[i] = {s3_object_path, s3_object_size}; - } - - readIntText(ref_count, buf); - assertChar('\n', buf); - - if (version >= VERSION_READ_ONLY_FLAG) - { - readBoolText(read_only, buf); - assertChar('\n', buf); - } - } - catch (Exception & e) - { - if (e.code() == ErrorCodes::UNKNOWN_FORMAT) - throw; - - throw Exception("Failed to read metadata file", e, ErrorCodes::UNKNOWN_FORMAT); - } - } - - void addObject(const String & path, size_t size) - { - total_size += size; - s3_objects.emplace_back(path, size); - } - - /// Fsync metadata file if 'sync' flag is set. - void save(bool sync = false) - { - WriteBufferFromFile buf(disk_path + metadata_file_path, 1024); - - writeIntText(VERSION_RELATIVE_PATHS, buf); - writeChar('\n', buf); - - writeIntText(s3_objects.size(), buf); - writeChar('\t', buf); - writeIntText(total_size, buf); - writeChar('\n', buf); - for (const auto & [s3_object_path, s3_object_size] : s3_objects) - { - writeIntText(s3_object_size, buf); - writeChar('\t', buf); - writeEscapedString(s3_object_path, buf); - writeChar('\n', buf); - } - - writeIntText(ref_count, buf); - writeChar('\n', buf); - - writeBoolText(read_only, buf); - writeChar('\n', buf); - - buf.finalize(); - if (sync) - buf.sync(); - } -}; - -DiskS3::Metadata DiskS3::readOrCreateMetaForWriting(const String & path, WriteMode mode) -{ - bool exist = exists(path); - if (exist) - { - auto metadata = readMeta(path); - if (metadata.read_only) - throw Exception("File is read-only: " + path, ErrorCodes::PATH_ACCESS_DENIED); - - if (mode == WriteMode::Rewrite) - removeFile(path); /// Remove for re-write. - else - return metadata; - } - - auto metadata = createMeta(path); - /// Save empty metadata to disk to have ability to get file size while buffer is not finalized. - metadata.save(); - - return metadata; -} - -DiskS3::Metadata DiskS3::readMeta(const String & path) const -{ - return Metadata(s3_root_path, metadata_path, path); -} - -DiskS3::Metadata DiskS3::createMeta(const String & path) const -{ - return Metadata(s3_root_path, metadata_path, path, true); -} - /// Reads data from S3 using stored paths in metadata. -class ReadIndirectBufferFromS3 final : public ReadBufferFromFileBase +class ReadIndirectBufferFromS3 final : public ReadIndirectBufferFromRemoteFS { public: ReadIndirectBufferFromS3( @@ -293,291 +124,26 @@ public: DiskS3::Metadata metadata_, size_t s3_max_single_read_retries_, size_t buf_size_) - : client_ptr(std::move(client_ptr_)) + : ReadIndirectBufferFromRemoteFS(metadata_) + , client_ptr(std::move(client_ptr_)) , bucket(bucket_) - , metadata(std::move(metadata_)) , s3_max_single_read_retries(s3_max_single_read_retries_) , buf_size(buf_size_) { } - off_t seek(off_t offset_, int whence) override + std::unique_ptr createReadBuffer(const String & path) override { - if (whence == SEEK_CUR) - { - /// If position within current working buffer - shift pos. - if (!working_buffer.empty() && size_t(getPosition() + offset_) < absolute_position) - { - pos += offset_; - return getPosition(); - } - else - { - absolute_position += offset_; - } - } - else if (whence == SEEK_SET) - { - /// If position within current working buffer - shift pos. - if (!working_buffer.empty() && size_t(offset_) >= absolute_position - working_buffer.size() - && size_t(offset_) < absolute_position) - { - pos = working_buffer.end() - (absolute_position - offset_); - return getPosition(); - } - else - { - absolute_position = offset_; - } - } - else - throw Exception("Only SEEK_SET or SEEK_CUR modes are allowed.", ErrorCodes::CANNOT_SEEK_THROUGH_FILE); - - current_buf = initialize(); - pos = working_buffer.end(); - - return absolute_position; + return std::make_unique(client_ptr, bucket, metadata.remote_fs_root_path + path, s3_max_single_read_retries, buf_size); } - off_t getPosition() override { return absolute_position - available(); } - - std::string getFileName() const override { return metadata.metadata_file_path; } - private: - std::unique_ptr initialize() - { - size_t offset = absolute_position; - for (size_t i = 0; i < metadata.s3_objects.size(); ++i) - { - current_buf_idx = i; - const auto & [path, size] = metadata.s3_objects[i]; - if (size > offset) - { - auto buf = std::make_unique(client_ptr, bucket, metadata.s3_root_path + path, s3_max_single_read_retries, buf_size); - buf->seek(offset, SEEK_SET); - return buf; - } - offset -= size; - } - return nullptr; - } - - bool nextImpl() override - { - /// Find first available buffer that fits to given offset. - if (!current_buf) - current_buf = initialize(); - - /// If current buffer has remaining data - use it. - if (current_buf && current_buf->next()) - { - working_buffer = current_buf->buffer(); - absolute_position += working_buffer.size(); - return true; - } - - /// If there is no available buffers - nothing to read. - if (current_buf_idx + 1 >= metadata.s3_objects.size()) - return false; - - ++current_buf_idx; - const auto & path = metadata.s3_objects[current_buf_idx].first; - current_buf = std::make_unique(client_ptr, bucket, metadata.s3_root_path + path, s3_max_single_read_retries, buf_size); - current_buf->next(); - working_buffer = current_buf->buffer(); - absolute_position += working_buffer.size(); - - return true; - } - std::shared_ptr client_ptr; const String & bucket; - DiskS3::Metadata metadata; size_t s3_max_single_read_retries; size_t buf_size; - - size_t absolute_position = 0; - size_t current_buf_idx = 0; - std::unique_ptr current_buf; }; -/// Stores data in S3 and adds the object key (S3 path) and object size to metadata file on local FS. -class WriteIndirectBufferFromS3 final : public WriteBufferFromFileDecorator -{ -public: - WriteIndirectBufferFromS3( - std::unique_ptr impl_, - DiskS3::Metadata metadata_, - String & s3_path_) - : WriteBufferFromFileDecorator(std::move(impl_)) - , metadata(std::move(metadata_)) - , s3_path(s3_path_) { } - - virtual ~WriteIndirectBufferFromS3() override - { - try - { - WriteIndirectBufferFromS3::finalize(); - } - catch (...) - { - tryLogCurrentException(__PRETTY_FUNCTION__); - } - } - - void finalize() override - { - if (finalized) - return; - - WriteBufferFromFileDecorator::finalize(); - - metadata.addObject(s3_path, count()); - metadata.save(); - } - - void sync() override - { - if (finalized) - metadata.save(true); - } - - std::string getFileName() const override { return metadata.metadata_file_path; } - -private: - DiskS3::Metadata metadata; - String s3_path; -}; - - -class DiskS3DirectoryIterator final : public IDiskDirectoryIterator -{ -public: - DiskS3DirectoryIterator(const String & full_path, const String & folder_path_) : iter(full_path), folder_path(folder_path_) {} - - void next() override { ++iter; } - - bool isValid() const override { return iter != Poco::DirectoryIterator(); } - - String path() const override - { - if (iter->isDirectory()) - return folder_path + iter.name() + '/'; - else - return folder_path + iter.name(); - } - - String name() const override { return iter.name(); } - -private: - Poco::DirectoryIterator iter; - String folder_path; -}; - - -using DiskS3Ptr = std::shared_ptr; - -class DiskS3Reservation final : public IReservation -{ -public: - DiskS3Reservation(const DiskS3Ptr & disk_, UInt64 size_) - : disk(disk_), size(size_), metric_increment(CurrentMetrics::DiskSpaceReservedForMerge, size_) - { - } - - UInt64 getSize() const override { return size; } - - DiskPtr getDisk(size_t i) const override - { - if (i != 0) - { - throw Exception("Can't use i != 0 with single disk reservation", ErrorCodes::INCORRECT_DISK_INDEX); - } - return disk; - } - - Disks getDisks() const override { return {disk}; } - - void update(UInt64 new_size) override - { - std::lock_guard lock(disk->reservation_mutex); - disk->reserved_bytes -= size; - size = new_size; - disk->reserved_bytes += size; - } - - ~DiskS3Reservation() override - { - try - { - std::lock_guard lock(disk->reservation_mutex); - if (disk->reserved_bytes < size) - { - disk->reserved_bytes = 0; - LOG_ERROR(disk->log, "Unbalanced reservations size for disk '{}'.", disk->getName()); - } - else - { - disk->reserved_bytes -= size; - } - - if (disk->reservation_count == 0) - LOG_ERROR(disk->log, "Unbalanced reservation count for disk '{}'.", disk->getName()); - else - --disk->reservation_count; - } - catch (...) - { - tryLogCurrentException(__PRETTY_FUNCTION__); - } - } - -private: - DiskS3Ptr disk; - UInt64 size; - CurrentMetrics::Increment metric_increment; -}; - -/// Runs tasks asynchronously using thread pool. -class AsyncExecutor : public Executor -{ -public: - explicit AsyncExecutor(int thread_pool_size) : pool(ThreadPool(thread_pool_size)) { } - - std::future execute(std::function task) override - { - auto promise = std::make_shared>(); - pool.scheduleOrThrowOnError( - [promise, task]() - { - try - { - task(); - promise->set_value(); - } - catch (...) - { - tryLogCurrentException("DiskS3", "Failed to run async task"); - - try - { - promise->set_exception(std::current_exception()); - } catch (...) { } - } - }); - - return promise->get_future(); - } - - void setMaxThreads(size_t threads) - { - pool.setMaxThreads(threads); - } -private: - ThreadPool pool; -}; - - DiskS3::DiskS3( String name_, String bucket_, @@ -585,73 +151,46 @@ DiskS3::DiskS3( String metadata_path_, SettingsPtr settings_, GetDiskSettings settings_getter_) - : IDisk(std::make_unique(settings_->thread_pool_size)) - , name(std::move(name_)) + : IDiskRemote(name_, s3_root_path_, metadata_path_, "DiskS3", settings_->thread_pool_size) , bucket(std::move(bucket_)) - , s3_root_path(std::move(s3_root_path_)) - , metadata_path(std::move(metadata_path_)) , current_settings(std::move(settings_)) , settings_getter(settings_getter_) { } -ReservationPtr DiskS3::reserve(UInt64 bytes) -{ - if (!tryReserve(bytes)) - return {}; - return std::make_unique(std::static_pointer_cast(shared_from_this()), bytes); -} - -bool DiskS3::exists(const String & path) const -{ - return Poco::File(metadata_path + path).exists(); -} - -bool DiskS3::isFile(const String & path) const -{ - return Poco::File(metadata_path + path).isFile(); -} - -bool DiskS3::isDirectory(const String & path) const -{ - return Poco::File(metadata_path + path).isDirectory(); -} - -size_t DiskS3::getFileSize(const String & path) const -{ - auto metadata = readMeta(path); - return metadata.total_size; -} - -void DiskS3::createDirectory(const String & path) -{ - Poco::File(metadata_path + path).createDirectory(); -} - -void DiskS3::createDirectories(const String & path) -{ - Poco::File(metadata_path + path).createDirectories(); -} - String DiskS3::getUniqueId(const String & path) const { - Metadata metadata(s3_root_path, metadata_path, path); + Metadata metadata(remote_fs_root_path, metadata_path, path); String id; - if (!metadata.s3_objects.empty()) - id = metadata.s3_root_path + metadata.s3_objects[0].first; + if (!metadata.remote_fs_objects.empty()) + id = metadata.remote_fs_root_path + metadata.remote_fs_objects[0].first; return id; } -DiskDirectoryIteratorPtr DiskS3::iterateDirectory(const String & path) +RemoteFSPathKeeperPtr DiskS3::createFSPathKeeper() const { - return std::make_unique(metadata_path + path, path); + auto settings = current_settings.get(); + return std::make_shared(settings->objects_chunk_size_to_delete); } -void DiskS3::clearDirectory(const String & path) +void DiskS3::removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) { - for (auto it{iterateDirectory(path)}; it->isValid(); it->next()) - if (isFile(it->path())) - removeFile(it->path()); + auto settings = current_settings.get(); + auto * s3_paths_keeper = dynamic_cast(fs_paths_keeper.get()); + + if (s3_paths_keeper) + s3_paths_keeper->removePaths([&](S3PathKeeper::Chunk && chunk) + { + LOG_DEBUG(log, "Remove AWS keys {}", S3PathKeeper::getChunkKeys(chunk)); + Aws::S3::Model::Delete delkeys; + delkeys.SetObjects(chunk); + /// TODO: Make operation idempotent. Do not throw exception if key is already deleted. + Aws::S3::Model::DeleteObjectsRequest request; + request.SetBucket(bucket); + request.SetDelete(delkeys); + auto outcome = settings->client->DeleteObjects(request); + throwIfError(outcome); + }); } void DiskS3::moveFile(const String & from_path, const String & to_path) @@ -679,26 +218,13 @@ void DiskS3::moveFile(const String & from_path, const String & to_path, bool sen Poco::File(metadata_path + from_path).renameTo(metadata_path + to_path); } -void DiskS3::replaceFile(const String & from_path, const String & to_path) -{ - if (exists(to_path)) - { - const String tmp_path = to_path + ".old"; - moveFile(to_path, tmp_path); - moveFile(from_path, to_path); - removeFile(tmp_path); - } - else - moveFile(from_path, to_path); -} - std::unique_ptr DiskS3::readFile(const String & path, size_t buf_size, size_t, size_t, size_t, MMappedFileCache *) const { auto settings = current_settings.get(); auto metadata = readMeta(path); LOG_DEBUG(log, "Read from file by path: {}. Existing S3 objects: {}", - backQuote(metadata_path + path), metadata.s3_objects.size()); + backQuote(metadata_path + path), metadata.remote_fs_objects.size()); auto reader = std::make_unique(settings->client, bucket, metadata, settings->s3_max_single_read_retries, buf_size); return std::make_unique(std::move(reader), settings->min_bytes_for_seek); @@ -723,180 +249,23 @@ std::unique_ptr DiskS3::writeFile(const String & path, } LOG_DEBUG(log, "{} to file by path: {}. S3 path: {}", - mode == WriteMode::Rewrite ? "Write" : "Append", backQuote(metadata_path + path), s3_root_path + s3_path); + mode == WriteMode::Rewrite ? "Write" : "Append", backQuote(metadata_path + path), remote_fs_root_path + s3_path); auto s3_buffer = std::make_unique( settings->client, bucket, - metadata.s3_root_path + s3_path, + metadata.remote_fs_root_path + s3_path, settings->s3_min_upload_part_size, settings->s3_max_single_part_upload_size, std::move(object_metadata), buf_size); - return std::make_unique(std::move(s3_buffer), std::move(metadata), s3_path); -} - -void DiskS3::removeMeta(const String & path, AwsS3KeyKeeper & keys) -{ - LOG_DEBUG(log, "Remove file by path: {}", backQuote(metadata_path + path)); - - Poco::File file(metadata_path + path); - - if (!file.isFile()) - throw Exception(ErrorCodes::CANNOT_DELETE_DIRECTORY, "Path '{}' is a directory", path); - - try - { - auto metadata = readMeta(path); - - /// If there is no references - delete content from S3. - if (metadata.ref_count == 0) - { - file.remove(); - - for (const auto & [s3_object_path, _] : metadata.s3_objects) - keys.addKey(s3_root_path + s3_object_path); - } - else /// In other case decrement number of references, save metadata and delete file. - { - --metadata.ref_count; - metadata.save(); - file.remove(); - } - } - catch (const Exception & e) - { - /// If it's impossible to read meta - just remove it from FS. - if (e.code() == ErrorCodes::UNKNOWN_FORMAT) - { - LOG_WARNING( - log, - "Metadata file {} can't be read by reason: {}. Removing it forcibly.", - backQuote(path), - e.nested() ? e.nested()->message() : e.message()); - - file.remove(); - } - else - throw; - } -} - -void DiskS3::removeMetaRecursive(const String & path, AwsS3KeyKeeper & keys) -{ - checkStackSize(); /// This is needed to prevent stack overflow in case of cyclic symlinks. - - Poco::File file(metadata_path + path); - if (file.isFile()) - { - removeMeta(path, keys); - } - else - { - for (auto it{iterateDirectory(path)}; it->isValid(); it->next()) - removeMetaRecursive(it->path(), keys); - file.remove(); - } -} - -void DiskS3::removeAws(const AwsS3KeyKeeper & keys) -{ - if (!keys.empty()) - { - auto settings = current_settings.get(); - - for (const auto & chunk : keys) - { - LOG_DEBUG(log, "Remove AWS keys {}", AwsS3KeyKeeper::getChunkKeys(chunk)); - - Aws::S3::Model::Delete delkeys; - delkeys.SetObjects(chunk); - - /// TODO: Make operation idempotent. Do not throw exception if key is already deleted. - Aws::S3::Model::DeleteObjectsRequest request; - request.SetBucket(bucket); - request.SetDelete(delkeys); - auto outcome = settings->client->DeleteObjects(request); - throwIfError(outcome); - } - } -} - -void DiskS3::removeFileIfExists(const String & path) -{ - AwsS3KeyKeeper keys; - if (Poco::File(metadata_path + path).exists()) - { - removeMeta(path, keys); - removeAws(keys); - } -} - -void DiskS3::removeDirectory(const String & path) -{ - Poco::File(metadata_path + path).remove(); -} - -void DiskS3::removeSharedFile(const String & path, bool keep_s3) -{ - AwsS3KeyKeeper keys; - removeMeta(path, keys); - if (!keep_s3) - removeAws(keys); -} - -void DiskS3::removeSharedRecursive(const String & path, bool keep_s3) -{ - AwsS3KeyKeeper keys; - removeMetaRecursive(path, keys); - if (!keep_s3) - removeAws(keys); -} - -bool DiskS3::tryReserve(UInt64 bytes) -{ - std::lock_guard lock(reservation_mutex); - if (bytes == 0) - { - LOG_DEBUG(log, "Reserving 0 bytes on s3 disk {}", backQuote(name)); - ++reservation_count; - return true; - } - - auto available_space = getAvailableSpace(); - UInt64 unreserved_space = available_space - std::min(available_space, reserved_bytes); - if (unreserved_space >= bytes) - { - LOG_DEBUG(log, "Reserving {} on disk {}, having unreserved {}.", - ReadableSize(bytes), backQuote(name), ReadableSize(unreserved_space)); - ++reservation_count; - reserved_bytes += bytes; - return true; - } - return false; -} - -void DiskS3::listFiles(const String & path, std::vector & file_names) -{ - for (auto it = iterateDirectory(path); it->isValid(); it->next()) - file_names.push_back(it->name()); -} - -void DiskS3::setLastModified(const String & path, const Poco::Timestamp & timestamp) -{ - Poco::File(metadata_path + path).setLastModified(timestamp); -} - -Poco::Timestamp DiskS3::getLastModified(const String & path) -{ - return Poco::File(metadata_path + path).getLastModified(); + return std::make_unique>(std::move(s3_buffer), std::move(metadata), s3_path); } void DiskS3::createHardLink(const String & src_path, const String & dst_path) { auto settings = current_settings.get(); - createHardLink(src_path, dst_path, settings->send_metadata); } @@ -922,22 +291,6 @@ void DiskS3::createHardLink(const String & src_path, const String & dst_path, bo DB::createHardLink(metadata_path + src_path, metadata_path + dst_path); } -void DiskS3::createFile(const String & path) -{ - /// Create empty metadata file. - auto metadata = createMeta(path); - metadata.save(); -} - -void DiskS3::setReadOnly(const String & path) -{ - /// We should store read only flag inside metadata file (instead of using FS flag), - /// because we modify metadata file when create hard-links from it. - auto metadata = readMeta(path); - metadata.read_only = true; - metadata.save(); -} - void DiskS3::shutdown() { auto settings = current_settings.get(); @@ -955,7 +308,7 @@ void DiskS3::createFileOperationObject(const String & operation_name, UInt64 rev WriteBufferFromS3 buffer( settings->client, bucket, - s3_root_path + key, + remote_fs_root_path + key, settings->s3_min_upload_part_size, settings->s3_max_single_part_upload_size, metadata); @@ -975,7 +328,7 @@ void DiskS3::startup() restore(); - if (readSchemaVersion(bucket, s3_root_path) < RESTORABLE_SCHEMA_VERSION) + if (readSchemaVersion(bucket, remote_fs_root_path) < RESTORABLE_SCHEMA_VERSION) migrateToRestorableSchema(); findLastRevision(); @@ -995,8 +348,8 @@ void DiskS3::findLastRevision() LOG_DEBUG(log, "Check object exists with revision prefix {}", revision_prefix); /// Check file or operation with such revision prefix exists. - if (checkObjectExists(bucket, s3_root_path + "r" + revision_prefix) - || checkObjectExists(bucket, s3_root_path + "operations/r" + revision_prefix)) + if (checkObjectExists(bucket, remote_fs_root_path + "r" + revision_prefix) + || checkObjectExists(bucket, remote_fs_root_path + "operations/r" + revision_prefix)) revision += "1"; else revision += "0"; @@ -1030,7 +383,7 @@ void DiskS3::saveSchemaVersion(const int & version) WriteBufferFromS3 buffer( settings->client, bucket, - s3_root_path + SCHEMA_VERSION_OBJECT, + remote_fs_root_path + SCHEMA_VERSION_OBJECT, settings->s3_min_upload_part_size, settings->s3_max_single_part_upload_size); @@ -1058,12 +411,12 @@ void DiskS3::migrateFileToRestorableSchema(const String & path) auto meta = readMeta(path); - for (const auto & [key, _] : meta.s3_objects) + for (const auto & [key, _] : meta.remote_fs_objects) { ObjectMetadata metadata { {"path", path} }; - updateObjectMetadata(s3_root_path + key, metadata); + updateObjectMetadata(remote_fs_root_path + key, metadata); } } @@ -1286,7 +639,7 @@ void DiskS3::restore() { RestoreInformation information; information.source_bucket = bucket; - information.source_path = s3_root_path; + information.source_path = remote_fs_root_path; readRestoreInformation(information); if (information.revision == 0) @@ -1298,11 +651,11 @@ void DiskS3::restore() { /// In this case we need to additionally cleanup S3 from objects with later revision. /// Will be simply just restore to different path. - if (information.source_path == s3_root_path && information.revision != LATEST_REVISION) + if (information.source_path == remote_fs_root_path && information.revision != LATEST_REVISION) throw Exception("Restoring to the same bucket and path is allowed if revision is latest (0)", ErrorCodes::BAD_ARGUMENTS); /// This case complicates S3 cleanup in case of unsuccessful restore. - if (information.source_path != s3_root_path && s3_root_path.starts_with(information.source_path)) + if (information.source_path != remote_fs_root_path && remote_fs_root_path.starts_with(information.source_path)) throw Exception("Restoring to the same bucket is allowed only if source path is not a sub-path of configured path in S3 disk", ErrorCodes::BAD_ARGUMENTS); } @@ -1314,7 +667,7 @@ void DiskS3::restore() LOG_INFO(log, "Removing old metadata..."); - bool cleanup_s3 = information.source_bucket != bucket || information.source_path != s3_root_path; + bool cleanup_s3 = information.source_bucket != bucket || information.source_path != remote_fs_root_path; for (const auto & root : data_roots) if (exists(root)) removeSharedRecursive(root + '/', !cleanup_s3); @@ -1408,8 +761,8 @@ void DiskS3::processRestoreFiles(const String & source_bucket, const String & so auto relative_key = shrinkKey(source_path, key); /// Copy object if we restore to different bucket / path. - if (bucket != source_bucket || s3_root_path != source_path) - copyObject(source_bucket, key, bucket, s3_root_path + relative_key); + if (bucket != source_bucket || remote_fs_root_path != source_path) + copyObject(source_bucket, key, bucket, remote_fs_root_path + relative_key); metadata.addObject(relative_key, head_result.GetContentLength()); metadata.save(); @@ -1425,7 +778,7 @@ void DiskS3::restoreFileOperations(const RestoreInformation & restore_informatio LOG_INFO(log, "Starting restore file operations for disk {}", name); /// Enable recording file operations if we restore to different bucket / path. - bool send_metadata = bucket != restore_information.source_bucket || s3_root_path != restore_information.source_path; + bool send_metadata = bucket != restore_information.source_bucket || remote_fs_root_path != restore_information.source_path; std::set renames; auto restore_file_operations = [this, &restore_information, &renames, &send_metadata](auto list_result) @@ -1581,7 +934,8 @@ DiskS3Settings::DiskS3Settings( size_t min_bytes_for_seek_, bool send_metadata_, int thread_pool_size_, - int list_object_keys_size_) + int list_object_keys_size_, + int objects_chunk_size_to_delete_) : client(client_) , s3_max_single_read_retries(s3_max_single_read_retries_) , s3_min_upload_part_size(s3_min_upload_part_size_) @@ -1590,6 +944,7 @@ DiskS3Settings::DiskS3Settings( , send_metadata(send_metadata_) , thread_pool_size(thread_pool_size_) , list_object_keys_size(list_object_keys_size_) + , objects_chunk_size_to_delete(objects_chunk_size_to_delete_) { } diff --git a/src/Disks/S3/DiskS3.h b/src/Disks/S3/DiskS3.h index efc7fdcf643..8857a00d709 100644 --- a/src/Disks/S3/DiskS3.h +++ b/src/Disks/S3/DiskS3.h @@ -2,7 +2,6 @@ #include #include -#include #include "Disks/DiskFactory.h" #include "Disks/Executor.h" @@ -12,6 +11,7 @@ #include #include +#include namespace DB @@ -28,7 +28,8 @@ struct DiskS3Settings size_t min_bytes_for_seek_, bool send_metadata_, int thread_pool_size_, - int list_object_keys_size_); + int list_object_keys_size_, + int objects_chunk_size_to_delete_); std::shared_ptr client; size_t s3_max_single_read_retries; @@ -38,25 +39,24 @@ struct DiskS3Settings bool send_metadata; int thread_pool_size; int list_object_keys_size; + int objects_chunk_size_to_delete; }; + /** * Storage for persisting data in S3 and metadata on the local disk. * Files are represented by file in local filesystem (clickhouse_root/disks/disk_name/path/to/file) * that contains S3 object key with actual data. */ -class DiskS3 : public IDisk +class DiskS3 final : public IDiskRemote { public: using ObjectMetadata = std::map; using Futures = std::vector>; + using SettingsPtr = std::unique_ptr; using GetDiskSettings = std::function; - friend class DiskS3Reservation; - - class AwsS3KeyKeeper; - struct Metadata; struct RestoreInformation; DiskS3( @@ -67,44 +67,6 @@ public: SettingsPtr settings_, GetDiskSettings settings_getter_); - const String & getName() const override { return name; } - - const String & getPath() const override { return metadata_path; } - - ReservationPtr reserve(UInt64 bytes) override; - - UInt64 getTotalSpace() const override { return std::numeric_limits::max(); } - - UInt64 getAvailableSpace() const override { return std::numeric_limits::max(); } - - UInt64 getUnreservedSpace() const override { return std::numeric_limits::max(); } - - UInt64 getKeepingFreeSpace() const override { return 0; } - - bool exists(const String & path) const override; - - bool isFile(const String & path) const override; - - bool isDirectory(const String & path) const override; - - size_t getFileSize(const String & path) const override; - - void createDirectory(const String & path) override; - - void createDirectories(const String & path) override; - - void clearDirectory(const String & path) override; - - void moveDirectory(const String & from_path, const String & to_path) override { moveFile(from_path, to_path); } - - DiskDirectoryIteratorPtr iterateDirectory(const String & path) override; - - void moveFile(const String & from_path, const String & to_path) override; - void moveFile(const String & from_path, const String & to_path, bool send_metadata); - void replaceFile(const String & from_path, const String & to_path) override; - - void listFiles(const String & path, std::vector & file_names) override; - std::unique_ptr readFile( const String & path, size_t buf_size, @@ -118,25 +80,16 @@ public: size_t buf_size, WriteMode mode) override; - void removeFile(const String & path) override { removeSharedFile(path, false); } - void removeFileIfExists(const String & path) override; - void removeDirectory(const String & path) override; - void removeRecursive(const String & path) override { removeSharedRecursive(path, false); } + void removeFromRemoteFS(RemoteFSPathKeeperPtr keeper) override; - void removeSharedFile(const String & path, bool keep_s3) override; - void removeSharedRecursive(const String & path, bool keep_s3) override; + RemoteFSPathKeeperPtr createFSPathKeeper() const override; + + void moveFile(const String & from_path, const String & to_path, bool send_metadata); + void moveFile(const String & from_path, const String & to_path) override; void createHardLink(const String & src_path, const String & dst_path) override; void createHardLink(const String & src_path, const String & dst_path, bool send_metadata); - void setLastModified(const String & path, const Poco::Timestamp & timestamp) override; - - Poco::Timestamp getLastModified(const String & path) override; - - void createFile(const String & path) override; - - void setReadOnly(const String & path) override; - DiskType::Type getType() const override { return DiskType::Type::S3; } void shutdown() override; @@ -157,16 +110,6 @@ public: void applyNewSettings(const Poco::Util::AbstractConfiguration & config, ContextConstPtr context) override; private: - bool tryReserve(UInt64 bytes); - - void removeMeta(const String & path, AwsS3KeyKeeper & keys); - void removeMetaRecursive(const String & path, AwsS3KeyKeeper & keys); - void removeAws(const AwsS3KeyKeeper & keys); - - Metadata readOrCreateMetaForWriting(const String & path, WriteMode mode); - Metadata readMeta(const String & path) const; - Metadata createMeta(const String & path) const; - void createFileOperationObject(const String & operation_name, UInt64 revision, const ObjectMetadata & metadata); /// Converts revision to binary string with leading zeroes (64 bit). static String revisionToString(UInt64 revision); @@ -200,19 +143,12 @@ private: /// Forms detached path '../../detached/part_name/' from '../../part_name/' static String pathToDetached(const String & source_path); - const String name; const String bucket; - const String s3_root_path; - const String metadata_path; - MultiVersion current_settings; + MultiVersion current_settings; /// Gets disk settings from context. GetDiskSettings settings_getter; - UInt64 reserved_bytes = 0; - UInt64 reservation_count = 0; - std::mutex reservation_mutex; - std::atomic revision_counter = 0; static constexpr UInt64 LATEST_REVISION = std::numeric_limits::max(); static constexpr UInt64 UNKNOWN_REVISION = 0; @@ -229,8 +165,6 @@ private: static constexpr int RESTORABLE_SCHEMA_VERSION = 1; /// Directories with data. const std::vector data_roots {"data", "store"}; - - Poco::Logger * log = &Poco::Logger::get("DiskS3"); }; } diff --git a/src/Disks/S3/registerDiskS3.cpp b/src/Disks/S3/registerDiskS3.cpp index 767e8890a01..e02f413c65e 100644 --- a/src/Disks/S3/registerDiskS3.cpp +++ b/src/Disks/S3/registerDiskS3.cpp @@ -156,7 +156,8 @@ std::unique_ptr getSettings(const Poco::Util::AbstractConfigurat config.getUInt64(config_prefix + ".min_bytes_for_seek", 1024 * 1024), config.getBool(config_prefix + ".send_metadata", false), config.getInt(config_prefix + ".thread_pool_size", 16), - config.getInt(config_prefix + ".list_object_keys_size", 1000)); + config.getInt(config_prefix + ".list_object_keys_size", 1000), + config.getInt(config_prefix + ".objects_chunk_size_to_delete", 1000)); } } @@ -225,4 +226,3 @@ void registerDiskS3(DiskFactory & factory) void registerDiskS3(DiskFactory &) {} #endif - diff --git a/src/Disks/WriteIndirectBufferFromRemoteFS.cpp b/src/Disks/WriteIndirectBufferFromRemoteFS.cpp new file mode 100644 index 00000000000..adc711608d7 --- /dev/null +++ b/src/Disks/WriteIndirectBufferFromRemoteFS.cpp @@ -0,0 +1,71 @@ +#include "WriteIndirectBufferFromRemoteFS.h" + +#if USE_AWS_S3 || USE_HDFS +#include +#include + + +namespace DB +{ + +/// Stores data in S3/HDFS and adds the object key (S3 path) and object size to metadata file on local FS. +template +WriteIndirectBufferFromRemoteFS::WriteIndirectBufferFromRemoteFS( + std::unique_ptr impl_, + IDiskRemote::Metadata metadata_, + const String & remote_fs_path_) + : WriteBufferFromFileDecorator(std::move(impl_)) + , metadata(std::move(metadata_)) + , remote_fs_path(remote_fs_path_) +{ +} + + +template +WriteIndirectBufferFromRemoteFS::~WriteIndirectBufferFromRemoteFS() +{ + try + { + WriteIndirectBufferFromRemoteFS::finalize(); + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } +} + + +template +void WriteIndirectBufferFromRemoteFS::finalize() +{ + if (finalized) + return; + + WriteBufferFromFileDecorator::finalize(); + + metadata.addObject(remote_fs_path, count()); + metadata.save(); +} + + +template +void WriteIndirectBufferFromRemoteFS::sync() +{ + if (finalized) + metadata.save(true); +} + + +#if USE_AWS_S3 +template +class WriteIndirectBufferFromRemoteFS; +#endif + +#if USE_HDFS +template +class WriteIndirectBufferFromRemoteFS; +#endif + +} + +#endif diff --git a/src/Disks/WriteIndirectBufferFromRemoteFS.h b/src/Disks/WriteIndirectBufferFromRemoteFS.h new file mode 100644 index 00000000000..cda7523e19e --- /dev/null +++ b/src/Disks/WriteIndirectBufferFromRemoteFS.h @@ -0,0 +1,39 @@ +#pragma once +#include + +#if USE_AWS_S3 || USE_HDFS + +#include +#include +#include + +namespace DB +{ + +/// Stores data in S3/HDFS and adds the object key (S3 path) and object size to metadata file on local FS. +template +class WriteIndirectBufferFromRemoteFS final : public WriteBufferFromFileDecorator +{ +public: + WriteIndirectBufferFromRemoteFS( + std::unique_ptr impl_, + IDiskRemote::Metadata metadata_, + const String & remote_fs_path_); + + virtual ~WriteIndirectBufferFromRemoteFS() override; + + void finalize() override; + + void sync() override; + + String getFileName() const override { return metadata.metadata_file_path; } + +private: + IDiskRemote::Metadata metadata; + + String remote_fs_path; +}; + +} + +#endif diff --git a/src/Disks/registerDisks.cpp b/src/Disks/registerDisks.cpp index 2da39e62b19..8f4901e49e5 100644 --- a/src/Disks/registerDisks.cpp +++ b/src/Disks/registerDisks.cpp @@ -8,21 +8,33 @@ namespace DB { + void registerDiskLocal(DiskFactory & factory); void registerDiskMemory(DiskFactory & factory); + #if USE_AWS_S3 void registerDiskS3(DiskFactory & factory); #endif +#if USE_HDFS +void registerDiskHDFS(DiskFactory & factory); +#endif + + void registerDisks() { auto & factory = DiskFactory::instance(); registerDiskLocal(factory); registerDiskMemory(factory); + #if USE_AWS_S3 registerDiskS3(factory); #endif + +#if USE_HDFS + registerDiskHDFS(factory); +#endif } } diff --git a/src/Disks/tests/gtest_disk.cpp b/src/Disks/tests/gtest_disk.cpp index 3b9dca63002..714abf485ee 100644 --- a/src/Disks/tests/gtest_disk.cpp +++ b/src/Disks/tests/gtest_disk.cpp @@ -1,14 +1,15 @@ #include - #include #include #include "gtest_disk.h" + #if !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsuggest-override" #endif + template DB::DiskPtr createDisk(); diff --git a/src/Disks/tests/gtest_disk.h b/src/Disks/tests/gtest_disk.h index d7ca0beca5f..fa1028089cb 100644 --- a/src/Disks/tests/gtest_disk.h +++ b/src/Disks/tests/gtest_disk.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include diff --git a/src/Disks/tests/gtest_disk_hdfs.cpp b/src/Disks/tests/gtest_disk_hdfs.cpp new file mode 100644 index 00000000000..d537ac967e5 --- /dev/null +++ b/src/Disks/tests/gtest_disk_hdfs.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include "gtest_disk.h" + + +#define RUN_HDFS_TEST 0 +#if RUN_HDFS_TEST + +#include +#include + +const String hdfs_uri = "hdfs://172.20.0.2:9000/disk_test/"; +const String metadata_path = "/path/to/metadata/"; +const String config_path = "/path/to/config.xml"; +const String file_name = "test.txt"; + + +TEST(DiskTestHDFS, RemoveFileHDFS) +{ + Poco::Util::AbstractConfiguration *config = new Poco::Util::XMLConfiguration(config_path); + auto settings = std::make_unique(1024 * 1024); + auto disk = DB::DiskHDFS("disk_hdfs", hdfs_uri, std::move(settings), metadata_path, *config); + + DB::HDFSBuilderWrapper builder = DB::createHDFSBuilder(hdfs_uri, *config); + DB::HDFSFSPtr fs = DB::createHDFSFS(builder.get()); + + disk.writeFile(file_name, 1024, DB::WriteMode::Rewrite); + auto metadata = disk.readMeta(file_name); + + const String hdfs_file_name = metadata.remote_fs_objects[0].first; + const String hdfs_file_path = "/disk_test/" + hdfs_file_name; + + auto ret = hdfsExists(fs.get(), hdfs_file_path.data()); + EXPECT_EQ(0, ret); + + disk.removeFile(file_name); + ret = hdfsExists(fs.get(), hdfs_file_path.data()); + EXPECT_EQ(-1, ret); +} + + +TEST(DiskTestHDFS, WriteReadHDFS) +{ + Poco::Util::AbstractConfiguration *config = new Poco::Util::XMLConfiguration(config_path); + auto settings = std::make_unique(1024 * 1024); + auto disk = DB::DiskHDFS("disk_hdfs", hdfs_uri, std::move(settings), metadata_path, *config); + + { + auto out = disk.writeFile(file_name, 1024, DB::WriteMode::Rewrite); + writeString("Test write to file", *out); + } + + { + DB::String result; + auto in = disk.readFile(file_name, 1024, 1024, 1024, 1024, nullptr); + readString(result, *in); + EXPECT_EQ("Test write to file", result); + } + + disk.removeFileIfExists(file_name); +} + + +TEST(DiskTestHDFS, RewriteFileHDFS) +{ + Poco::Util::AbstractConfiguration *config = new Poco::Util::XMLConfiguration(config_path); + auto settings = std::make_unique(1024 * 1024); + auto disk = DB::DiskHDFS("disk_hdfs", hdfs_uri, std::move(settings), metadata_path, *config); + + for (size_t i = 1; i <= 10; ++i) + { + std::unique_ptr out = disk.writeFile(file_name, 1024, DB::WriteMode::Rewrite); + writeString("Text" + DB::toString(i), *out); + } + + { + String result; + auto in = disk.readFile(file_name, 1024, 1024, 1024, 1024, nullptr); + readString(result, *in); + EXPECT_EQ("Text10", result); + readString(result, *in); + EXPECT_EQ("", result); + } + + disk.removeFileIfExists(file_name); +} + + +TEST(DiskTestHDFS, AppendFileHDFS) +{ + Poco::Util::AbstractConfiguration *config = new Poco::Util::XMLConfiguration(config_path); + auto settings = std::make_unique(1024 * 1024); + auto disk = DB::DiskHDFS("disk_hdfs", hdfs_uri, std::move(settings), metadata_path, *config); + + { + std::unique_ptr out = disk.writeFile(file_name, 1024, DB::WriteMode::Append); + writeString("Text", *out); + for (size_t i = 0; i < 10; ++i) + { + writeIntText(i, *out); + } + } + + { + String result, expected; + auto in = disk.readFile(file_name, 1024, 1024, 1024, 1024, nullptr); + + readString(result, *in); + EXPECT_EQ("Text0123456789", result); + + readString(result, *in); + EXPECT_EQ("", result); + } + + disk.removeFileIfExists(file_name); +} + + +TEST(DiskTestHDFS, SeekHDFS) +{ + Poco::Util::AbstractConfiguration *config = new Poco::Util::XMLConfiguration(config_path); + auto settings = std::make_unique(1024 * 1024); + auto disk = DB::DiskHDFS("disk_hdfs", hdfs_uri, std::move(settings), metadata_path, *config); + + { + std::unique_ptr out = disk.writeFile(file_name, 1024, DB::WriteMode::Rewrite); + writeString("test data", *out); + } + + /// Test SEEK_SET + { + String buf(4, '0'); + std::unique_ptr in = disk.readFile(file_name, 1024, 1024, 1024, 1024, nullptr); + + in->seek(5, SEEK_SET); + + in->readStrict(buf.data(), 4); + EXPECT_EQ("data", buf); + } + + /// Test SEEK_CUR + { + std::unique_ptr in = disk.readFile(file_name, 1024, 1024, 1024, 1024, nullptr); + String buf(4, '0'); + + in->readStrict(buf.data(), 4); + EXPECT_EQ("test", buf); + + // Skip whitespace + in->seek(1, SEEK_CUR); + + in->readStrict(buf.data(), 4); + EXPECT_EQ("data", buf); + } + + disk.removeFileIfExists(file_name); +} + +#endif diff --git a/src/Disks/ya.make.in b/src/Disks/ya.make.in index d030c7b1482..4f7ccff17ef 100644 --- a/src/Disks/ya.make.in +++ b/src/Disks/ya.make.in @@ -7,8 +7,7 @@ PEERDIR( ) -SRCS( - + ) END() diff --git a/src/Functions/CustomWeekTransforms.h b/src/Functions/CustomWeekTransforms.h index d1a8e3a54fd..2b28620c84e 100644 --- a/src/Functions/CustomWeekTransforms.h +++ b/src/Functions/CustomWeekTransforms.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,6 @@ namespace DB { namespace ErrorCodes { - extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_COLUMN; } @@ -26,43 +26,6 @@ namespace ErrorCodes * CustomWeek Transformations. */ -static inline UInt32 dateIsNotSupported(const char * name) -{ - throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); -} - -/// This factor transformation will say that the function is monotone everywhere. -struct ZeroTransform -{ - static inline UInt16 execute(UInt16, UInt8, const DateLUTImpl &) { return 0; } - static inline UInt16 execute(UInt32, UInt8, const DateLUTImpl &) { return 0; } - static inline UInt16 execute(Int64, UInt8, const DateLUTImpl &) { return 0; } -}; - -struct ToWeekImpl -{ - static constexpr auto name = "toWeek"; - - static inline UInt8 execute(Int64 t, UInt8 week_mode, const DateLUTImpl & time_zone) - { - // TODO: ditch conversion to DayNum, since it doesn't support extended range. - YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode); - return yw.second; - } - static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone) - { - YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode); - return yw.second; - } - static inline UInt8 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone) - { - YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode); - return yw.second; - } - - using FactorTransform = ZeroTransform; -}; - struct ToYearWeekImpl { static constexpr auto name = "toYearWeek"; @@ -110,10 +73,34 @@ struct ToStartOfWeekImpl using FactorTransform = ZeroTransform; }; -template -struct Transformer +struct ToWeekImpl { - explicit Transformer(Transform transform_) + static constexpr auto name = "toWeek"; + + static inline UInt8 execute(Int64 t, UInt8 week_mode, const DateLUTImpl & time_zone) + { + // TODO: ditch conversion to DayNum, since it doesn't support extended range. + YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode); + return yw.second; + } + static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone) + { + YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode); + return yw.second; + } + static inline UInt8 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone) + { + YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode); + return yw.second; + } + + using FactorTransform = ToStartOfYearImpl; +}; + +template +struct WeekTransformer +{ + explicit WeekTransformer(Transform transform_) : transform(std::move(transform_)) {} @@ -139,7 +126,7 @@ struct CustomWeekTransformImpl template static ColumnPtr execute(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/, Transform transform = {}) { - const auto op = Transformer{std::move(transform)}; + const auto op = WeekTransformer{std::move(transform)}; UInt8 week_mode = DEFAULT_WEEK_MODE; if (arguments.size() > 1) diff --git a/src/Functions/FunctionCustomWeekToSomething.h b/src/Functions/FunctionCustomWeekToSomething.h index 0ef6a342218..84ecd4d7a5a 100644 --- a/src/Functions/FunctionCustomWeekToSomething.h +++ b/src/Functions/FunctionCustomWeekToSomething.h @@ -10,6 +10,7 @@ namespace DB { + namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; @@ -143,15 +144,15 @@ public: if (checkAndGetDataType(&type)) { - return Transform::FactorTransform::execute(UInt16(left.get()), DEFAULT_WEEK_MODE, date_lut) - == Transform::FactorTransform::execute(UInt16(right.get()), DEFAULT_WEEK_MODE, date_lut) + return Transform::FactorTransform::execute(UInt16(left.get()), date_lut) + == Transform::FactorTransform::execute(UInt16(right.get()), date_lut) ? is_monotonic : is_not_monotonic; } else { - return Transform::FactorTransform::execute(UInt32(left.get()), DEFAULT_WEEK_MODE, date_lut) - == Transform::FactorTransform::execute(UInt32(right.get()), DEFAULT_WEEK_MODE, date_lut) + return Transform::FactorTransform::execute(UInt32(left.get()), date_lut) + == Transform::FactorTransform::execute(UInt32(right.get()), date_lut) ? is_monotonic : is_not_monotonic; } diff --git a/src/Interpreters/examples/jit_example.cpp b/src/Interpreters/examples/jit_example.cpp index db02d843f28..9694314b820 100644 --- a/src/Interpreters/examples/jit_example.cpp +++ b/src/Interpreters/examples/jit_example.cpp @@ -1,57 +1,57 @@ #include -// #include +#include -// #include +#include -// void test_function() -// { -// std::cerr << "Test function" << std::endl; -// } +void test_function() +{ + std::cerr << "Test function" << std::endl; +} int main(int argc, char **argv) { (void)(argc); (void)(argv); - // auto jit = DB::CHJIT(); + auto jit = DB::CHJIT(); - // jit.registerExternalSymbol("test_function", reinterpret_cast(&test_function)); + jit.registerExternalSymbol("test_function", reinterpret_cast(&test_function)); - // auto compiled_module_info = jit.compileModule([](llvm::Module & module) - // { - // auto & context = module.getContext(); - // llvm::IRBuilder<> b (context); + auto compiled_module_info = jit.compileModule([](llvm::Module & module) + { + auto & context = module.getContext(); + llvm::IRBuilder<> b (context); - // auto * func_declaration_type = llvm::FunctionType::get(b.getVoidTy(), { }, /*isVarArg=*/false); - // auto * func_declaration = llvm::Function::Create(func_declaration_type, llvm::Function::ExternalLinkage, "test_function", module); + auto * func_declaration_type = llvm::FunctionType::get(b.getVoidTy(), { }, /*isVarArg=*/false); + auto * func_declaration = llvm::Function::Create(func_declaration_type, llvm::Function::ExternalLinkage, "test_function", module); - // auto * value_type = b.getInt64Ty(); - // auto * pointer_type = value_type->getPointerTo(); + auto * value_type = b.getInt64Ty(); + auto * pointer_type = value_type->getPointerTo(); - // auto * func_type = llvm::FunctionType::get(b.getVoidTy(), { pointer_type }, /*isVarArg=*/false); - // auto * function = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, "test_name", module); - // auto * entry = llvm::BasicBlock::Create(context, "entry", function); + auto * func_type = llvm::FunctionType::get(b.getVoidTy(), { pointer_type }, /*isVarArg=*/false); + auto * function = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, "test_name", module); + auto * entry = llvm::BasicBlock::Create(context, "entry", function); - // auto * argument = function->args().begin(); - // b.SetInsertPoint(entry); + auto * argument = function->args().begin(); + b.SetInsertPoint(entry); - // b.CreateCall(func_declaration); + b.CreateCall(func_declaration); - // auto * load_argument = b.CreateLoad(value_type, argument); - // auto * value = b.CreateAdd(load_argument, load_argument); - // b.CreateRet(value); - // }); + auto * load_argument = b.CreateLoad(value_type, argument); + auto * value = b.CreateAdd(load_argument, load_argument); + b.CreateRet(value); + }); - // for (const auto & compiled_function_name : compiled_module_info.compiled_functions) - // { - // std::cerr << compiled_function_name << std::endl; - // } + for (const auto & compiled_function_name : compiled_module_info.compiled_functions) + { + std::cerr << compiled_function_name << std::endl; + } - // int64_t value = 5; - // auto * test_name_function = reinterpret_cast(jit.findCompiledFunction(compiled_module_info, "test_name")); - // auto result = test_name_function(&value); - // std::cerr << "Result " << result << std::endl; + int64_t value = 5; + auto * test_name_function = reinterpret_cast(jit.findCompiledFunction(compiled_module_info, "test_name")); + auto result = test_name_function(&value); + std::cerr << "Result " << result << std::endl; return 0; } diff --git a/src/Storages/HDFS/HDFSCommon.cpp b/src/Storages/HDFS/HDFSCommon.cpp index 40f52921008..d7e57a0f9eb 100644 --- a/src/Storages/HDFS/HDFSCommon.cpp +++ b/src/Storages/HDFS/HDFSCommon.cpp @@ -145,6 +145,7 @@ HDFSBuilderWrapper createHDFSBuilder(const String & uri_str, const Poco::Util::A hdfsBuilderSetUserName(builder.get(), user.c_str()); } + hdfsBuilderSetNameNode(builder.get(), host.c_str()); if (port != 0) { diff --git a/src/Storages/HDFS/HDFSCommon.h b/src/Storages/HDFS/HDFSCommon.h index 154c253a76b..5c70a8997c3 100644 --- a/src/Storages/HDFS/HDFSCommon.h +++ b/src/Storages/HDFS/HDFSCommon.h @@ -1,13 +1,15 @@ #pragma once +#if !defined(ARCADIA_BUILD) #include +#endif #if USE_HDFS #include #include #include -#include +#include // Y_IGNORE #include #include diff --git a/src/Storages/HDFS/ReadBufferFromHDFS.cpp b/src/Storages/HDFS/ReadBufferFromHDFS.cpp index 29ea46c7590..f81d7736db3 100644 --- a/src/Storages/HDFS/ReadBufferFromHDFS.cpp +++ b/src/Storages/HDFS/ReadBufferFromHDFS.cpp @@ -13,6 +13,8 @@ namespace ErrorCodes { extern const int NETWORK_ERROR; extern const int CANNOT_OPEN_FILE; + extern const int CANNOT_SEEK_THROUGH_FILE; + extern const int SEEK_POSITION_OUT_OF_BOUND; } ReadBufferFromHDFS::~ReadBufferFromHDFS() = default; @@ -29,6 +31,9 @@ struct ReadBufferFromHDFS::ReadBufferFromHDFSImpl HDFSBuilderWrapper builder; HDFSFSPtr fs; + off_t offset = 0; + bool initialized = false; + explicit ReadBufferFromHDFSImpl( const std::string & hdfs_uri_, const std::string & hdfs_file_path_, @@ -48,8 +53,30 @@ struct ReadBufferFromHDFS::ReadBufferFromHDFSImpl hdfs_uri + hdfs_file_path, std::string(hdfsGetLastError())); } - int read(char * start, size_t size) const + ~ReadBufferFromHDFSImpl() { + std::lock_guard lock(hdfs_init_mutex); + hdfsCloseFile(fs.get(), fin); + } + + void initialize() const + { + if (!offset) + return; + + int seek_status = hdfsSeek(fs.get(), fin, offset); + if (seek_status != 0) + throw Exception(ErrorCodes::CANNOT_SEEK_THROUGH_FILE, "Fail to seek HDFS file: {}, error: {}", hdfs_uri, std::string(hdfsGetLastError())); + } + + int read(char * start, size_t size) + { + if (!initialized) + { + initialize(); + initialized = true; + } + int bytes_read = hdfsRead(fs.get(), fin, start, size); if (bytes_read < 0) throw Exception(ErrorCodes::NETWORK_ERROR, @@ -58,10 +85,25 @@ struct ReadBufferFromHDFS::ReadBufferFromHDFSImpl return bytes_read; } - ~ReadBufferFromHDFSImpl() + int seek(off_t offset_, int whence) { - std::lock_guard lock(hdfs_init_mutex); - hdfsCloseFile(fs.get(), fin); + if (initialized) + throw Exception("Seek is allowed only before first read attempt from the buffer.", ErrorCodes::CANNOT_SEEK_THROUGH_FILE); + + if (whence != SEEK_SET) + throw Exception("Only SEEK_SET mode is allowed.", ErrorCodes::CANNOT_SEEK_THROUGH_FILE); + + if (offset_ < 0) + throw Exception(ErrorCodes::SEEK_POSITION_OUT_OF_BOUND, "Seek position is out of bounds. Offset: {}", std::to_string(offset_)); + + offset = offset_; + + return offset; + } + + int tell() const + { + return offset; } }; @@ -73,7 +115,7 @@ ReadBufferFromHDFS::ReadBufferFromHDFS( const String & hdfs_file_path_, const Poco::Util::AbstractConfiguration & config_, size_t buf_size_) - : BufferWithOwnMemory(buf_size_) + : BufferWithOwnMemory(buf_size_) , impl(std::make_unique(hdfs_uri_, hdfs_file_path_, config_)) { } @@ -90,6 +132,18 @@ bool ReadBufferFromHDFS::nextImpl() return true; } + +off_t ReadBufferFromHDFS::seek(off_t off, int whence) +{ + return impl->seek(off, whence); +} + + +off_t ReadBufferFromHDFS::getPosition() +{ + return impl->tell() + count(); +} + } #endif diff --git a/src/Storages/HDFS/ReadBufferFromHDFS.h b/src/Storages/HDFS/ReadBufferFromHDFS.h index bd14e3d3792..498056ea376 100644 --- a/src/Storages/HDFS/ReadBufferFromHDFS.h +++ b/src/Storages/HDFS/ReadBufferFromHDFS.h @@ -7,28 +7,34 @@ #include #include #include -#include +#include // Y_IGNORE #include #include +#include namespace DB { + /** Accepts HDFS path to file and opens it. * Closes file by himself (thus "owns" a file descriptor). */ -class ReadBufferFromHDFS : public BufferWithOwnMemory +class ReadBufferFromHDFS : public BufferWithOwnMemory { struct ReadBufferFromHDFSImpl; public: ReadBufferFromHDFS(const String & hdfs_uri_, const String & hdfs_file_path_, - const Poco::Util::AbstractConfiguration &, size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE); + const Poco::Util::AbstractConfiguration & config_, size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE); ~ReadBufferFromHDFS() override; bool nextImpl() override; + off_t seek(off_t offset_, int whence) override; + + off_t getPosition() override; + private: std::unique_ptr impl; }; diff --git a/src/Storages/HDFS/StorageHDFS.cpp b/src/Storages/HDFS/StorageHDFS.cpp index 68c2d022ab8..1428e80f9af 100644 --- a/src/Storages/HDFS/StorageHDFS.cpp +++ b/src/Storages/HDFS/StorageHDFS.cpp @@ -64,7 +64,6 @@ public: struct SourcesInfo { std::vector uris; - std::atomic next_uri_to_read = 0; bool need_path_column = false; diff --git a/src/Storages/HDFS/WriteBufferFromHDFS.cpp b/src/Storages/HDFS/WriteBufferFromHDFS.cpp index c1323df86f0..5696e8f247f 100644 --- a/src/Storages/HDFS/WriteBufferFromHDFS.cpp +++ b/src/Storages/HDFS/WriteBufferFromHDFS.cpp @@ -27,20 +27,24 @@ struct WriteBufferFromHDFS::WriteBufferFromHDFSImpl HDFSBuilderWrapper builder; HDFSFSPtr fs; - explicit WriteBufferFromHDFSImpl(const std::string & hdfs_name_, const Poco::Util::AbstractConfiguration & config_) - : hdfs_uri(hdfs_name_) - , builder(createHDFSBuilder(hdfs_uri,config_)) + explicit WriteBufferFromHDFSImpl( + const std::string & hdfs_uri_, + const Poco::Util::AbstractConfiguration & config_, + int flags) + : hdfs_uri(hdfs_uri_) + , builder(createHDFSBuilder(hdfs_uri, config_)) , fs(createHDFSFS(builder.get())) { const size_t begin_of_path = hdfs_uri.find('/', hdfs_uri.find("//") + 2); const String path = hdfs_uri.substr(begin_of_path); + if (path.find_first_of("*?{") != std::string::npos) - throw Exception("URI '" + hdfs_uri + "' contains globs, so the table is in readonly mode", ErrorCodes::CANNOT_OPEN_FILE); + throw Exception(ErrorCodes::CANNOT_OPEN_FILE, "URI '{}' contains globs, so the table is in readonly mode", hdfs_uri); if (!hdfsExists(fs.get(), path.c_str())) - throw Exception("File: " + path + " is already exists", ErrorCodes::BAD_ARGUMENTS); + throw Exception(ErrorCodes::BAD_ARGUMENTS, "File {} already exists", path); - fout = hdfsOpenFile(fs.get(), path.c_str(), O_WRONLY, 0, 0, 0); /// O_WRONLY meaning create or overwrite i.e., implies O_TRUNCAT here + fout = hdfsOpenFile(fs.get(), path.c_str(), flags, 0, 0, 0); /// O_WRONLY meaning create or overwrite i.e., implies O_TRUNCAT here if (fout == nullptr) { @@ -76,9 +80,13 @@ struct WriteBufferFromHDFS::WriteBufferFromHDFSImpl } }; -WriteBufferFromHDFS::WriteBufferFromHDFS(const std::string & hdfs_name_, const Poco::Util::AbstractConfiguration & config_, size_t buf_size_) +WriteBufferFromHDFS::WriteBufferFromHDFS( + const std::string & hdfs_name_, + const Poco::Util::AbstractConfiguration & config_, + size_t buf_size_, + int flags_) : BufferWithOwnMemory(buf_size_) - , impl(std::make_unique(hdfs_name_, config_)) + , impl(std::make_unique(hdfs_name_, config_, flags_)) { } diff --git a/src/Storages/HDFS/WriteBufferFromHDFS.h b/src/Storages/HDFS/WriteBufferFromHDFS.h index a28caaf60ee..9dc74e69d40 100644 --- a/src/Storages/HDFS/WriteBufferFromHDFS.h +++ b/src/Storages/HDFS/WriteBufferFromHDFS.h @@ -8,6 +8,7 @@ #include #include + namespace DB { /** Accepts HDFS path to file and opens it. @@ -15,11 +16,13 @@ namespace DB */ class WriteBufferFromHDFS final : public BufferWithOwnMemory { - struct WriteBufferFromHDFSImpl; - std::unique_ptr impl; public: - WriteBufferFromHDFS(const std::string & hdfs_name_, const Poco::Util::AbstractConfiguration &, size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE); + WriteBufferFromHDFS( + const std::string & hdfs_name_, + const Poco::Util::AbstractConfiguration & config_, + size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE, + int flags = O_WRONLY); WriteBufferFromHDFS(WriteBufferFromHDFS &&) = default; @@ -30,6 +33,11 @@ public: void sync() override; void finalize() override; + +private: + struct WriteBufferFromHDFSImpl; + std::unique_ptr impl; }; + } #endif diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 4e151bfdb91..f5edde01478 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -343,6 +343,9 @@ SelectPartsDecision MergeTreeDataMergerMutator::selectPartsToMerge( if (parts_to_merge.empty()) { SimpleMergeSelector::Settings merge_settings; + /// Override value from table settings + merge_settings.max_parts_to_merge_at_once = data_settings->max_parts_to_merge_at_once; + if (aggressive) merge_settings.base = 1; diff --git a/src/Storages/MergeTree/MergeTreeSettings.h b/src/Storages/MergeTree/MergeTreeSettings.h index da953eb0f47..888ca80e015 100644 --- a/src/Storages/MergeTree/MergeTreeSettings.h +++ b/src/Storages/MergeTree/MergeTreeSettings.h @@ -56,6 +56,7 @@ struct Settings; M(UInt64, write_ahead_log_interval_ms_to_fsync, 100, "Interval in milliseconds after which fsync for WAL is being done.", 0) \ M(Bool, in_memory_parts_insert_sync, false, "If true insert of part with in-memory format will wait for fsync of WAL", 0) \ M(UInt64, non_replicated_deduplication_window, 0, "How many last blocks of hashes should be kept on disk (0 - disabled).", 0) \ + M(UInt64, max_parts_to_merge_at_once, 100, "Max amount of parts which can be merged at once (0 - disabled). Doesn't affect OPTIMIZE FINAL query.", 0) \ \ /** Inserts settings. */ \ M(UInt64, parts_to_delay_insert, 150, "If table contains at least that many active parts in single partition, artificially slow down insert into table.", 0) \ diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp index f960c04abbe..30569e53f64 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp @@ -268,9 +268,9 @@ void ReplicatedMergeTreeQueue::removeCoveredPartsFromMutations(const String & pa bool some_mutations_are_probably_done = false; - for (auto it = in_partition->second.begin(); it != in_partition->second.end(); ++it) + for (auto & it : in_partition->second) { - MutationStatus & status = *it->second; + MutationStatus & status = *it.second; if (remove_part && remove_covered_parts) status.parts_to_do.removePartAndCoveredParts(part_name); diff --git a/src/Storages/MergeTree/SimpleMergeSelector.h b/src/Storages/MergeTree/SimpleMergeSelector.h index 4f277ad74cd..af339dbfa24 100644 --- a/src/Storages/MergeTree/SimpleMergeSelector.h +++ b/src/Storages/MergeTree/SimpleMergeSelector.h @@ -88,7 +88,7 @@ class SimpleMergeSelector final : public IMergeSelector public: struct Settings { - /// Zero means unlimited. + /// Zero means unlimited. Can be overridden by the same merge tree setting. size_t max_parts_to_merge_at_once = 100; /** Minimum ratio of size of one part to all parts in set of parts to merge (for usual cases). diff --git a/tests/integration/test_disk_types/configs/storage.xml b/tests/integration/test_disk_types/configs/storage.xml index 2bf9a2e363a..1167a4f7382 100644 --- a/tests/integration/test_disk_types/configs/storage.xml +++ b/tests/integration/test_disk_types/configs/storage.xml @@ -11,6 +11,10 @@ memory + + hdfs + http://hdfs1:9000/data/ + diff --git a/tests/integration/test_disk_types/test.py b/tests/integration/test_disk_types/test.py index ad09519a484..3f1a656d98f 100644 --- a/tests/integration/test_disk_types/test.py +++ b/tests/integration/test_disk_types/test.py @@ -5,6 +5,7 @@ disk_types = { "default": "local", "disk_s3": "s3", "disk_memory": "memory", + "disk_hdfs": "hdfs", } @@ -12,7 +13,7 @@ disk_types = { def cluster(): try: cluster = ClickHouseCluster(__file__) - cluster.add_instance("node", main_configs=["configs/storage.xml"], with_minio=True) + cluster.add_instance("node", main_configs=["configs/storage.xml"], with_minio=True, with_hdfs=True) cluster.start() yield cluster finally: @@ -35,3 +36,4 @@ def test_select_by_type(cluster): node = cluster.instances["node"] for name, disk_type in list(disk_types.items()): assert node.query("SELECT name FROM system.disks WHERE type='" + disk_type + "'") == name + "\n" + diff --git a/tests/integration/test_log_family_hdfs/__init__.py b/tests/integration/test_log_family_hdfs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/test_log_family_hdfs/configs/config.d/log_conf.xml b/tests/integration/test_log_family_hdfs/configs/config.d/log_conf.xml new file mode 100644 index 00000000000..318a6bca95d --- /dev/null +++ b/tests/integration/test_log_family_hdfs/configs/config.d/log_conf.xml @@ -0,0 +1,12 @@ + + 3 + + trace + /var/log/clickhouse-server/log.log + /var/log/clickhouse-server/log.err.log + 1000M + 10 + /var/log/clickhouse-server/stderr.log + /var/log/clickhouse-server/stdout.log + + diff --git a/tests/integration/test_log_family_hdfs/configs/storage_conf.xml b/tests/integration/test_log_family_hdfs/configs/storage_conf.xml new file mode 100644 index 00000000000..15ff3891474 --- /dev/null +++ b/tests/integration/test_log_family_hdfs/configs/storage_conf.xml @@ -0,0 +1,11 @@ + + + + + + hdfs + hdfs://hdfs1:9000/clickhouse/ + + + + diff --git a/tests/integration/test_log_family_hdfs/test.py b/tests/integration/test_log_family_hdfs/test.py new file mode 100644 index 00000000000..44f6904e8ea --- /dev/null +++ b/tests/integration/test_log_family_hdfs/test.py @@ -0,0 +1,59 @@ +import logging +import sys + +import pytest +from helpers.cluster import ClickHouseCluster + +from pyhdfs import HdfsClient + + +@pytest.fixture(scope="module") +def cluster(): + try: + cluster = ClickHouseCluster(__file__) + cluster.add_instance("node", + main_configs=["configs/storage_conf.xml", "configs/config.d/log_conf.xml"], + with_hdfs=True) + logging.info("Starting cluster...") + cluster.start() + logging.info("Cluster started") + + fs = HdfsClient(hosts='localhost') + fs.mkdirs('/clickhouse') + + yield cluster + finally: + cluster.shutdown() + + +def assert_objects_count(cluster, objects_count, path='data/'): + fs = HdfsClient(hosts='localhost') + hdfs_objects = fs.listdir('/clickhouse') + assert objects_count == len(hdfs_objects) + + +@pytest.mark.parametrize( + "log_engine,files_overhead,files_overhead_per_insert", + [("TinyLog", 1, 1), ("Log", 2, 1), ("StripeLog", 1, 2)]) +def test_log_family_hdfs(cluster, log_engine, files_overhead, files_overhead_per_insert): + node = cluster.instances["node"] + + node.query("CREATE TABLE hdfs_test (id UInt64) ENGINE={} SETTINGS disk = 'hdfs'".format(log_engine)) + + node.query("INSERT INTO hdfs_test SELECT number FROM numbers(5)") + assert node.query("SELECT * FROM hdfs_test") == "0\n1\n2\n3\n4\n" + assert_objects_count(cluster, files_overhead_per_insert + files_overhead) + + node.query("INSERT INTO hdfs_test SELECT number + 5 FROM numbers(3)") + assert node.query("SELECT * FROM hdfs_test order by id") == "0\n1\n2\n3\n4\n5\n6\n7\n" + assert_objects_count(cluster, files_overhead_per_insert * 2 + files_overhead) + + node.query("INSERT INTO hdfs_test SELECT number + 8 FROM numbers(1)") + assert node.query("SELECT * FROM hdfs_test order by id") == "0\n1\n2\n3\n4\n5\n6\n7\n8\n" + assert_objects_count(cluster, files_overhead_per_insert * 3 + files_overhead) + + node.query("TRUNCATE TABLE hdfs_test") + assert_objects_count(cluster, 0) + + node.query("DROP TABLE hdfs_test") + diff --git a/tests/integration/test_merge_tree_hdfs/__init__.py b/tests/integration/test_merge_tree_hdfs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/test_merge_tree_hdfs/configs/config.d/log_conf.xml b/tests/integration/test_merge_tree_hdfs/configs/config.d/log_conf.xml new file mode 100644 index 00000000000..318a6bca95d --- /dev/null +++ b/tests/integration/test_merge_tree_hdfs/configs/config.d/log_conf.xml @@ -0,0 +1,12 @@ + + 3 + + trace + /var/log/clickhouse-server/log.log + /var/log/clickhouse-server/log.err.log + 1000M + 10 + /var/log/clickhouse-server/stderr.log + /var/log/clickhouse-server/stdout.log + + diff --git a/tests/integration/test_merge_tree_hdfs/configs/config.d/storage_conf.xml b/tests/integration/test_merge_tree_hdfs/configs/config.d/storage_conf.xml new file mode 100644 index 00000000000..d43e18fa99c --- /dev/null +++ b/tests/integration/test_merge_tree_hdfs/configs/config.d/storage_conf.xml @@ -0,0 +1,30 @@ + + + + + hdfs + hdfs://hdfs1:9000/clickhouse/ + + + local + / + + + + + +
+ hdfs +
+ + hdd + +
+
+
+
+ + + 0 + +
diff --git a/tests/integration/test_merge_tree_hdfs/test.py b/tests/integration/test_merge_tree_hdfs/test.py new file mode 100644 index 00000000000..2d0d9d9fb1e --- /dev/null +++ b/tests/integration/test_merge_tree_hdfs/test.py @@ -0,0 +1,317 @@ +import logging +import random +import string +import time +import threading +import os + +import pytest +from helpers.cluster import ClickHouseCluster + +from pyhdfs import HdfsClient + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +CONFIG_PATH = os.path.join(SCRIPT_DIR, './_instances/node/configs/config.d/storage_conf.xml') + + +def create_table(cluster, table_name, additional_settings=None): + node = cluster.instances["node"] + + create_table_statement = """ + CREATE TABLE {} ( + dt Date, id Int64, data String, + INDEX min_max (id) TYPE minmax GRANULARITY 3 + ) ENGINE=MergeTree() + PARTITION BY dt + ORDER BY (dt, id) + SETTINGS + storage_policy='hdfs', + old_parts_lifetime=0, + index_granularity=512 + """.format(table_name) + + if additional_settings: + create_table_statement += "," + create_table_statement += additional_settings + + node.query(create_table_statement) + + +FILES_OVERHEAD = 1 +FILES_OVERHEAD_PER_COLUMN = 2 # Data and mark files +FILES_OVERHEAD_PER_PART_WIDE = FILES_OVERHEAD_PER_COLUMN * 3 + 2 + 6 + 1 +FILES_OVERHEAD_PER_PART_COMPACT = 10 + 1 + + +def random_string(length): + letters = string.ascii_letters + return ''.join(random.choice(letters) for i in range(length)) + + +def generate_values(date_str, count, sign=1): + data = [[date_str, sign * (i + 1), random_string(10)] for i in range(count)] + data.sort(key=lambda tup: tup[1]) + return ",".join(["('{}',{},'{}')".format(x, y, z) for x, y, z in data]) + + +@pytest.fixture(scope="module") +def cluster(): + try: + cluster = ClickHouseCluster(__file__) + cluster.add_instance("node", main_configs=["configs/config.d/storage_conf.xml", + "configs/config.d/log_conf.xml"], with_hdfs=True) + logging.info("Starting cluster...") + cluster.start() + logging.info("Cluster started") + + fs = HdfsClient(hosts='localhost') + fs.mkdirs('/clickhouse') + + logging.info("Created HDFS directory") + + yield cluster + finally: + cluster.shutdown() + + +def wait_for_delete_hdfs_objects(cluster, expected, num_tries=30): + fs = HdfsClient(hosts='localhost') + while num_tries > 0: + num_hdfs_objects = len(fs.listdir('/clickhouse')) + if num_hdfs_objects == expected: + break; + num_tries -= 1 + time.sleep(1) + assert(len(fs.listdir('/clickhouse')) == expected) + + +@pytest.fixture(autouse=True) +def drop_table(cluster): + node = cluster.instances["node"] + + fs = HdfsClient(hosts='localhost') + hdfs_objects = fs.listdir('/clickhouse') + print('Number of hdfs objects to delete:', len(hdfs_objects), sep=' ') + + node.query("DROP TABLE IF EXISTS hdfs_test SYNC") + + try: + wait_for_delete_hdfs_objects(cluster, 0) + finally: + hdfs_objects = fs.listdir('/clickhouse') + if len(hdfs_objects) == 0: + return + print("Manually removing extra objects to prevent tests cascade failing: ", hdfs_objects) + for path in hdfs_objects: + fs.delete(path) + + +@pytest.mark.parametrize("min_rows_for_wide_part,files_per_part", [(0, FILES_OVERHEAD_PER_PART_WIDE), (8192, FILES_OVERHEAD_PER_PART_COMPACT)]) +def test_simple_insert_select(cluster, min_rows_for_wide_part, files_per_part): + create_table(cluster, "hdfs_test", additional_settings="min_rows_for_wide_part={}".format(min_rows_for_wide_part)) + + node = cluster.instances["node"] + + values1 = generate_values('2020-01-03', 4096) + node.query("INSERT INTO hdfs_test VALUES {}".format(values1)) + assert node.query("SELECT * FROM hdfs_test order by dt, id FORMAT Values") == values1 + + fs = HdfsClient(hosts='localhost') + + hdfs_objects = fs.listdir('/clickhouse') + print(hdfs_objects) + assert len(hdfs_objects) == FILES_OVERHEAD + files_per_part + + values2 = generate_values('2020-01-04', 4096) + node.query("INSERT INTO hdfs_test VALUES {}".format(values2)) + assert node.query("SELECT * FROM hdfs_test ORDER BY dt, id FORMAT Values") == values1 + "," + values2 + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + files_per_part * 2 + + assert node.query("SELECT count(*) FROM hdfs_test where id = 1 FORMAT Values") == "(2)" + + +def test_alter_table_columns(cluster): + create_table(cluster, "hdfs_test") + + node = cluster.instances["node"] + fs = HdfsClient(hosts='localhost') + + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096, -1))) + + node.query("ALTER TABLE hdfs_test ADD COLUMN col1 UInt64 DEFAULT 1") + # To ensure parts have merged + node.query("OPTIMIZE TABLE hdfs_test") + + assert node.query("SELECT sum(col1) FROM hdfs_test FORMAT Values") == "(8192)" + assert node.query("SELECT sum(col1) FROM hdfs_test WHERE id > 0 FORMAT Values") == "(4096)" + wait_for_delete_hdfs_objects(cluster, FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE + FILES_OVERHEAD_PER_COLUMN) + + node.query("ALTER TABLE hdfs_test MODIFY COLUMN col1 String", settings={"mutations_sync": 2}) + + assert node.query("SELECT distinct(col1) FROM hdfs_test FORMAT Values") == "('1')" + # and file with mutation + wait_for_delete_hdfs_objects(cluster, FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE + FILES_OVERHEAD_PER_COLUMN + 1) + + node.query("ALTER TABLE hdfs_test DROP COLUMN col1", settings={"mutations_sync": 2}) + + # and 2 files with mutations + wait_for_delete_hdfs_objects(cluster, FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE + 2) + + +def test_attach_detach_partition(cluster): + create_table(cluster, "hdfs_test") + + node = cluster.instances["node"] + fs = HdfsClient(hosts='localhost') + + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-04', 4096))) + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("ALTER TABLE hdfs_test DETACH PARTITION '2020-01-03'") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(4096)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("ALTER TABLE hdfs_test ATTACH PARTITION '2020-01-03'") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("ALTER TABLE hdfs_test DROP PARTITION '2020-01-03'") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(4096)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE + + node.query("ALTER TABLE hdfs_test DETACH PARTITION '2020-01-04'") + node.query("ALTER TABLE hdfs_test DROP DETACHED PARTITION '2020-01-04'", settings={"allow_drop_detached": 1}) + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(0)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + + +def test_move_partition_to_another_disk(cluster): + create_table(cluster, "hdfs_test") + + node = cluster.instances["node"] + fs = HdfsClient(hosts='localhost') + + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-04', 4096))) + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("ALTER TABLE hdfs_test MOVE PARTITION '2020-01-04' TO DISK 'hdd'") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE + + node.query("ALTER TABLE hdfs_test MOVE PARTITION '2020-01-04' TO DISK 'hdfs'") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + +def test_table_manipulations(cluster): + create_table(cluster, "hdfs_test") + + node = cluster.instances["node"] + fs = HdfsClient(hosts='localhost') + + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-04', 4096))) + + node.query("RENAME TABLE hdfs_test TO hdfs_renamed") + assert node.query("SELECT count(*) FROM hdfs_renamed FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("RENAME TABLE hdfs_renamed TO hdfs_test") + assert node.query("CHECK TABLE hdfs_test FORMAT Values") == "(1)" + + node.query("DETACH TABLE hdfs_test") + node.query("ATTACH TABLE hdfs_test") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 2 + + node.query("TRUNCATE TABLE hdfs_test") + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(0)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + + +def test_move_replace_partition_to_another_table(cluster): + create_table(cluster, "hdfs_test") + + node = cluster.instances["node"] + fs = HdfsClient(hosts='localhost') + + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-04', 4096))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-05', 4096, -1))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-06', 4096, -1))) + assert node.query("SELECT sum(id) FROM hdfs_test FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(16384)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 4 + + create_table(cluster, "hdfs_clone") + + node.query("ALTER TABLE hdfs_test MOVE PARTITION '2020-01-03' TO TABLE hdfs_clone") + node.query("ALTER TABLE hdfs_test MOVE PARTITION '2020-01-05' TO TABLE hdfs_clone") + assert node.query("SELECT sum(id) FROM hdfs_test FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(8192)" + assert node.query("SELECT sum(id) FROM hdfs_clone FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_clone FORMAT Values") == "(8192)" + + # Number of objects in HDFS should be unchanged. + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD * 2 + FILES_OVERHEAD_PER_PART_WIDE * 4 + + # Add new partitions to source table, but with different values and replace them from copied table. + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-03', 4096, -1))) + node.query("INSERT INTO hdfs_test VALUES {}".format(generate_values('2020-01-05', 4096))) + assert node.query("SELECT sum(id) FROM hdfs_test FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(16384)" + + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD * 2 + FILES_OVERHEAD_PER_PART_WIDE * 6 + + node.query("ALTER TABLE hdfs_test REPLACE PARTITION '2020-01-03' FROM hdfs_clone") + node.query("ALTER TABLE hdfs_test REPLACE PARTITION '2020-01-05' FROM hdfs_clone") + assert node.query("SELECT sum(id) FROM hdfs_test FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(16384)" + assert node.query("SELECT sum(id) FROM hdfs_clone FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_clone FORMAT Values") == "(8192)" + + # Wait for outdated partitions deletion. + print(1) + wait_for_delete_hdfs_objects(cluster, FILES_OVERHEAD * 2 + FILES_OVERHEAD_PER_PART_WIDE * 4) + + node.query("DROP TABLE hdfs_clone NO DELAY") + assert node.query("SELECT sum(id) FROM hdfs_test FORMAT Values") == "(0)" + assert node.query("SELECT count(*) FROM hdfs_test FORMAT Values") == "(16384)" + + # Data should remain in hdfs + hdfs_objects = fs.listdir('/clickhouse') + assert len(hdfs_objects) == FILES_OVERHEAD + FILES_OVERHEAD_PER_PART_WIDE * 4 + diff --git a/tests/performance/joins_in_memory_pmj.xml b/tests/performance/joins_in_memory_pmj.xml index d122dba72c3..05d1876cb06 100644 --- a/tests/performance/joins_in_memory_pmj.xml +++ b/tests/performance/joins_in_memory_pmj.xml @@ -1,4 +1,4 @@ - + CREATE TABLE ints (i64 Int64, i32 Int32, i16 Int16, i8 Int8) ENGINE = Memory diff --git a/tests/performance/set_index.xml b/tests/performance/set_index.xml index 1fb7cf967f3..551044e4195 100644 --- a/tests/performance/set_index.xml +++ b/tests/performance/set_index.xml @@ -1,4 +1,4 @@ - + CREATE TABLE test_in (`a` UInt32) ENGINE = MergeTree() ORDER BY a INSERT INTO test_in SELECT number FROM numbers(500000000) @@ -89,34 +89,4 @@ so we can't mark it as short.--> SELECT (rand(), rand()) IN tuple(tuple(17258, 93148), tuple(4508, 52749), tuple(68660, 70017), tuple(77797, 23528), tuple(1136, 37393), tuple(53237, 15379), tuple(68370, 73211), tuple(15782, 54962), tuple(59432, 45415), tuple(68396, 920), tuple(96154, 21016), tuple(12700, 26887), tuple(88016, 43191), tuple(68153, 51575), tuple(91315, 40005), tuple(18070, 73178), tuple(86, 631), tuple(77717, 20324), tuple(3227, 76188), tuple(74960, 43147), tuple(77538, 19628), tuple(82292, 6525), tuple(24293, 12566), tuple(85244, 96287), tuple(93982, 1329), tuple(38064, 54723), tuple(83999, 45810), tuple(71921, 53673), tuple(88638, 9669), tuple(1959, 39535), tuple(82235, 95796), tuple(27907, 90975), tuple(42383, 91015), tuple(9948, 91514), tuple(81712, 47309), tuple(400, 25808), tuple(31791, 46948), tuple(39740, 36098), tuple(25943, 84598), tuple(99598, 52939), tuple(77134, 15845), tuple(40313, 72174), tuple(85017, 94036), tuple(36595, 14303), tuple(83961, 68078), tuple(55792, 72759), tuple(73574, 43606), tuple(9853, 63560), tuple(28580, 56721), tuple(74804, 41025), tuple(32095, 55657), tuple(52881, 63416), tuple(91368, 90310), tuple(23922, 38883), tuple(30592, 10758), tuple(66448, 61183), tuple(31880, 96697), tuple(11362, 20633), tuple(75331, 2015), tuple(71129, 8785), tuple(1115, 70955), tuple(7886, 83698), tuple(18961, 84556), tuple(16677, 43028), tuple(37347, 70220), tuple(31699, 71244), tuple(10578, 96159), tuple(67600, 39041), tuple(78791, 86687), tuple(21545, 54174), tuple(68774, 37637), tuple(46132, 81768), tuple(98413, 20605), tuple(2960, 23665), tuple(31507, 35719), tuple(96209, 18368), tuple(60558, 38035), tuple(21952, 3264), tuple(11834, 86458), tuple(21651, 17650), tuple(86276, 36087), tuple(18818, 24849), tuple(61951, 3390), tuple(59637, 62545), tuple(30346, 72253), tuple(36281, 2992), tuple(78340, 49872), tuple(94326, 93723), tuple(3416, 94405), tuple(12272, 8741), tuple(22600, 22095), tuple(57636, 37106), tuple(38702, 14889), tuple(70238, 11276), tuple(17325, 60648), tuple(16492, 41271), tuple(52100, 1304), tuple(93416, 7795), tuple(57209, 71008), tuple(48010, 36078), tuple(20384, 74420), tuple(77440, 34439), tuple(69224, 45099), tuple(30374, 33884), tuple(49038, 90140), tuple(1154, 84725), tuple(64926, 86985), tuple(91746, 73472), tuple(59757, 75755), tuple(45860, 71557), tuple(45833, 36526), tuple(74618, 73598), tuple(91360, 65168), tuple(58029, 30793), tuple(56332, 14973), tuple(99943, 96877), tuple(97454, 6450), tuple(64502, 77301), tuple(73182, 31853), tuple(76809, 83964), tuple(82916, 86188), tuple(78736, 65427), tuple(36495, 7422), tuple(76196, 2804), tuple(96117, 61093), tuple(9177, 26099), tuple(52942, 63007), tuple(48578, 47876), tuple(50638, 89903), tuple(7113, 97316), tuple(35301, 12750), tuple(47807, 7254), tuple(38217, 55418), tuple(56970, 41687), tuple(20527, 62886), tuple(358, 14021), tuple(64018, 18582), tuple(91740, 21683), tuple(81967, 53589), tuple(45437, 38450), tuple(45476, 67752), tuple(76851, 72072), tuple(7304, 60091), tuple(40097, 12897), tuple(39906, 29247), tuple(84262, 58734), tuple(30857, 43791), tuple(56087, 78929), tuple(20498, 45954), tuple(48726, 500), tuple(62723, 43763), tuple(28368, 30756), tuple(74048, 52403), tuple(15045, 95926), tuple(75542, 55384), tuple(52543, 22525), tuple(56001, 6935), tuple(11431, 46745), tuple(77731, 7310), tuple(36718, 59909), tuple(32235, 91254), tuple(92417, 25917), tuple(21782, 79277), tuple(46378, 87536), tuple(35324, 26075), tuple(6310, 76915), tuple(1551, 69473), tuple(50642, 68865), tuple(55190, 72934), tuple(49780, 21873), tuple(99466, 29686), tuple(90761, 13179), tuple(72959, 57033), tuple(20020, 90200), tuple(46186, 79105), tuple(73871, 52382), tuple(59559, 38801), tuple(59916, 16082), tuple(33610, 94966), tuple(46001, 45225), tuple(86679, 26469), tuple(77245, 91929), tuple(32887, 36623), tuple(11179, 46898), tuple(87881, 68087), tuple(45438, 47991), tuple(24950, 94525), tuple(91664, 51656), tuple(43914, 47805), tuple(15736, 96156), tuple(56346, 20283), tuple(85053, 48931), tuple(17790, 26179), tuple(96195, 55728), tuple(43765, 54807), tuple(44988, 89269), tuple(55911, 99411), tuple(52446, 47397), tuple(28346, 65442), tuple(96669, 68226), tuple(66194, 26848), tuple(37276, 55864), tuple(14116, 41583), tuple(18058, 16317), tuple(93136, 85318), tuple(35616, 86252), tuple(29222, 29969), tuple(33386, 85372), tuple(71094, 44238), tuple(27733, 31838), tuple(64626, 16692), tuple(52904, 97899), tuple(97619, 12663), tuple(50165, 4688), tuple(67557, 44053), tuple(69184, 66269), tuple(73164, 89705), tuple(39822, 15169), tuple(65499, 72808), tuple(30068, 63697), tuple(30154, 64235), tuple(97016, 58716), tuple(94366, 36592), tuple(1592, 16261), tuple(87985, 52102), tuple(12554, 23652), tuple(15909, 25292), tuple(2527, 91531), tuple(92139, 36031), tuple(28986, 30032), tuple(3038, 56314), tuple(32239, 26707), tuple(15973, 34901), tuple(70246, 39680), tuple(82529, 38132), tuple(45827, 74783), tuple(53665, 64111), tuple(55218, 84170), tuple(20466, 16130), tuple(55734, 71203), tuple(31438, 96906), tuple(66338, 85858), tuple(35988, 68511), tuple(78391, 15191), tuple(80747, 59213), tuple(5357, 11546), tuple(16822, 16607), tuple(36607, 41106), tuple(74949, 30739), tuple(45726, 64887), tuple(1524, 54847), tuple(37371, 89195), tuple(28726, 27788), tuple(22600, 44777), tuple(53999, 63625), tuple(84304, 98338), tuple(49260, 76480), tuple(74564, 53907), tuple(89867, 97096), tuple(60157, 61299), tuple(17165, 10146), tuple(56334, 36268), tuple(62114, 49222), tuple(22715, 23620), tuple(42830, 11539), tuple(41091, 69151), tuple(75471, 68364), tuple(18681, 43249), tuple(42738, 63219), tuple(35474, 98454), tuple(76815, 46024), tuple(66310, 36521), tuple(86095, 77013), tuple(63693, 77319), tuple(80731, 63031), tuple(95478, 92387), tuple(23787, 63724), tuple(46299, 68994), tuple(4800, 2460), tuple(9663, 80639), tuple(77231, 85814), tuple(81615, 11311), tuple(35638, 27340), tuple(13598, 14322), tuple(30657, 17238), tuple(90957, 96846), tuple(69962, 52140), tuple(41681, 65962), tuple(96836, 58177), tuple(36190, 11623), tuple(4231, 40500), tuple(43049, 41949), tuple(71177, 98492), tuple(30193, 39750), tuple(19744, 33204), tuple(63358, 30210), tuple(45638, 58918), tuple(43641, 38741), tuple(35598, 40932), tuple(33238, 36236), tuple(50835, 20968), tuple(25099, 34071), tuple(84986, 88456), tuple(35333, 1529), tuple(79771, 23985), tuple(647, 61658), tuple(9424, 11743), tuple(77766, 31528), tuple(77811, 86973), tuple(76403, 74377), tuple(55568, 79251), tuple(68858, 20762), tuple(68520, 66773), tuple(93598, 89823), tuple(8080, 82539), tuple(87760, 52247), tuple(25191, 16905), tuple(17837, 8339), tuple(85177, 59050), tuple(51680, 77374), tuple(3287, 43018), tuple(43479, 62141), tuple(34909, 46322), tuple(11869, 5885), tuple(96193, 58417), tuple(101, 47460), tuple(34937, 88582), tuple(83216, 88388), tuple(28571, 15292), tuple(66683, 62613), tuple(34478, 8924), tuple(2680, 89973), tuple(62438, 44460), tuple(11724, 4791), tuple(5383, 72888), tuple(88206, 67586), tuple(8124, 21690), tuple(28779, 75789), tuple(66791, 4757), tuple(6176, 47760), tuple(6403, 78084), tuple(78122, 35446), tuple(99494, 73608), tuple(39691, 89098), tuple(59182, 19484), tuple(25389, 98963), tuple(96487, 3692), tuple(76222, 67381), tuple(21199, 50358), tuple(95998, 58137), tuple(28777, 43913), tuple(14176, 60117), tuple(52257, 81703), tuple(14604, 13438), tuple(71301, 14401), tuple(19758, 66914), tuple(15506, 29873), tuple(87205, 29449), tuple(93295, 15930), tuple(63651, 11287), tuple(19785, 15966), tuple(30795, 75112), tuple(69462, 37655), tuple(18793, 85764), tuple(36240, 31236), tuple(98153, 73724), tuple(72491, 4223), tuple(66930, 35048), tuple(25686, 13269), tuple(13940, 13259), tuple(69163, 11235), tuple(1183, 86961), tuple(54323, 67315), tuple(85044, 60872), tuple(48875, 3683), tuple(43052, 92861), tuple(87574, 32969), tuple(92552, 80564), tuple(94832, 47682), tuple(72011, 80994), tuple(60182, 917), tuple(97788, 34169), tuple(66432, 47940), tuple(87468, 80954), tuple(35385, 68758), tuple(50555, 63710), tuple(55311, 44337), tuple(87065, 26514), tuple(84581, 98736), tuple(23212, 56499), tuple(75120, 72447), tuple(56087, 38285), tuple(58171, 45629), tuple(28401, 44319), tuple(70432, 27883), tuple(18891, 14646), tuple(26206, 49924), tuple(79957, 44914), tuple(56064, 27529), tuple(99090, 29197), tuple(49435, 340), tuple(53525, 65601), tuple(76998, 88349), tuple(50416, 70860), tuple(42506, 75290), tuple(34024, 13295), tuple(86663, 46523), tuple(88814, 231), tuple(57809, 21), tuple(84914, 84771), tuple(43042, 66892), tuple(17288, 33908), tuple(4934, 63195), tuple(50590, 1516), tuple(97843, 80208), tuple(20091, 86717), tuple(71566, 15929), tuple(19531, 23634), tuple(41646, 45549), tuple(89226, 82902), tuple(96683, 63386), tuple(31072, 53788), tuple(51135, 41099), tuple(78912, 65609), tuple(36094, 23603), tuple(88403, 51455), tuple(73795, 47066), tuple(26448, 82852), tuple(22829, 2894), tuple(30041, 92548), tuple(27733, 20608), tuple(70180, 19892), tuple(51650, 63440), tuple(76328, 13666), tuple(40514, 6677), tuple(2786, 51059), tuple(40809, 16499), tuple(10857, 82541), tuple(78221, 61067), tuple(17982, 51969), tuple(85369, 66965), tuple(47153, 47149), tuple(43965, 75796), tuple(82725, 60767), tuple(42407, 97249), tuple(51475, 81224), tuple(60957, 89414), tuple(33065, 21663), tuple(36601, 5290), tuple(95842, 67301), tuple(64630, 60398), tuple(55212, 35638), tuple(41750, 44235), tuple(75260, 82400), tuple(91291, 25843), tuple(6477, 8311), tuple(14919, 52306), tuple(66220, 33180), tuple(45736, 2313), tuple(37450, 64444), tuple(98614, 61344), tuple(75007, 50946), tuple(56701, 28117), tuple(66632, 5174), tuple(92323, 76613), tuple(6796, 73695), tuple(33696, 76280), tuple(86876, 5614), tuple(50863, 67993), tuple(36068, 17049), tuple(91912, 34271), tuple(70706, 1904), tuple(97798, 41117), tuple(68154, 72483), tuple(83862, 25578), tuple(61643, 17204), tuple(69974, 64232), tuple(77926, 19637), tuple(64901, 88988), tuple(71424, 91703), tuple(91655, 17147), tuple(46872, 56530), tuple(44189, 98087), tuple(95939, 54420), tuple(72651, 68785), tuple(67624, 84875), tuple(92587, 87663), tuple(65275, 81256), tuple(53798, 2506), tuple(14702, 3638), tuple(71291, 50452), tuple(14909, 13903), tuple(66965, 26606), tuple(14127, 60345), tuple(35306, 1738), tuple(77234, 10468), tuple(53521, 41218), tuple(80681, 82583), tuple(44227, 26521), tuple(32263, 21482), tuple(82270, 56963), tuple(50580, 80567), tuple(11593, 22346), tuple(20074, 26867), tuple(73126, 28667), tuple(62996, 24317), tuple(20295, 57163), tuple(1506, 57668), tuple(69567, 45236), tuple(43366, 26001), tuple(88052, 40181), tuple(1599, 89349), tuple(36789, 1579), tuple(39895, 46673), tuple(30381, 3206), tuple(31723, 5625), tuple(19252, 31317), tuple(16932, 77149), tuple(48794, 34409), tuple(55986, 30328), tuple(47551, 75088), tuple(57363, 78365), tuple(95221, 63385), tuple(26449, 5733), tuple(96588, 53077), tuple(52980, 41140), tuple(8187, 85947), tuple(36723, 26520), tuple(23579, 38909), tuple(33350, 19275), tuple(63930, 19357), tuple(43536, 59941), tuple(31117, 77322), tuple(44638, 94812), tuple(44730, 99097), tuple(95108, 48170), tuple(57813, 49503), tuple(79959, 89436), tuple(86980, 62031), tuple(8275, 44009), tuple(36666, 94645), tuple(22064, 38882), tuple(40471, 16939), tuple(31156, 11337), tuple(13101, 96977), tuple(17906, 26835), tuple(89861, 51405), tuple(73369, 67946), tuple(99141, 58572), tuple(27131, 98703), tuple(15900, 43412), tuple(51768, 93125), tuple(78579, 46689), tuple(23029, 13895), tuple(60870, 55830), tuple(22553, 8236), tuple(76449, 96207), tuple(83766, 51024), tuple(27630, 50614), tuple(53484, 90104), tuple(77626, 21944), tuple(46755, 41583), tuple(53616, 34240), tuple(94159, 44415), tuple(13914, 90059), tuple(44387, 89012), tuple(27499, 64579), tuple(83415, 30809), tuple(77558, 82619), tuple(88880, 9814), tuple(8466, 4424), tuple(43598, 91921), tuple(24695, 3349), tuple(46295, 65208), tuple(51256, 82461), tuple(49126, 93012), tuple(16186, 96585), tuple(43284, 22655), tuple(93130, 90393), tuple(77495, 34372), tuple(85509, 65856), tuple(86662, 61906), tuple(50988, 44393), tuple(29828, 17737), tuple(91651, 35308), tuple(29796, 49716), tuple(14019, 87751), tuple(29688, 71207), tuple(82845, 19100), tuple(11989, 50132), tuple(21158, 99905), tuple(54732, 42547), tuple(32314, 12851), tuple(46405, 43794), tuple(87849, 45643), tuple(53524, 21212), tuple(61925, 75491), tuple(12498, 21937), tuple(30185, 69475), tuple(48421, 52487), tuple(15112, 90935), tuple(33187, 17801), tuple(61704, 25514), tuple(17889, 23917), tuple(18758, 57197), tuple(7693, 47232), tuple(47905, 24618), tuple(11494, 78950), tuple(95662, 54561), tuple(8075, 33909), tuple(90427, 46065), tuple(73962, 19821), tuple(50691, 79400), tuple(58218, 4881), tuple(94106, 2509), tuple(60633, 55169), tuple(49600, 83054), tuple(23339, 13270), tuple(70262, 58946), tuple(48417, 97266), tuple(27629, 46905), tuple(74465, 75514), tuple(41687, 2564), tuple(12814, 19492), tuple(78899, 30168), tuple(17745, 35206), tuple(37972, 35296), tuple(22288, 80001), tuple(68026, 36558), tuple(40187, 12234), tuple(92380, 22866), tuple(56488, 64402), tuple(41404, 62562), tuple(47802, 45287), tuple(83302, 85215), tuple(58999, 85776), tuple(35158, 16804), tuple(13416, 94146), tuple(62953, 28243), tuple(83290, 19103), tuple(4564, 21789), tuple(64468, 20927), tuple(25582, 47206), tuple(57810, 18693), tuple(28938, 97986), tuple(61704, 14838), tuple(19214, 3232), tuple(12911, 25438), tuple(85802, 28837), tuple(56506, 89458), tuple(66392, 47773), tuple(68190, 43841), tuple(43044, 52214), tuple(57886, 32830), tuple(15943, 59771), tuple(37081, 89294), tuple(4032, 32960), tuple(46931, 85790), tuple(69656, 72737), tuple(28217, 39872), tuple(86170, 42776), tuple(55116, 51495), tuple(90485, 45274), tuple(60773, 36788), tuple(2193, 2636), tuple(70222, 62086), tuple(75720, 70712), tuple(17549, 51460), tuple(23609, 31515), tuple(70254, 39825), tuple(63762, 11061), tuple(13107, 15394), tuple(45916, 72130), tuple(91558, 86662), tuple(99524, 69106), tuple(93073, 29881), tuple(31724, 3007), tuple(69051, 59452), tuple(59701, 86760), tuple(4967, 82028), tuple(57404, 48226), tuple(71829, 79910), tuple(23714, 62439), tuple(73881, 67618), tuple(63269, 40085), tuple(6164, 23415), tuple(48156, 93907), tuple(18627, 16570), tuple(6676, 22991), tuple(36916, 41488), tuple(99079, 13264), tuple(32533, 99243), tuple(55505, 63339), tuple(89564, 3290), tuple(24886, 34916), tuple(91310, 9343), tuple(49779, 12740), tuple(26320, 3406), tuple(57661, 5702), tuple(10765, 57881), tuple(5518, 47638), tuple(93148, 27438), tuple(73451, 24477), tuple(84075, 96822), tuple(58883, 58883), tuple(96812, 82388), tuple(30659, 59654), tuple(24498, 95808), tuple(25591, 21834), tuple(13090, 87704), tuple(76495, 17249), tuple(75975, 84318), tuple(55459, 70426), tuple(84256, 88604), tuple(79438, 43104), tuple(45331, 7495), tuple(63619, 11123), tuple(24772, 2601), tuple(63343, 14138), tuple(39957, 98339), tuple(55595, 17823), tuple(97676, 53933), tuple(91867, 25023), tuple(64677, 67859), tuple(43737, 34315), tuple(24800, 53968), tuple(93157, 17507), tuple(24264, 35273), tuple(33889, 507), tuple(10207, 40542), tuple(40213, 57800), tuple(38321, 74160), tuple(42391, 7651), tuple(80267, 94736), tuple(52473, 79634), tuple(17075, 2531), tuple(8595, 75890), tuple(31496, 50367), tuple(16069, 79896), tuple(70067, 200), tuple(23420, 49517), tuple(1628, 45646), tuple(8916, 36794), tuple(72294, 88976), tuple(40603, 86008), tuple(91871, 71098), tuple(5447, 70998), tuple(24152, 17561), tuple(65046, 34951), tuple(56950, 9292), tuple(19244, 31385), tuple(74693, 31813), tuple(97343, 21572), tuple(38834, 135), tuple(79717, 62486), tuple(38, 10308), tuple(58035, 71344), tuple(85802, 81079), tuple(5943, 156), tuple(38735, 38867), tuple(3803, 99366), tuple(15853, 19408), tuple(62988, 62008), tuple(8316, 44684), tuple(17035, 71012), tuple(48584, 2117), tuple(75425, 37336), tuple(2405, 50420), tuple(43653, 28836), tuple(12394, 69430), tuple(54522, 4954), tuple(33359, 148), tuple(41018, 82851), tuple(79995, 55417), tuple(65008, 32342), tuple(36547, 88185), tuple(8131, 7054), tuple(38980, 20146), tuple(27976, 63039), tuple(53119, 67009), tuple(40043, 98393), tuple(29333, 51980), tuple(85818, 98405), tuple(77956, 20099), tuple(99747, 16916), tuple(11597, 50181), tuple(40961, 8262), tuple(75103, 13912), tuple(62339, 69155), tuple(3869, 85481), tuple(7053, 30956), tuple(33563, 53272), tuple(96178, 81751), tuple(99365, 88728), tuple(34447, 11164), tuple(62856, 30939), tuple(92486, 3357), tuple(56605, 35330), tuple(42180, 15137), tuple(83946, 62984), tuple(61869, 55711), tuple(52880, 49871), tuple(44588, 27387), tuple(16332, 24496), tuple(1781, 13508), tuple(56674, 95773), tuple(21328, 19628), tuple(96455, 24155), tuple(14302, 74435), tuple(54053, 24590), tuple(86642, 22177), tuple(24089, 16186), tuple(70281, 4601), tuple(18552, 70708), tuple(95442, 5895), tuple(96714, 6293), tuple(43803, 45857), tuple(93257, 18497), tuple(90032, 85086), tuple(40566, 87233), tuple(32674, 73822), tuple(95599, 49334), tuple(62745, 51898), tuple(8245, 93882), tuple(14093, 40977), tuple(47215, 53001), tuple(59737, 68452), tuple(90937, 25354), tuple(43805, 82571), tuple(81953, 68572), tuple(37298, 96262), tuple(94899, 65066), tuple(34772, 80762), tuple(55469, 1186), tuple(8734, 91665), tuple(18622, 51150), tuple(85200, 39575), tuple(65381, 15979), tuple(89734, 89656), tuple(64712, 53691), tuple(87187, 58256), tuple(8476, 89694), tuple(49935, 35239), tuple(63730, 34982), tuple(27687, 91571), tuple(87543, 15350), tuple(85208, 18781), tuple(14783, 2574), tuple(44699, 666), tuple(56440, 87617), tuple(32732, 49301), tuple(76725, 3895), tuple(10419, 90580), tuple(34725, 69476), tuple(14831, 81588), tuple(93924, 38057), tuple(38528, 99060), tuple(57136, 44206), tuple(74685, 99559), tuple(43083, 87511), tuple(43105, 35474), tuple(35582, 17560), tuple(5578, 98727), tuple(78947, 53865), tuple(32013, 95029), tuple(61552, 42674), tuple(52191, 49975), tuple(71566, 16403), tuple(78534, 16350), tuple(18520, 80501), tuple(29114, 46547), tuple(11488, 5069), tuple(89591, 82384), tuple(13741, 42318), tuple(74385, 58849), tuple(49739, 63421), tuple(83821, 6676), tuple(51997, 93321), tuple(36677, 81768), tuple(37915, 73495), tuple(47175, 6086), tuple(39989, 83110), tuple(6489, 48112), tuple(88822, 20370), tuple(12846, 13952), tuple(28930, 20879), tuple(25139, 84552), tuple(76434, 2665), tuple(55145, 31523), tuple(21177, 18630), tuple(81077, 96275), tuple(61006, 30845), tuple(77722, 62651), tuple(61181, 72545), tuple(93838, 84287), tuple(59300, 19014), tuple(75076, 97980), tuple(76979, 1473), tuple(48409, 13097), tuple(51718, 5325), tuple(36522, 72119), tuple(60917, 18995), tuple(61469, 42853), tuple(34387, 37322), tuple(38684, 28120), tuple(64136, 8559), tuple(15368, 99424), tuple(97824, 7864), tuple(33833, 72029), tuple(7024, 9961), tuple(49400, 66220), tuple(63025, 97179), tuple(6135, 98878), tuple(19873, 8438), tuple(3963, 35670), tuple(65186, 89423), tuple(26653, 65943), tuple(83132, 67000), tuple(82578, 35007), tuple(42680, 60479), tuple(71102, 98589), tuple(74842, 94010), tuple(22931, 33725), tuple(46537, 42629), tuple(75793, 48115), tuple(21630, 92454), tuple(97993, 81332), tuple(25747, 31814), tuple(91231, 65953), tuple(91981, 12219), tuple(64719, 16254), tuple(60914, 8334), tuple(15887, 96432), tuple(42110, 28837), tuple(7295, 83147), tuple(50334, 7053), tuple(3949, 33594), tuple(1524, 98230), tuple(17265, 98024), tuple(75969, 36232), tuple(89538, 5212), tuple(13444, 55946), tuple(69823, 81848), tuple(32578, 74024), tuple(52018, 98290), tuple(59118, 40186), tuple(61002, 16977), tuple(69537, 44780), tuple(92, 13937), tuple(33715, 42663), tuple(46347, 8312), tuple(86196, 59301), tuple(17128, 85014), tuple(26429, 57682), tuple(45888, 99588), tuple(22750, 96110), tuple(46809, 49251), tuple(24521, 40071), tuple(287, 22115), - tuple(11741, 36315), tuple(22742, 17581), tuple(35808, 3110), tuple(98904, 30407), tuple(4584, 13383), tuple(28585, 69669), tuple(94823, 29715), tuple(9551, 36389), tuple(77997, 45746), tuple(49894, 55722), tuple(23415, 69459), tuple(58246, 85144), tuple(74136, 18102), tuple(97366, 85724), tuple(34271, 51601), tuple(47535, 70883), tuple(59443, 90103), tuple(45213, 45811), tuple(62741, 86898), tuple(17324, 50034), tuple(62080, 25193), tuple(89524, 4421), tuple(13476, 51456), tuple(69198, 56718), tuple(58024, 22969), tuple(65210, 67941), tuple(32561, 44881), tuple(62295, 67448), tuple(66135, 95453), tuple(9417, 20443), tuple(82486, 23745), tuple(19185, 99041), tuple(40662, 91714), tuple(3423, 58624), tuple(4512, 74502), tuple(67772, 98023), tuple(69575, 75779), tuple(69107, 62805), tuple(517, 33801), tuple(47406, 7581), tuple(81108, 10546), tuple(12976, 47001), tuple(16742, 83811), tuple(44593, 82124), tuple(52731, 34642), tuple(81725, 20555), tuple(94126, 91919), tuple(24800, 59302), tuple(97253, 39249), tuple(71692, 10769), tuple(88721, 56321), tuple(7019, 69771), tuple(31464, 61774), tuple(29597, 19263), tuple(65557, 31875), tuple(28653, 69636), tuple(58074, 76848), tuple(15906, 80620), tuple(18259, 40193), tuple(99991, 4769), tuple(98935, 99269), tuple(12123, 60124), tuple(20787, 47346), tuple(13526, 33592), tuple(95370, 40350), tuple(17479, 42884), tuple(58368, 83218), tuple(63290, 74406), tuple(97030, 35102), tuple(45298, 27660), tuple(64593, 21262), tuple(76268, 82641), tuple(1107, 44044), tuple(21427, 79959), tuple(85180, 62412), tuple(7359, 1318), tuple(83618, 9762), tuple(1425, 55804), tuple(32874, 97943), tuple(68191, 38742), tuple(41715, 17902), tuple(3771, 15032), tuple(7848, 74950), tuple(33881, 40904), tuple(75295, 26151), tuple(75775, 13760), tuple(90262, 89822), tuple(88169, 18679), tuple(57506, 32356), tuple(94983, 44281), tuple(37385, 37432), tuple(18248, 48162), tuple(45573, 66278), tuple(25277, 72788), tuple(26977, 36778), tuple(26254, 61758), tuple(12860, 48026), tuple(96819, 3339), tuple(13134, 1173), tuple(26822, 53374), tuple(15989, 29698), tuple(11258, 54515), tuple(37866, 34928), tuple(22996, 26577), tuple(39952, 42732), tuple(6754, 70595), tuple(86245, 44669), tuple(47044, 34170), tuple(6789, 45220), tuple(31706, 2090), tuple(42582, 40023), tuple(35147, 46591), tuple(88210, 11307), tuple(53644, 7680), tuple(11280, 91075), tuple(42961, 65122), tuple(40066, 52185), tuple(20050, 6154), tuple(98440, 20393), tuple(88992, 75432), tuple(32386, 66731), tuple(36952, 34149), tuple(18453, 32715), tuple(84413, 10378), tuple(59440, 2374), tuple(45354, 85009), tuple(50382, 66510), tuple(64428, 95401), tuple(9336, 41760), tuple(26317, 91416), tuple(81941, 99504), tuple(26600, 53522), tuple(81069, 40236), tuple(51126, 27911), tuple(97144, 14243), tuple(62738, 50287), tuple(37372, 28962), tuple(12053, 9090), tuple(69492, 95524), tuple(68141, 52931), tuple(17276, 16487), tuple(69227, 25949), tuple(14143, 70193), tuple(7077, 53032), tuple(65463, 74082), tuple(94997, 66496), tuple(80443, 55832), tuple(66796, 5970), tuple(15852, 95662), tuple(81559, 97272), tuple(55851, 18977), tuple(91142, 48976), tuple(91143, 950), tuple(79225, 31004), tuple(61310, 20760), tuple(74541, 90842), tuple(80322, 11630), tuple(84631, 544), tuple(66785, 86591), tuple(25650, 63252), tuple(59635, 18586), tuple(2964, 6741), tuple(37091, 71148), tuple(11984, 43077), tuple(87505, 62049), tuple(61925, 92290), tuple(18808, 3937), tuple(8300, 33268), tuple(70850, 50661), tuple(86024, 73730), tuple(85161, 47116), tuple(50193, 89155), tuple(37773, 40845), tuple(9251, 41688), tuple(6940, 65399), tuple(42479, 95630), tuple(19401, 43102), tuple(48069, 36040), tuple(62760, 95013), tuple(394, 2641), tuple(32567, 29306), tuple(13870, 58835), tuple(98248, 47291), tuple(49803, 4523), tuple(40222, 12883), tuple(53576, 73105), tuple(88265, 23629), tuple(67865, 67875), tuple(33473, 27144), tuple(80219, 53893), tuple(74878, 47341), tuple(78070, 84803), tuple(30003, 5600), tuple(41103, 6145), tuple(83490, 81076), tuple(55059, 66736), tuple(45015, 10239), tuple(79555, 85819), tuple(81808, 34970), tuple(19235, 85480), tuple(91807, 52177), tuple(40887, 87009), tuple(5003, 2687), tuple(64964, 88122), tuple(765, 94893), tuple(93573, 20504), tuple(28854, 38438), tuple(94244, 93475), tuple(72996, 84801), tuple(75427, 81692), tuple(63161, 98637), tuple(18814, 61343), tuple(22863, 60110), tuple(8949, 12694), tuple(19675, 94313), tuple(43857, 74073), tuple(15737, 58218), tuple(48895, 68474), tuple(22220, 92926), tuple(69055, 50282), tuple(40532, 74934), tuple(59062, 66405), tuple(85784, 87704), tuple(58494, 88222), tuple(2260, 20401), tuple(73112, 99666), tuple(46739, 95433), tuple(21179, 85119), tuple(11545, 38801), tuple(59993, 50866), tuple(10086, 4709), tuple(70560, 29611), tuple(27095, 89017), tuple(6896, 2279), tuple(92506, 5013), tuple(48600, 90491), tuple(18782, 54638), tuple(54337, 82734), tuple(52054, 13481), tuple(38297, 56559), tuple(15998, 30591), tuple(89789, 7522), tuple(18149, 28725), tuple(3532, 28625), tuple(70934, 49617), tuple(84599, 55664), tuple(74229, 52269), tuple(55431, 11893), tuple(32807, 72543), tuple(83882, 53025), tuple(11490, 83442), tuple(14844, 88612), tuple(12526, 45953), tuple(906, 2231), tuple(68240, 95612), tuple(18818, 31535), tuple(57774, 91290), tuple(67250, 67400), tuple(77332, 23550), tuple(42332, 57775), tuple(28792, 11539), tuple(19108, 34608), tuple(12399, 38591), tuple(7329, 10740), tuple(84288, 50928), tuple(29461, 17629), tuple(63884, 88489), tuple(47479, 61085), tuple(75357, 57255), tuple(60107, 94046), tuple(32934, 66312), tuple(28615, 42600), tuple(55553, 85213), tuple(57838, 91426), tuple(9783, 11513), tuple(73677, 28821), tuple(75408, 75561), tuple(22995, 59224), tuple(74874, 54145), tuple(18513, 75901), tuple(46440, 69414), tuple(36072, 22263), tuple(60560, 73325), tuple(69967, 93358), tuple(75949, 98634), tuple(3688, 57991), tuple(43482, 94541), tuple(40922, 31011), tuple(57763, 74497), tuple(93576, 96392), tuple(83038, 80656), tuple(47757, 87045), tuple(14061, 53465), tuple(65619, 33775), tuple(11341, 6702), tuple(6249, 87358), tuple(15766, 85937), tuple(13135, 93945), tuple(24495, 95900), tuple(80359, 1739), tuple(15468, 73426), tuple(49240, 44999), tuple(82839, 90808), tuple(87438, 75613), tuple(348, 73144), tuple(99523, 85853), tuple(21557, 70210), tuple(64933, 1672), tuple(38154, 17477), tuple(97136, 67363), tuple(96491, 8038), tuple(97981, 3434), tuple(54372, 27038), tuple(88480, 86675), tuple(21028, 21083), tuple(43197, 4440), tuple(31702, 78290), tuple(66631, 24438), tuple(11482, 17922), tuple(90351, 39503), tuple(46186, 32439), tuple(73828, 6640), tuple(56916, 26029), tuple(62840, 1815), tuple(20281, 28488), tuple(18211, 30043), tuple(65211, 93012), tuple(43614, 58012), tuple(90322, 77343), tuple(64293, 94525), tuple(59489, 39760), tuple(93219, 78440), tuple(74613, 9732), tuple(38085, 19191), tuple(58029, 48186), tuple(88762, 1764), tuple(28627, 21993), tuple(49975, 41225), tuple(70486, 43480), tuple(82764, 96425), tuple(27218, 78327), tuple(17844, 73333), tuple(70463, 37629), tuple(10500, 33826), tuple(97343, 66575), tuple(82833, 51210), tuple(77353, 45073), tuple(27163, 39728), tuple(78076, 46691), tuple(80302, 39342), tuple(77142, 1319), tuple(87403, 80110), tuple(53805, 27786), tuple(50558, 74264), tuple(83146, 31358), tuple(11567, 4438), tuple(30041, 54287), tuple(91731, 18496), tuple(57591, 93894), tuple(72534, 59009), tuple(98064, 59148), tuple(69626, 66615), tuple(20951, 43949), tuple(61960, 68060), tuple(48892, 67918), tuple(61321, 56222), tuple(75424, 77260), tuple(4916, 81929), tuple(68892, 81531), tuple(28096, 28548), tuple(62016, 107), tuple(8593, 12030), tuple(66743, 36772), tuple(60174, 15106), tuple(52844, 1923), tuple(34768, 22065), tuple(88988, 62910), tuple(79214, 2998), tuple(25675, 31376), tuple(69959, 3614), tuple(43885, 31708), tuple(12206, 46548), tuple(69924, 19343), tuple(12984, 38980), tuple(58250, 69438), tuple(2580, 48684), tuple(38112, 37124), tuple(21842, 43150), tuple(59384, 21921), tuple(19908, 46678), tuple(73396, 79529), tuple(8274, 1557), tuple(36975, 65519), tuple(81069, 18712), tuple(13692, 9148), tuple(60617, 84762), tuple(75749, 66154), tuple(80375, 24553), tuple(4257, 47056), tuple(76880, 7687), tuple(40714, 43448), tuple(79112, 74791), tuple(33119, 72730), tuple(17670, 89183), tuple(51614, 3921), tuple(21247, 39857), tuple(86756, 67673), tuple(32792, 70035), tuple(5917, 7197), tuple(1762, 23130), tuple(6455, 63664), tuple(32806, 3729), tuple(60469, 20511), tuple(12522, 15149), tuple(98106, 79338), tuple(84754, 11162), tuple(52058, 17973), tuple(28789, 1521), tuple(32766, 36325), tuple(78914, 40453), tuple(70297, 71854), tuple(9313, 45190), tuple(54559, 66227), tuple(22342, 43860), tuple(44152, 84294), tuple(36913, 93173), tuple(88523, 36338), tuple(82234, 71140), tuple(8328, 22947), tuple(73250, 88125), tuple(74356, 16820), tuple(94472, 37349), tuple(23126, 87806), tuple(40315, 88729), tuple(19935, 19145), tuple(93312, 65719), tuple(8477, 33108), tuple(86660, 69525), tuple(75557, 66964), tuple(60437, 57494), tuple(94419, 42524), tuple(95372, 72274), tuple(49866, 85685), tuple(96808, 39404), tuple(62961, 72507), tuple(38634, 70815), tuple(91379, 42430), tuple(66359, 98699), tuple(24382, 4186), tuple(4003, 77760), tuple(87840, 75265), tuple(57641, 68871), tuple(9773, 15942), tuple(5664, 51289), tuple(47923, 31308), tuple(58632, 82468), tuple(14097, 71829), tuple(1838, 97710), tuple(70433, 11364), tuple(82363, 97879), tuple(25257, 20615), tuple(18249, 6758), tuple(98581, 13639), tuple(3290, 72449), tuple(74546, 79380), tuple(97254, 44448), tuple(80316, 31760), tuple(40516, 94809), tuple(14444, 88981), tuple(9693, 10259), tuple(83795, 95485), tuple(70201, 81014), tuple(66644, 16761), tuple(35529, 82718), tuple(75774, 73476), tuple(80139, 3957), tuple(34803, 80689), tuple(92085, 46499), tuple(97871, 8004), tuple(67369, 11354), tuple(43578, 81596), - tuple(94695, 44963), tuple(93741, 41629), tuple(16005, 96652), tuple(37918, 69012), tuple(89832, 56041), tuple(51798, 32386), tuple(89749, 27647), tuple(76279, 7990), tuple(31746, 1346), tuple(40841, 20480), tuple(99942, 24473), tuple(78495, 99194), tuple(13588, 57088), tuple(22183, 42297), tuple(82707, 34435), tuple(45026, 12747), tuple(8000, 93211), tuple(40453, 13025), tuple(44100, 39880), tuple(83900, 56474), tuple(87691, 42802), tuple(82000, 63867), tuple(76627, 84731), tuple(112, 92774), tuple(34749, 97737), tuple(59262, 57169), tuple(95571, 44144), tuple(74310, 68970), tuple(63232, 92744), tuple(53698, 21610), tuple(39969, 75475), tuple(39942, 28713), tuple(81230, 50140), tuple(97953, 96528), tuple(86144, 48041), tuple(96677, 49677), tuple(22051, 48183), tuple(33436, 54784), tuple(5553, 11920), tuple(67057, 17115), tuple(57736, 72309), tuple(8086, 85329), tuple(72623, 94949), tuple(13096, 48992), tuple(63153, 56337), tuple(30462, 1036), tuple(75000, 24048), tuple(62635, 50128), tuple(91480, 83131), tuple(25929, 79809), tuple(96237, 76974), tuple(59645, 20603), tuple(31850, 29754), tuple(91070, 36568), tuple(30191, 33785), tuple(86776, 67259), tuple(49073, 39179), tuple(16121, 73834), tuple(84217, 52951), tuple(95866, 47244), tuple(63326, 73460), tuple(134, 91953), tuple(48189, 86069), tuple(42971, 3700), tuple(28643, 10479), tuple(80151, 7446), tuple(78798, 2655), tuple(39135, 69364), tuple(80244, 24904), tuple(22818, 74964), tuple(26753, 82419), tuple(16858, 5212), tuple(79891, 11215), tuple(785, 46103), tuple(12559, 24617), tuple(73601, 71490), tuple(70342, 7099), tuple(73330, 6665), tuple(11903, 28194), tuple(16375, 37746), tuple(86132, 51788), tuple(90345, 68366), tuple(5464, 78338), tuple(23569, 83141), tuple(17904, 94046), tuple(35868, 60017), tuple(22591, 93373), tuple(70584, 72116), tuple(49331, 34312), tuple(16180, 91286), tuple(58494, 65441), tuple(9336, 52671), tuple(32523, 26734), tuple(40205, 83549), tuple(28810, 96876), tuple(44703, 38944), tuple(46981, 37157), tuple(8582, 7529), tuple(59718, 71700), tuple(62545, 73716), tuple(6531, 23200), tuple(30528, 59720), tuple(57152, 84660), tuple(16232, 67946), tuple(60446, 45983), tuple(68737, 54959), tuple(57795, 73107), tuple(26930, 35938), tuple(9844, 44760), tuple(3716, 79020), tuple(99126, 8264), tuple(66120, 16151), tuple(50616, 25765), tuple(93340, 95875), tuple(34103, 88003), tuple(14879, 99758), tuple(49188, 6087), tuple(89858, 42861), tuple(36730, 72076), tuple(25069, 26403), tuple(98183, 48108), tuple(3229, 5367), tuple(59306, 80078), tuple(61144, 58598), tuple(72600, 98765), tuple(57701, 23177), tuple(10176, 11553), tuple(82964, 13697), tuple(7788, 28538), tuple(39943, 97491), tuple(56261, 17781), tuple(2458, 1892), tuple(6679, 45554), tuple(42171, 66222), tuple(24420, 44115), tuple(35852, 41965), tuple(50196, 49555), tuple(34718, 60734), tuple(6932, 61638), tuple(69472, 56723), tuple(489, 97620), tuple(41335, 90578), tuple(1333, 92787), tuple(97883, 64754), tuple(14208, 22097), tuple(75776, 5938), tuple(67446, 61518), tuple(58743, 45162), tuple(34749, 81243), tuple(71451, 91991), tuple(27804, 41836), tuple(45274, 8039), tuple(17593, 24498), tuple(8801, 38559), tuple(87460, 7109), tuple(50075, 18284), tuple(84043, 82146), tuple(62932, 25018), tuple(89647, 56768), tuple(59920, 80801), tuple(56357, 35142), tuple(97376, 58181), tuple(70715, 91103), tuple(90829, 78985), tuple(29776, 13275), tuple(30546, 42320), tuple(99266, 35340), tuple(21234, 61062), tuple(39239, 10745), tuple(45990, 715), tuple(47047, 6619), tuple(4270, 94575), tuple(90009, 72203), tuple(25629, 2691), tuple(67926, 89112), tuple(46990, 61101), tuple(22355, 69536), tuple(1977, 56723), tuple(54681, 34041), tuple(83819, 7024), tuple(81235, 7093), tuple(16659, 87135), tuple(49384, 32135), tuple(42204, 17362), tuple(90585, 70374), tuple(51255, 1), tuple(31600, 70085), tuple(90189, 95778), tuple(57349, 87789), tuple(83384, 93771), tuple(20718, 15529), tuple(10644, 53591), tuple(84103, 62101), tuple(91340, 48382), tuple(82854, 84420), tuple(12561, 53517), tuple(64835, 45362), tuple(54154, 75841), tuple(46498, 31175), tuple(75035, 49552), tuple(9306, 53403), tuple(68851, 49139), tuple(13463, 42107), tuple(2322, 36695), tuple(55953, 12098), tuple(60656, 80482), tuple(78987, 19632), tuple(31228, 18523), tuple(98972, 80489), tuple(32367, 98405), tuple(25139, 5164), tuple(5692, 60610), tuple(36535, 70097), tuple(80542, 74320), tuple(87984, 46750), tuple(98201, 41341), tuple(35217, 46813), tuple(81795, 69057), tuple(83927, 41032), tuple(60149, 26087), tuple(39954, 48361), tuple(64485, 61448), tuple(87185, 14580), tuple(74559, 93251), tuple(88544, 83366), tuple(74015, 15864), tuple(78623, 69719), tuple(16941, 80710), tuple(16315, 58313), tuple(47277, 59107), tuple(16067, 66290), tuple(63906, 59891), tuple(20754, 67817), tuple(44428, 10652), tuple(95960, 99045), tuple(52163, 26221), tuple(65566, 22057), tuple(26836, 38898), tuple(57107, 79274), tuple(39020, 74857), tuple(53540, 84159), tuple(76646, 44324), tuple(27967, 40171), tuple(28710, 56332), tuple(84036, 28711), tuple(68742, 57241), tuple(40535, 34737), tuple(62681, 85386), tuple(30472, 58405), tuple(85086, 33013), tuple(67059, 47481), tuple(30441, 55098), tuple(97892, 71991), tuple(90296, 42905), tuple(22441, 18863), tuple(19606, 77242), tuple(11206, 58380), tuple(23901, 49962), tuple(84094, 33761), tuple(64400, 28093), tuple(64228, 94543), tuple(71874, 20871), tuple(25385, 73117), tuple(63398, 20999), tuple(77547, 51893), tuple(80783, 65858), tuple(39807, 80754), tuple(10336, 90318), tuple(7826, 55346), tuple(30206, 10711), tuple(94411, 67364), tuple(33509, 14329), tuple(65350, 17006), tuple(65999, 55699), tuple(82753, 61081), tuple(38851, 11896), tuple(15155, 48635), tuple(19985, 75204), tuple(37144, 5344), tuple(26173, 39587), tuple(61111, 30966), tuple(16180, 22987), tuple(60707, 43599), tuple(30136, 74118), tuple(7880, 43857), tuple(97445, 30233), tuple(62700, 24828), tuple(90914, 89452), tuple(64131, 56925), tuple(25259, 39132), tuple(47104, 43950), tuple(93891, 21913), tuple(84573, 91029), tuple(8604, 79858), tuple(33141, 25534), tuple(12468, 90413), tuple(97063, 76359), tuple(80826, 26061), tuple(64013, 99099), tuple(82158, 38882), tuple(25799, 7564), tuple(25477, 69847), tuple(73374, 58520), tuple(48230, 9453), tuple(91424, 72273), tuple(64893, 11750), tuple(46753, 48434), tuple(15974, 94633), tuple(14872, 27027), tuple(14527, 21313), tuple(25660, 64644), tuple(54196, 15138), tuple(6313, 10911), tuple(36168, 47170), tuple(45346, 76), tuple(10305, 60286), tuple(65283, 39977), tuple(21804, 37972), tuple(65389, 86954), tuple(90674, 64458), tuple(15838, 22392), tuple(43540, 42503), tuple(49584, 67828), tuple(56711, 87887), tuple(40075, 73696), tuple(23832, 91552), tuple(39002, 65562), tuple(20451, 64664), tuple(70783, 92171), tuple(29319, 57694), tuple(56217, 44247), tuple(52856, 57873), tuple(80560, 90902), tuple(31068, 11280), tuple(46996, 34739), tuple(57527, 4554), tuple(8410, 25816), tuple(12269, 38319), tuple(88054, 49939), tuple(337, 13231), tuple(56432, 68236), tuple(74841, 21476), tuple(96006, 15712), tuple(87145, 91660), tuple(58090, 55111), tuple(10310, 79789), tuple(5734, 79710), tuple(98992, 69026), tuple(77033, 5734), tuple(43338, 42635), tuple(23898, 28669), tuple(62708, 81652), tuple(41279, 51722), tuple(93444, 26355), tuple(62046, 52199), tuple(71492, 58736), tuple(7379, 62581), tuple(8592, 71885), tuple(75026, 40387), tuple(46696, 3939), tuple(9787, 88907), tuple(86356, 363), tuple(97479, 20358), tuple(77363, 65553), tuple(44036, 22178), tuple(98279, 64612), tuple(3615, 411), tuple(77003, 93018), tuple(41605, 88489), tuple(55992, 83614), tuple(19493, 21633), tuple(34639, 97064), tuple(94602, 89289), tuple(45853, 26299), tuple(84170, 73386), tuple(9221, 51439), tuple(41513, 68166), tuple(37170, 17690), tuple(82511, 59246), tuple(96674, 27574), tuple(99301, 45675), tuple(42716, 41520), tuple(56623, 49130), tuple(84100, 76804), tuple(73855, 97007), tuple(73303, 26912), tuple(37151, 23837), tuple(49190, 97104), tuple(23487, 45628), tuple(87763, 46550), tuple(65111, 92605), tuple(80481, 8151), tuple(83949, 18930), tuple(81749, 27244), tuple(37449, 3023), tuple(28303, 51545), tuple(96441, 93242), tuple(22082, 43254), tuple(35135, 68407), tuple(37712, 48709), tuple(5111, 26774), tuple(15532, 74246), tuple(93605, 83583), tuple(21491, 66472), tuple(38922, 53076), tuple(55455, 54432), tuple(955, 44063), tuple(311, 91630), tuple(53554, 4522), tuple(29927, 65668), tuple(7525, 16035), tuple(44093, 76745), tuple(21481, 78198), tuple(76875, 5306), tuple(56126, 76437), tuple(96534, 16880), tuple(85600, 68336), tuple(4479, 81002), tuple(80414, 11593), tuple(8186, 61147), tuple(5624, 32879), tuple(79312, 20995), tuple(40407, 41512), tuple(91261, 66022), tuple(93228, 75364), tuple(21136, 40111), tuple(92148, 60681), tuple(42549, 7944), tuple(60157, 15040), tuple(63562, 88365), tuple(69056, 72713), tuple(78263, 89223), tuple(3776, 33039), tuple(30042, 59984), tuple(64567, 20977), tuple(24720, 39157), tuple(63582, 75653), tuple(45363, 20249), tuple(58093, 53833), tuple(27918, 93306), tuple(25791, 92686), tuple(15904, 862), tuple(72093, 19257), tuple(64125, 88986), tuple(41717, 27989), tuple(43165, 98675), tuple(76840, 48170), tuple(64508, 3535), tuple(91964, 33435), tuple(96686, 88673), tuple(66648, 64594), tuple(17927, 30539), tuple(73615, 22800), tuple(18580, 48077), tuple(59803, 48202), - tuple(76805, 89886), tuple(2744, 52965), tuple(55596, 22519), tuple(35358, 11629), tuple(83029, 80047), tuple(36120, 91930), tuple(26066, 23035), tuple(48857, 14268), tuple(63020, 26197), tuple(60623, 23252), tuple(34911, 72754), tuple(34808, 21593), tuple(64067, 58963), tuple(34509, 8739), tuple(52686, 96405), tuple(98282, 10463), tuple(6495, 64680), tuple(59016, 86968), tuple(33928, 51222), tuple(39609, 84992), tuple(67603, 89875), tuple(14723, 16144), tuple(30751, 46856), tuple(76874, 75024), tuple(89584, 58806), tuple(51278, 4113), tuple(27187, 93483), tuple(80039, 52159), tuple(6132, 25127), tuple(42358, 77498), tuple(33838, 79064), tuple(74147, 76851), tuple(39752, 27366), tuple(44888, 9809), tuple(10887, 4135), tuple(22303, 36417), tuple(58690, 34613), tuple(53998, 74014), tuple(71567, 32438), tuple(65110, 93406), tuple(77365, 41299), tuple(18044, 70636), tuple(77346, 21236), tuple(78408, 245), tuple(57704, 34662), tuple(75258, 64730), tuple(96992, 15533), tuple(56010, 60769), tuple(69163, 4826), tuple(88709, 20725), tuple(33197, 69743), tuple(97169, 83194), tuple(75277, 53343), tuple(14531, 64740), tuple(19997, 4752), tuple(74016, 55946), tuple(55290, 63626), tuple(32533, 32920), tuple(32946, 74610), tuple(12386, 33853), tuple(34825, 35374), tuple(28772, 32716), tuple(17280, 42683), tuple(54184, 34332), tuple(29964, 16203), tuple(65767, 61448), tuple(29133, 35728), tuple(6861, 14160), tuple(65483, 40224), tuple(78335, 76002), tuple(3061, 40615), tuple(11780, 87517), tuple(46135, 73448), tuple(10920, 72592), tuple(15696, 28810), tuple(44154, 64134), tuple(59365, 27248), tuple(76601, 39862), tuple(68264, 30019), tuple(48572, 54575), tuple(59499, 85796), tuple(35064, 23789), tuple(57028, 83545), tuple(33911, 8463), tuple(21827, 67966), tuple(15983, 69649), tuple(13919, 20584), tuple(82742, 67956), tuple(75457, 45767), tuple(55394, 62309), tuple(6099, 67510), tuple(58078, 9594), tuple(24511, 83149), tuple(24781, 79624), tuple(39745, 777), tuple(92023, 40085), tuple(22889, 37179), tuple(17919, 28607), tuple(79865, 72682), tuple(99829, 38190), tuple(21273, 21278), tuple(88299, 23433), tuple(88887, 48163), tuple(62993, 61567), tuple(82107, 84224), tuple(65049, 61245), tuple(75113, 93564), tuple(81562, 7874), tuple(32314, 32313), tuple(3979, 46996), tuple(40558, 93278), tuple(58758, 68163), tuple(40502, 58941), tuple(76961, 65762), tuple(48032, 36117), tuple(64712, 9137), tuple(12092, 56665), tuple(12315, 66581), tuple(20954, 29083), tuple(57317, 48290), tuple(23534, 86828), tuple(4869, 35950), tuple(26993, 24840), tuple(93007, 45049), tuple(18009, 20350), tuple(43053, 71248), tuple(47320, 66119), tuple(50898, 96627), tuple(669, 40018), tuple(89236, 44039), tuple(47375, 63306), tuple(61906, 6658), tuple(2672, 84546), tuple(59778, 72319), tuple(14497, 71952), tuple(42420, 87023), tuple(96465, 46140), tuple(32857, 22772), tuple(4985, 35125), tuple(61918, 28016), tuple(90275, 24406), tuple(49799, 10811), tuple(74137, 63345), tuple(26135, 86306), tuple(92971, 65541), tuple(40134, 95892), tuple(38554, 46307), tuple(48113, 16343), tuple(63990, 66283), tuple(17793, 49570), tuple(21736, 79819), tuple(13831, 27523), tuple(8939, 93929), tuple(96577, 4909), tuple(38583, 32781), tuple(13701, 24436), tuple(43444, 56054), tuple(17166, 32346), tuple(57202, 26264), tuple(82858, 75049), tuple(46317, 95666), tuple(54911, 68161), tuple(3894, 38521), tuple(26456, 30270), tuple(65214, 35331), tuple(41143, 13109), tuple(85441, 48899), tuple(93226, 25027), tuple(77045, 81171), tuple(30345, 79232), tuple(71167, 40854), tuple(58761, 56824), tuple(89047, 85314), tuple(31686, 81947), tuple(74946, 60661), tuple(49903, 13625), tuple(76341, 69067), tuple(46963, 88891), tuple(97223, 5921), tuple(52143, 9828), tuple(17413, 42731), tuple(30236, 93426), tuple(14540, 17652), tuple(52251, 97233), tuple(41581, 30097), tuple(28771, 46426), tuple(36260, 45179), tuple(4068, 16410), tuple(3146, 95055), tuple(5993, 88855), tuple(46103, 30022), tuple(26667, 18756), tuple(54576, 13438), tuple(12800, 11258), tuple(80761, 44979), tuple(59811, 76627), tuple(77917, 87270), tuple(46286, 28657), tuple(30609, 86852), tuple(15200, 28936), tuple(86331, 34195), tuple(98461, 55054), tuple(91760, 62792), tuple(91551, 70192), tuple(96030, 78205), tuple(8254, 27057), tuple(600, 37830), tuple(58635, 65506), tuple(81661, 73708), tuple(11225, 24255), tuple(15830, 9029), tuple(84384, 46190), tuple(31344, 25765), tuple(25670, 30716), tuple(88507, 19484), tuple(28207, 45941), tuple(91874, 15786), tuple(10094, 10934), tuple(38013, 2179), tuple(14558, 36415), tuple(65079, 48850), tuple(65486, 85046), tuple(54958, 60275), tuple(99800, 96623), tuple(68895, 99829), tuple(3708, 75830), tuple(96368, 22631), tuple(99411, 50094), tuple(56888, 3883), tuple(87288, 45604), tuple(64512, 84543), tuple(45565, 14170), tuple(77114, 15132), tuple(31800, 70333), tuple(57775, 40548), tuple(31788, 67511), tuple(51929, 13684), tuple(53736, 81543), tuple(84251, 86303), tuple(63823, 83258), tuple(77539, 61381), tuple(43570, 39418), tuple(79859, 34773), tuple(8595, 64524), tuple(97242, 9283), tuple(15530, 84591), tuple(75535, 65546), tuple(16516, 50162), tuple(58815, 1815), tuple(34897, 82920), tuple(66215, 81262), tuple(81487, 4902), tuple(64039, 25703), tuple(78006, 90468), tuple(3081, 26910), tuple(58159, 4777), tuple(73715, 36375), tuple(69189, 60971), tuple(18169, 39587), tuple(57960, 57668), tuple(6582, 63707), tuple(11155, 47930), tuple(70829, 92266), tuple(6294, 92305), tuple(2188, 6419), tuple(17141, 54972), tuple(60240, 35276), tuple(10788, 29414), tuple(17464, 76377), tuple(3994, 17227), tuple(12039, 24992), tuple(1340, 77467), tuple(1212, 41758), tuple(52186, 80763), tuple(970, 78819), tuple(92897, 68714), tuple(6349, 77016), tuple(22069, 77732), tuple(78209, 72708), tuple(71986, 56770), tuple(8580, 87225), tuple(97505, 63546), tuple(67459, 39771), tuple(50707, 57066), tuple(68226, 54176), tuple(65425, 27407), tuple(57723, 19288), tuple(56974, 90449), tuple(55878, 1264), tuple(46939, 79863), tuple(34868, 4652), tuple(39872, 78482), tuple(92657, 20961), tuple(99690, 28825), tuple(33761, 52922), tuple(73738, 64995), tuple(92092, 3237), tuple(2463, 45045), tuple(43984, 69864), tuple(60146, 5333), tuple(58127, 79082), tuple(84395, 73949), tuple(50818, 68457), tuple(48585, 47420), tuple(60878, 67337), tuple(16573, 30621), tuple(46524, 14168), tuple(87995, 44854), tuple(73143, 77177), tuple(33967, 37276), tuple(95038, 17670), tuple(69022, 16038), tuple(58485, 90526), tuple(1705, 1443), tuple(97969, 40011), tuple(14719, 42770), tuple(8695, 27192), tuple(47546, 51349), tuple(75263, 24419), tuple(25420, 66286), tuple(39198, 41401), tuple(77896, 85583), tuple(28265, 76766), tuple(88836, 48759), tuple(47768, 39582), tuple(65103, 3167), tuple(92171, 85360), tuple(1549, 79296), tuple(71725, 16645), tuple(87349, 29290), tuple(66201, 61712), tuple(43525, 70338), tuple(99025, 63090), tuple(3687, 79963), tuple(63600, 92088), tuple(2480, 1359), tuple(31384, 63603), tuple(29650, 24391), tuple(8552, 82260), tuple(16729, 29139), tuple(26503, 4767), tuple(88945, 19824), tuple(66695, 95696), tuple(84016, 35417), tuple(71521, 22206), tuple(88433, 55606), tuple(66380, 81316), tuple(30573, 36000), tuple(85223, 20494), tuple(99672, 82813), tuple(65500, 78258), tuple(55817, 98414), tuple(43248, 53800), tuple(62787, 21018), tuple(48981, 36258), tuple(41216, 98585), tuple(18576, 18004), tuple(27272, 72860), tuple(76774, 87664), tuple(26737, 11514), tuple(24472, 42538), tuple(5860, 81355), tuple(29066, 10012), tuple(75308, 28561), tuple(23609, 10007), tuple(10007, 19146), tuple(15568, 1487), tuple(80743, 85294), tuple(11207, 90623), tuple(61258, 63879), tuple(34363, 59005), tuple(74884, 2528), tuple(26604, 52738), tuple(33304, 1202), tuple(20381, 18984), tuple(81968, 92425), tuple(4407, 84677), tuple(2112, 79756), tuple(46970, 4367), tuple(36854, 23482), tuple(88346, 75107), tuple(10643, 31806), tuple(21351, 5590), tuple(69317, 53292), tuple(76711, 10085), tuple(70333, 90592), tuple(88818, 822), tuple(23927, 48141), tuple(84710, 33870), tuple(96932, 22686), tuple(5783, 87468), tuple(7785, 11585), tuple(49497, 33764), tuple(13506, 55969), tuple(37840, 78455), tuple(21532, 22292), tuple(97306, 42065), tuple(6579, 40749), tuple(2593, 4995), tuple(81985, 23611), tuple(63888, 98317), tuple(44975, 83777), tuple(57688, 42688), tuple(641, 45787), tuple(7316, 1967), tuple(43837, 18274), tuple(89994, 32770), tuple(4285, 50388), tuple(84699, 41841), tuple(19564, 20683), tuple(76027, 62278), tuple(26140, 11288), tuple(39656, 79954), tuple(16718, 17335), tuple(11583, 21283), tuple(55441, 32178), tuple(6810, 87225), tuple(27191, 54323), tuple(53406, 31512), tuple(48003, 80077), tuple(78497, 29570), tuple(39140, 66619), tuple(12651, 44576), tuple(1761, 88410), tuple(47139, 20766), tuple(39183, 69367), tuple(80479, 23285), tuple(1568, 78535), tuple(18476, 35058), tuple(93551, 81063), tuple(12059, 60021), tuple(23356, 26572), tuple(79975, 35434), tuple(82230, 67436), tuple(20243, 92343), tuple(47809, 10634), tuple(69537, 60167), tuple(3873, 77404), tuple(1136, 27956), tuple(17470, 24156), tuple(35849, 19150), tuple(74760, 37961), tuple(36660, 44448), tuple(36009, 96619), tuple(87110, 84921), tuple(16080, 60637), tuple(36046, 17351), tuple(96403, 99978), tuple(11060, 68629), tuple(36081, 23464), tuple(4684, 11817), tuple(50126, 82936), tuple(55262, 54135), tuple(53717, 66293), tuple(58028, 28065), tuple(92791, 99766), tuple(46266, 77711), tuple(61912, 65782), tuple(38677, 41158), tuple(4001, 46340), tuple(70987, 12784), tuple(14819, 42857), tuple(78985, 99956), tuple(79737, 42497), tuple(55305, 7329), tuple(64103, 24170), tuple(49093, 22115), tuple(2465, 97282), tuple(29009, 15663), tuple(80976, 86477), tuple(16439, 56685), tuple(53482, 15293), tuple(5038, 5991), tuple(67060, 84201), tuple(54350, 38095), tuple(67539, 68292), tuple(26464, 64908), tuple(92909, 12867), tuple(83517, 26474), tuple(76081, 85247), tuple(23250, 66616), - tuple(20783, 34330), tuple(43074, 10165), tuple(93968, 70375), tuple(83802, 70820), tuple(19871, 63094), tuple(35699, 36506), tuple(23905, 2401), tuple(27847, 31968), tuple(76714, 44112), tuple(62599, 32720), tuple(10362, 81985), tuple(35708, 2090), tuple(13071, 39035), tuple(71851, 59493), tuple(62833, 48082), tuple(77164, 22804), tuple(6469, 43229), tuple(92051, 3719), tuple(51910, 77689), tuple(91470, 63253), tuple(57914, 57836), tuple(98819, 97813), tuple(35975, 488), tuple(51431, 34061), tuple(45414, 85971), tuple(56563, 93517), tuple(40789, 53103), tuple(9242, 20814), tuple(784, 22584), tuple(8740, 56894), tuple(28504, 378), tuple(8287, 96930), tuple(74232, 97496), tuple(61565, 7904), tuple(9779, 45122), tuple(33767, 48471), tuple(16766, 10722), tuple(47764, 478), tuple(14374, 30099), tuple(89134, 19977), tuple(60860, 93201), tuple(71123, 29840), tuple(57159, 34410), tuple(82411, 99537), tuple(11307, 3733), tuple(70264, 43028), tuple(30418, 19372), tuple(46543, 31506), tuple(33043, 98980), tuple(21137, 63374), tuple(85640, 36957), tuple(6790, 60751), tuple(78771, 43700), tuple(33808, 38263), tuple(27232, 35152), tuple(39925, 5062), tuple(3120, 65621), tuple(39319, 6795), tuple(77468, 94964), tuple(10481, 43009), tuple(24237, 2103), tuple(16837, 55667), tuple(43846, 2874), tuple(78786, 66811), tuple(92185, 62395), tuple(26318, 87942), tuple(6208, 80815), tuple(66952, 71885), tuple(51435, 25450), tuple(21443, 69801), tuple(92554, 81977), tuple(58912, 82288), tuple(59681, 46177), tuple(60397, 65866), tuple(72065, 13318), tuple(2848, 73852), tuple(7577, 83238), tuple(209, 40659), tuple(72103, 15266), tuple(23365, 67286), tuple(14600, 29269), tuple(85541, 63289), tuple(25427, 54812), tuple(22967, 54965), tuple(81525, 27126), tuple(20473, 55455), tuple(84067, 25794), tuple(46798, 79332), tuple(93825, 74677), tuple(447, 5904), tuple(65661, 92916), tuple(54428, 76482), tuple(1025, 34415), tuple(63761, 30038), tuple(93025, 15090), tuple(98807, 93426), tuple(57562, 59615), tuple(84884, 30620), tuple(75066, 71824), tuple(51199, 37934), tuple(95530, 15260), tuple(513, 98278), tuple(62995, 28267), tuple(47535, 69776), tuple(39266, 4696), tuple(14742, 13225), tuple(44268, 16548), tuple(45976, 41680), tuple(99638, 30285), tuple(85609, 5578), tuple(28156, 12884), tuple(76188, 86573), tuple(44639, 15480), tuple(86789, 72636), tuple(18702, 13337), tuple(96638, 59398), tuple(90988, 26909), tuple(85069, 95193), tuple(67262, 38337), tuple(51694, 19659), tuple(93341, 80988), tuple(48733, 88460), tuple(55630, 22866), tuple(96203, 10316), tuple(30644, 68318), tuple(79292, 63136), tuple(60185, 73681), tuple(60474, 19078), tuple(48721, 82811), tuple(19713, 99527), tuple(17537, 55235), tuple(296, 8353), tuple(58691, 72158), tuple(66734, 92490), tuple(87642, 7174), tuple(78285, 35337), tuple(19503, 14273), tuple(10713, 64116), tuple(85966, 98738), tuple(56561, 99347), tuple(14869, 89963), tuple(95126, 30748), tuple(89272, 79060), tuple(69888, 7193), tuple(32583, 74564), tuple(95542, 52128), tuple(78360, 42675), tuple(86062, 68404), tuple(38732, 21411), tuple(92935, 45415), tuple(99027, 83925), tuple(73232, 37405), - tuple(97079, 87722), tuple(49453, 61633), tuple(53860, 29672), tuple(29513, 42837), tuple(47802, 37592), tuple(12469, 34249), tuple(42792, 53643), tuple(62613, 70068), tuple(82526, 73215), tuple(72285, 11113), tuple(42500, 55478), tuple(43717, 95240), tuple(43797, 75361), tuple(24335, 33138), tuple(38245, 66964), tuple(20717, 95342), tuple(64691, 18064), tuple(48154, 59381), tuple(25699, 77506), tuple(67122, 65358), tuple(4706, 47639), tuple(39616, 49499), tuple(81811, 43780), tuple(835, 85236), tuple(55259, 36091), tuple(66392, 64552), tuple(18111, 14142), tuple(3313, 24306), tuple(16039, 50370), tuple(13767, 95591), tuple(3619, 17285), tuple(91602, 86378), tuple(65370, 31997), tuple(73103, 79544), tuple(56207, 1833), tuple(83803, 12639), tuple(99772, 62410), tuple(48525, 27025), tuple(4843, 17363), tuple(90649, 69831), tuple(38294, 83908), tuple(62986, 50254), tuple(80915, 86505), tuple(77831, 38427), tuple(65269, 10998), tuple(18364, 35533), tuple(90495, 89902), tuple(18124, 73639), tuple(37972, 64523), tuple(59756, 55630), tuple(96021, 46676), tuple(62423, 13536), tuple(57039, 79285), tuple(22584, 69218), tuple(82257, 47164), tuple(85219, 84268), tuple(99430, 87817), tuple(54061, 57126), tuple(95756, 20489), tuple(27341, 67329), tuple(26826, 81082), tuple(86892, 10021), tuple(76753, 35118), tuple(45122, 27808), tuple(16706, 37697), tuple(54734, 85826), tuple(61753, 99199), tuple(54920, 42265), tuple(79397, 32234), tuple(95638, 71031), tuple(66412, 11193), tuple(1874, 46395), tuple(43748, 82517), tuple(99596, 63908), tuple(83987, 8436), tuple(52027, 64590), tuple(2821, 71063), tuple(38625, 47246), tuple(64890, 18073), tuple(78221, 45134), tuple(51729, 8336), tuple(7235, 32732), tuple(69849, 4355), tuple(66632, 29817), tuple(87115, 20363), tuple(95882, 30431), tuple(56386, 94579), tuple(90145, 75474), tuple(41608, 31413), tuple(9284, 22127), tuple(60184, 97820), tuple(70813, 48129), tuple(84426, 2932), tuple(74599, 76277), tuple(19551, 2074), tuple(83741, 31228), tuple(24540, 69367), tuple(66373, 48734), tuple(1043, 52758), tuple(55207, 67301), tuple(68930, 95643), tuple(60576, 84717), tuple(87418, 61921), tuple(40078, 11839), tuple(26131, 84239), tuple(57553, 30194), tuple(85992, 83655), tuple(9469, 26355), tuple(7871, 58669), tuple(66599, 94551), tuple(73474, 91775), tuple(49288, 37125), tuple(64459, 41703), tuple(20761, 11140), tuple(18453, 75828), tuple(7666, 3242), tuple(3851, 64702), tuple(99727, 5458), tuple(66247, 1430), tuple(53801, 94055), tuple(18633, 22045), tuple(5594, 41215), tuple(23845, 66849), tuple(51801, 46932), tuple(63960, 28483), tuple(41699, 56219), tuple(40125, 68122), tuple(1809, 49948), tuple(39206, 71380), tuple(29721, 38251), tuple(10858, 91494), tuple(33459, 29030), tuple(230, 28395), tuple(45666, 41675), tuple(31763, 68954), tuple(28498, 58275), tuple(27329, 14755), tuple(98543, 32649), tuple(26836, 19108), tuple(31349, 45705), tuple(89891, 19691), tuple(42716, 14912), tuple(34712, 4213), tuple(72574, 37957), tuple(29648, 54691), tuple(77851, 61441), tuple(62360, 25693), tuple(78942, 30840), tuple(58808, 36761), tuple(39400, 96765), tuple(77844, 33261), tuple(98738, 80688), tuple(99234, 82018), tuple(49364, 46291), tuple(12030, 32499), tuple(42946, 19435), tuple(34021, 40823), tuple(54070, 84294), tuple(76238, 50858), tuple(94576, 62781), tuple(4823, 17885), tuple(55861, 33029), tuple(43468, 78885), tuple(15775, 25990), tuple(2481, 64154), tuple(65398, 30074), tuple(85540, 50290), tuple(28926, 8236), tuple(75476, 56523), tuple(51363, 82037), tuple(97952, 75624), tuple(71526, 55313), tuple(55143, 49146), tuple(11616, 79576), tuple(83981, 24799), tuple(30197, 96196), tuple(46658, 64430), tuple(34062, 422), tuple(91483, 37099), tuple(88286, 27917), tuple(92932, 21740), tuple(66567, 3590), tuple(35787, 37973), tuple(22601, 93171), tuple(91215, 51041), tuple(41702, 3198), tuple(78731, 89390), tuple(46850, 34544), tuple(49404, 10082), tuple(6393, 62145), tuple(39241, 12836), tuple(10383, 56740), tuple(50850, 84828), tuple(39908, 95738), tuple(78309, 9041), tuple(7898, 34833), tuple(80987, 44693), tuple(66170, 90006), tuple(57599, 82993), tuple(33458, 68423), tuple(67786, 72838), tuple(20364, 89240), tuple(66760, 10965), tuple(77090, 39427), tuple(8706, 77011), tuple(76225, 66045), tuple(24829, 16580), tuple(29040, 10745), tuple(25973, 68024), tuple(42886, 90000), tuple(5125, 90725), tuple(71675, 42961), tuple(88074, 15503), tuple(7096, 77238), tuple(12467, 7224), tuple(94344, 88876), tuple(91643, 6834), tuple(99776, 88805), tuple(88992, 21613), tuple(27629, 72345), tuple(99262, 53013), tuple(84894, 7422), tuple(86469, 44852), tuple(44709, 82358), tuple(90338, 63426), tuple(70093, 65813), tuple(18014, 91297), tuple(38135, 18842), tuple(83643, 25418), tuple(61985, 23996), tuple(63495, 91872), tuple(65518, 85038), tuple(19747, 18165), tuple(60916, 55986), tuple(73826, 68279), tuple(26147, 73224), tuple(79606, 35342), tuple(84063, 47648), tuple(41115, 19541), tuple(3710, 46873), tuple(79926, 96227), tuple(77885, 38548), tuple(31121, 40100), tuple(17197, 42700), tuple(89898, 65060), tuple(70854, 69897), tuple(47212, 78340), tuple(8871, 20368), tuple(81502, 76700), tuple(6251, 92364), tuple(20652, 81152), tuple(60197, 15839), tuple(57972, 74525), tuple(51527, 19454), tuple(43081, 37404), tuple(11551, 38858), tuple(73710, 36789), tuple(99064, 62900), tuple(16292, 61362), tuple(5438, 13708), tuple(65610, 23813), tuple(14701, 66586), tuple(96560, 25606), tuple(6101, 50386), tuple(76171, 13159), tuple(453, 51097), tuple(80871, 29995), tuple(65414, 10781), tuple(56201, 59852), tuple(57872, 29305), tuple(85387, 49642), tuple(98332, 45308), tuple(78934, 88320), tuple(27288, 84343), tuple(71539, 48945), tuple(17348, 35121), tuple(51107, 73807), tuple(60739, 89966), tuple(49803, 53931), tuple(91339, 18943), tuple(66923, 8421), tuple(1314, 61554), tuple(56175, 73062), tuple(1215, 51606), tuple(16335, 1289), tuple(62888, 62858), tuple(26620, 51572), tuple(19757, 93782), tuple(95016, 49464), tuple(77323, 61370), tuple(14358, 40637), tuple(75344, 25972), tuple(12606, 53465), tuple(22258, 96976), tuple(36276, 85574), tuple(93544, 82426), tuple(50554, 1078), tuple(54132, 59093), tuple(83143, 37847), tuple(36778, 7827), tuple(42525, 47562), tuple(48399, 16894), tuple(48812, 94330), tuple(2197, 63615), tuple(16681, 83461), tuple(61452, 74775), tuple(33179, 39047), tuple(11357, 45663), tuple(34429, 3118), tuple(97453, 71262), tuple(76788, 9452), tuple(42050, 94414), tuple(62412, 57976), tuple(10366, 59637), tuple(91804, 41509), tuple(66410, 60144), tuple(20517, 85756), tuple(16402, 50019), tuple(33062, 94128), tuple(66717, 95079), tuple(80801, 53200), tuple(43638, 16443), tuple(68690, 30310), tuple(78330, 31709), tuple(53288, 51831), tuple(59612, 15340), tuple(69197, 67661), tuple(25729, 54544), tuple(61292, 3077), tuple(91198, 68925), tuple(94243, 84911), tuple(81175, 41496), tuple(56087, 30799), tuple(19192, 5965), tuple(6401, 98593), tuple(90599, 40564), tuple(99636, 3224), tuple(13595, 15079), tuple(81758, 98510), tuple(31894, 87256), tuple(32717, 15856), tuple(58045, 25722), tuple(80820, 39359), tuple(89127, 50007), tuple(57129, 30173), tuple(143, 82490), tuple(67320, 89298), tuple(78031, 70771), tuple(56372, 13856), tuple(53792, 93923), tuple(77536, 97686), tuple(34281, 88893), tuple(7720, 38598), tuple(42577, 15445), tuple(97049, 25292), tuple(5755, 75274), tuple(68500, 88440), tuple(65460, 91968), tuple(82251, 62700), tuple(58737, 28221), tuple(33966, 31858), tuple(89283, 6137), tuple(95713, 57892), tuple(64716, 41893), tuple(23226, 72596), tuple(77494, 1226), tuple(57456, 97085), tuple(56638, 73195), tuple(86791, 95487), tuple(38616, 3710), tuple(27612, 88402), tuple(86211, 61383), tuple(74751, 67963), tuple(35294, 45969), tuple(20040, 65633), tuple(46379, 55793), tuple(5254, 33070), tuple(96039, 46828), tuple(53169, 86515), tuple(61456, 32728), tuple(19306, 28894), tuple(266, 73943), tuple(6298, 78481), tuple(55429, 72954), tuple(4013, 62916), tuple(19413, 82227), tuple(97767, 41262), tuple(1754, 66034), tuple(62168, 95138), tuple(8502, 1232), tuple(87736, 22730), tuple(94519, 57526), tuple(47840, 52733), tuple(57430, 11063), tuple(48203, 88666), tuple(94187, 17306), tuple(51254, 3414), tuple(42847, 70850), tuple(30517, 37298), tuple(51002, 3187), tuple(69065, 57819), tuple(28158, 31948), tuple(28641, 57066), tuple(64401, 41541), tuple(16769, 93104), tuple(36026, 40379), tuple(89972, 21007), tuple(23129, 3532), tuple(25076, 78327), tuple(91716, 52771), tuple(85414, 74704), tuple(25078, 29180), tuple(82397, 27229), tuple(71809, 86957), tuple(84698, 34879), tuple(28573, 40238), tuple(79803, 740), tuple(9445, 37484), tuple(30310, 44337), tuple(48396, 61468), tuple(55206, 32145), tuple(85429, 8394), tuple(57022, 16754), tuple(20686, 31181), tuple(49381, 90708), tuple(86368, 80026), tuple(12503, 98384), tuple(17987, 17590), tuple(87975, 47488), tuple(1917, 40089), tuple(28921, 51966), tuple(75061, 47560), tuple(47682, 85166), tuple(44983, 43829), tuple(65132, 39721), tuple(63156, 48271), tuple(43469, 44812), tuple(92402, 52827), tuple(93574, 93874), tuple(49160, 14154), tuple(17068, 32747), tuple(24013, 48763), tuple(44388, 91612), tuple(50092, 46361), tuple(12638, 67958), tuple(22733, 48580), tuple(89692, 17614), tuple(76259, 80333), tuple(60667, 81071), tuple(11866, 60670), tuple(44127, 52256), tuple(91538, 16284), tuple(3947, 5775), tuple(24013, 87274), tuple(61890, 35907), tuple(40033, 65872), tuple(82217, 28133), tuple(92448, 66250), tuple(69722, 79165), tuple(45642, 94585), tuple(19332, 52617), tuple(68376, 74661), tuple(67538, 36822), tuple(44251, 51303), tuple(21737, 91360), tuple(89816, 80022), tuple(62003, 95863), tuple(91553, 89883), tuple(81327, 60251), tuple(79081, 77218), tuple(83875, 50172), tuple(64753, 45810), tuple(31661, 24768), tuple(37225, 43323), tuple(7290, 90747), tuple(83726, 40577), tuple(67955, 25873), tuple(64937, 91179), tuple(43298, 1399), tuple(63408, 89816), tuple(7356, 46305), tuple(45184, 57862), tuple(53297, 20667), tuple(31709, 43903), tuple(62673, 94822), tuple(27913, 75493), tuple(24687, 80894), tuple(98262, 61113), tuple(53123, 55460), tuple(35147, 99271), tuple(27646, 34622), tuple(73077, 30910), tuple(20512, 11203), tuple(33770, 99560), tuple(34580, 35708), tuple(67059, 99164), tuple(72256, 23281), tuple(92534, 87165), tuple(52774, 85396), tuple(56154, 92895), tuple(24460, 62535), tuple(56634, 4948), tuple(26182, 65572), tuple(99426, 1748), tuple(31410, 43026), tuple(24172, 44480), tuple(9426, 61375), tuple(21718, 32700), tuple(91428, 80453), tuple(27315, 90992), tuple(2743, 10613), tuple(31248, 75906), tuple(99086, 51477), tuple(44829, 14817), tuple(89794, 9674), tuple(88775, 1393), tuple(94268, 66168), tuple(76314, 51601), tuple(30142, 76441), tuple(39895, 74493), tuple(67323, 13617), tuple(54056, 77441), tuple(64209, 30237), tuple(81819, 2685), tuple(74961, 36558), tuple(54405, 11789), tuple(47419, 18927), tuple(95646, 97923), tuple(6793, 98367), tuple(84729, 55346), tuple(26209, 51661), tuple(88902, 47479), tuple(29248, 6257), tuple(90293, 56023), tuple(13659, 85240), tuple(33402, 22946), tuple(98715, 46806), tuple(59061, 14084), tuple(63647, 85523), tuple(16920, 66956), tuple(33806, 17029), tuple(5921, 55022), tuple(56918, 8478), tuple(26353, 8772), tuple(73846, 62583), tuple(66724, 13905), tuple(81927, 13344), tuple(8535, 73788), tuple(42475, 864), tuple(43233, 31055), tuple(25826, 92107), tuple(58400, 93558), tuple(76480, 27917), tuple(11783, 45463), tuple(1994, 30049), tuple(69277, 48157), tuple(64234, 49337), tuple(71741, 56314), tuple(2005, 69772), tuple(49003, 37695), tuple(39968, 77681), tuple(832, 10155), tuple(57104, 19308), tuple(96734, 11547), tuple(30184, 42421), tuple(48375, 44830), tuple(93937, 72937), tuple(90182, 73901), tuple(15777, 13356), tuple(23590, 41957), tuple(28339, 72344), tuple(57738, 11792), tuple(46981, 22885), tuple(89701, 63079), tuple(18853, 8142), tuple(71023, 86607), tuple(43044, 84076), tuple(15504, 63914), tuple(10852, 24752), tuple(84420, 8955), tuple(52478, 86472), tuple(31493, 34784), tuple(63120, 80839), tuple(99483, 7075), tuple(63463, 9701), tuple(72217, 81768), tuple(92665, 41759), tuple(61299, 30348), tuple(75839, 10811), tuple(40924, 73497), tuple(16847, 11969), tuple(62221, 40296), tuple(86514, 6737), tuple(71717, 21760), tuple(72280, 22605), tuple(84073, 26361), tuple(35313, 38026), tuple(88244, 41921), tuple(2395, 44993), tuple(43095, 4491), tuple(65029, 92486), tuple(77825, 37626), tuple(86268, 93758), tuple(12282, 96669), tuple(70109, 3854), tuple(88201, 48985), tuple(67432, 22291), tuple(71226, 78916), tuple(93242, 69896), tuple(45961, 28858), tuple(37543, 83277), tuple(33989, 19303), tuple(75997, 92827), tuple(81996, 50401), tuple(47766, 89974), tuple(6969, 58504), tuple(57495, 67704), tuple(42186, 15837), tuple(16266, 31543), tuple(53482, 57047), tuple(6741, 53863), tuple(69708, 62402), tuple(47622, 71680), tuple(56407, 59557), tuple(35497, 43626), tuple(94382, 72849), tuple(85322, 82494), tuple(48283, 84203), tuple(60187, 43506), tuple(30227, 72397), tuple(8179, 46362), tuple(91848, 6724), - tuple(44252, 49652), tuple(62329, 71957), tuple(48136, 53803), tuple(29408, 16811), tuple(2807, 35831), tuple(3982, 42400), tuple(31819, 922), tuple(60411, 29397), tuple(63298, 32886), tuple(56696, 94800), tuple(40452, 78058), tuple(72632, 46188), tuple(6187, 49011), tuple(41030, 50745), tuple(37287, 2821), tuple(85833, 58997), tuple(69513, 2449), tuple(67890, 79660), tuple(10510, 65067), tuple(81559, 31412), tuple(55173, 70275), tuple(30720, 65244), tuple(39362, 55066), tuple(82509, 54146), tuple(37012, 50941), tuple(7777, 73746), tuple(55324, 68529), tuple(64001, 14553), tuple(75092, 7732), tuple(30396, 68267), tuple(17785, 63304), tuple(60691, 75626), tuple(73360, 24028), tuple(49094, 44015), tuple(38186, 58436), tuple(85214, 87107), tuple(28851, 30981), tuple(97558, 16884), tuple(50485, 61227), tuple(66390, 14523), tuple(91346, 12746), tuple(27417, 80041), tuple(70675, 62543), tuple(30493, 34005), tuple(93733, 65419), tuple(84518, 73912), tuple(95889, 56587), tuple(25811, 87640), tuple(67088, 17670), tuple(61030, 10236), tuple(63615, 23071), tuple(95064, 2080), tuple(67171, 84194), tuple(29917, 74112), tuple(69466, 26813), tuple(65199, 34701), tuple(73665, 70408), tuple(79992, 52475), tuple(85736, 3601), tuple(37979, 55469), tuple(19825, 77218), tuple(31666, 7906), tuple(74130, 1569), tuple(51690, 48772), tuple(17872, 80141), tuple(26340, 29232), tuple(5680, 4004), tuple(72841, 60873), tuple(65725, 86711), tuple(39659, 76703), tuple(72206, 4834), tuple(25126, 62083), tuple(85881, 81324), tuple(36485, 89451), tuple(66626, 2033), tuple(23961, 13900), tuple(68404, 32573), tuple(6568, 75904), tuple(5463, 40836), tuple(82243, 72476), tuple(41469, 85222), tuple(53063, 80905), tuple(89420, 89405), tuple(64438, 54748), tuple(84609, 19925), tuple(58071, 76697), tuple(92927, 37184), tuple(86525, 43159), tuple(90683, 47762), tuple(4221, 94429), tuple(68456, 34730), tuple(5303, 27512), tuple(3857, 87130), tuple(25963, 49542), tuple(68981, 6398), tuple(16900, 38579), tuple(44288, 3921), tuple(41848, 6266), tuple(14235, 18205), tuple(93114, 74697), tuple(31693, 12438), tuple(93873, 3135), tuple(39867, 42033), tuple(39257, 83168), tuple(91557, 2168), tuple(25761, 99686), tuple(33891, 81917), tuple(69185, 25496), tuple(32937, 32667), tuple(53201, 77822), tuple(41625, 73595), tuple(75159, 2574), tuple(41500, 24049), tuple(18998, 17778), tuple(55319, 14793), tuple(98343, 55538), tuple(50306, 92789), tuple(8111, 36989), tuple(12332, 90370), tuple(50098, 64743), tuple(518, 42569), tuple(53218, 75332), tuple(37930, 85988), tuple(26622, 30733), tuple(72656, 87213), tuple(62011, 83799), tuple(74325, 39325), tuple(61424, 29292), tuple(38885, 35172), tuple(48893, 53493), tuple(5365, 46863), tuple(35192, 50130), tuple(67377, 31556), tuple(89291, 38089), tuple(7639, 18196), tuple(66102, 23782), tuple(94128, 13012), tuple(14739, 40637), tuple(38124, 70538), tuple(21143, 9114), tuple(81628, 1281), tuple(2385, 12814), tuple(97713, 12931), tuple(15392, 71469), tuple(285, 2139), tuple(75082, 86935), tuple(53815, 6664), tuple(21102, 26350), tuple(7191, 63079), tuple(77811, 54178), tuple(94993, 43113), tuple(23712, 9041), tuple(5151, 47040), tuple(48565, 21701), tuple(68877, 29339), tuple(41207, 91567), tuple(52928, 39020), tuple(90950, 13773), tuple(9269, 45202), tuple(76822, 23962), tuple(66347, 97641), tuple(39410, 6179), tuple(544, 78664), tuple(6168, 22720), tuple(41653, 97913), tuple(17421, 50726), tuple(7841, 71433), tuple(54395, 26607), tuple(4269, 30336), tuple(34319, 79247), tuple(95612, 91933), tuple(25657, 23904), tuple(19990, 4678), tuple(87858, 51586), tuple(5060, 74367), tuple(56282, 50346), tuple(24967, 12978), tuple(5413, 84898), tuple(83630, 81450), tuple(4445, 3246), tuple(8686, 37833), tuple(25753, 491), tuple(64949, 79083), tuple(91227, 99361), tuple(15478, 9776), tuple(19138, 1197), tuple(90231, 21775), tuple(1603, 3420), tuple(80483, 91945), tuple(26254, 47364), tuple(39956, 39011), tuple(21944, 68718), tuple(17476, 26688), tuple(35124, 95520), tuple(80675, 86048), tuple(37802, 24654), tuple(35991, 30501), tuple(58201, 32076), tuple(73425, 48851), tuple(32081, 62678), tuple(43890, 72473), tuple(55420, 55864), tuple(92730, 83636), tuple(98991, 57739), tuple(16554, 8671), tuple(97393, 74717), tuple(52946, 40432), tuple(54038, 57550), tuple(28407, 59994), tuple(51960, 69485), tuple(54362, 59837), tuple(56608, 44803), tuple(87765, 65240), tuple(83537, 20410), tuple(12462, 63600), tuple(29652, 60603), tuple(33694, 86009), tuple(6328, 2247), tuple(1163, 66579), tuple(14339, 11994), tuple(84291, 50111), tuple(68889, 88342), tuple(10269, 72875), tuple(66319, 32677), tuple(13064, 29930), tuple(32190, 83783), tuple(45970, 36250), tuple(76934, 56387), tuple(86991, 52198), tuple(595, 49931), tuple(56487, 35023), tuple(64301, 3898), tuple(62751, 16083), tuple(76962, 42724), tuple(25144, 84082), tuple(42577, 55365), tuple(35491, 62043), tuple(98690, 95354), tuple(78142, 63556), tuple(43994, 19756), tuple(44371, 88026), tuple(79528, 38378), tuple(60538, 95096), tuple(94043, 64914), tuple(56328, 52212), tuple(23125, 3718), tuple(68265, 31800), tuple(50257, 30242), tuple(90023, 16264), tuple(26536, 54846), tuple(56408, 26069), tuple(48835, 90685), tuple(41440, 85486), tuple(50439, 28977), tuple(57270, 58062), tuple(93385, 81991), tuple(41747, 90412), tuple(4397, 54135), tuple(27833, 63238), tuple(30047, 54689), tuple(97424, 22639), tuple(38666, 7212), tuple(11002, 23871), tuple(94214, 17282), tuple(38570, 36544), tuple(21281, 2550), tuple(18772, 9450), tuple(21573, 89501), tuple(72980, 3658), tuple(4270, 75217), tuple(84311, 52335), tuple(50238, 7027), tuple(79880, 13083), tuple(4453, 53612), tuple(42094, 83535), tuple(26820, 29389), tuple(68926, 37356), tuple(41068, 99341), tuple(17804, 36252), tuple(95293, 20765), tuple(16191, 10046), tuple(97430, 9242), tuple(8161, 65278), tuple(57448, 35098), tuple(94391, 77130), tuple(98916, 39103), tuple(20350, 32970), tuple(90646, 11890), tuple(75362, 5602), tuple(27639, 77663), tuple(43360, 46034), tuple(22402, 35513), tuple(8137, 28845), tuple(85004, 55522), tuple(41422, 53954), tuple(31450, 3352), tuple(43577, 62316), tuple(2055, 68345), tuple(81241, 88166), tuple(99688, 49801), tuple(34150, 62685), tuple(61452, 89940), tuple(17271, 17048), tuple(35360, 54074), tuple(24514, 86758), tuple(65291, 94634), tuple(56371, 29354), tuple(17017, 58323), tuple(97782, 38903), tuple(64924, 7863), tuple(26686, 44764), tuple(6946, 65671), tuple(19164, 55697), tuple(53736, 61363), tuple(16698, 88605), tuple(68917, 45223), tuple(61620, 71030), tuple(36833, 87), tuple(25499, 92341), tuple(64999, 60843), tuple(10332, 23741), tuple(44183, 74772), tuple(5876, 83182), tuple(2777, 13943), tuple(30518, 90419), tuple(95186, 2879), tuple(67770, 28915), tuple(63386, 30643), tuple(32598, 27470), tuple(22690, 14598), tuple(33055, 49882), tuple(66771, 87914), tuple(1528, 77971), tuple(35398, 19178), tuple(66537, 4756), tuple(45085, 80236), tuple(67054, 78236), tuple(98986, 54864), tuple(22249, 20937), tuple(63417, 4603), tuple(77613, 1198), tuple(6611, 68649), tuple(73627, 56112), tuple(73633, 97606), tuple(99775, 39893), tuple(56752, 97490), tuple(32399, 69269), tuple(62186, 76302), tuple(94184, 83140), tuple(75653, 83050), tuple(61775, 15249), tuple(47463, 90032), tuple(21937, 11962), tuple(62241, 55183), tuple(98504, 4933), tuple(66272, 28609), tuple(67746, 30488), tuple(54196, 35321), tuple(26259, 63765), tuple(11312, 41065), tuple(15815, 63183), tuple(18767, 28578), tuple(88996, 89259), tuple(5635, 82121), tuple(82835, 33221), tuple(23282, 57401), tuple(32823, 83397), tuple(9333, 22645), tuple(93949, 88014), tuple(53441, 1659), tuple(7606, 35735), tuple(52971, 83457), tuple(77912, 97683), tuple(33195, 462), tuple(36642, 84826), tuple(14627, 39789), tuple(35158, 74576), tuple(31178, 3298), tuple(78999, 38000), tuple(58392, 70574), tuple(32797, 9393), tuple(31745, 73068), tuple(3463, 99157), tuple(85198, 99633), tuple(69136, 61700), tuple(90269, 56346), tuple(54888, 30161), tuple(69578, 67718), tuple(31362, 88940), tuple(72151, 69802), tuple(10518, 42128), tuple(10389, 85928), tuple(79024, 46427), tuple(75298, 320), tuple(61374, 92005), tuple(52693, 92872), tuple(90677, 97987), tuple(4977, 7002), tuple(34950, 40356), tuple(1286, 84570), tuple(83587, 44056), tuple(60303, 67073), tuple(90693, 54889), tuple(3665, 65556), tuple(36965, 91476), tuple(36018, 1114), tuple(64173, 73176), tuple(74375, 37973), tuple(40226, 4894), tuple(15708, 44097), tuple(58497, 62654), tuple(88190, 8945), tuple(89623, 42745), tuple(34666, 46515), tuple(69908, 60462), tuple(85257, 89711), tuple(95343, 25345), tuple(9671, 68154), tuple(43560, 12783), tuple(56946, 86701), tuple(97027, 16286), tuple(73465, 82933), tuple(60864, 98196), tuple(65681, 78455), tuple(95949, 98824), tuple(60427, 79639), tuple(62125, 96638), tuple(44349, 55436), tuple(23889, 98287), tuple(7982, 57100), tuple(89134, 62354), tuple(90345, 13136), tuple(87117, 55670), tuple(87142, 56737), tuple(12166, 62622), tuple(25757, 83242), tuple(22839, 70866), tuple(55230, 87710), tuple(52836, 58660), tuple(52001, 11928), tuple(32300, 43940), tuple(82815, 15966), tuple(25995, 35077), tuple(85590, 71892), tuple(65700, 97621), tuple(93120, 45889), tuple(39090, 18624), tuple(40711, 54078), tuple(21512, 71018), tuple(84273, 83224), tuple(94455, 11165), tuple(44762, 81935), tuple(32383, 12092), tuple(57681, 45595), tuple(34900, 10701), tuple(75918, 15671), tuple(80894, 47445), tuple(84887, 65029), tuple(20046, 53478), tuple(35117, 350), tuple(58605, 95757), tuple(41129, 70721), tuple(17956, 60019), tuple(92256, 31486), tuple(25628, 39061), tuple(46442, 95181), tuple(99796, 98961), tuple(84447, 67126), tuple(90905, 35020), tuple(44920, 85041), tuple(29355, 81130), tuple(27574, 74365), tuple(18795, 87608), tuple(9134, 94033), tuple(40788, 33406), tuple(66374, 27589), tuple(35828, 79467), tuple(63337, 21310), tuple(19850, 91859), tuple(8457, 46910), tuple(17247, 54352), - tuple(78157, 12984), tuple(19014, 95404), tuple(33696, 61297), tuple(84179, 86141), tuple(88420, 35793), tuple(56841, 54309), tuple(61086, 51639), tuple(51422, 42981), tuple(24495, 8959), tuple(44560, 17680), tuple(5010, 310), tuple(7658, 47071), tuple(99542, 84835), tuple(32524, 20855), tuple(74043, 44891), tuple(86735, 20535), tuple(73782, 59995), tuple(93939, 67476), tuple(3553, 2160), tuple(28263, 96116), tuple(43233, 84344), tuple(82394, 41116), tuple(94208, 54259), tuple(19293, 78307), tuple(97691, 89913), tuple(69180, 92822), tuple(54514, 17070), tuple(41565, 19577), tuple(56860, 57944), tuple(79429, 90334), tuple(14209, 63872), tuple(8193, 47375), tuple(84457, 5729), tuple(35499, 29880), tuple(71046, 96629), tuple(78320, 16678), tuple(52171, 9008), tuple(10285, 77600), tuple(89222, 56452), tuple(74877, 45130), tuple(45122, 7477), tuple(27680, 36119), tuple(53572, 67978), tuple(84553, 96126), tuple(79947, 45957), tuple(62290, 6001), tuple(24885, 66150), tuple(74756, 8350), tuple(19600, 26665), tuple(26140, 12798), tuple(346, 52401), tuple(55107, 64736), tuple(55227, 20192), tuple(5250, 75970), tuple(22597, 36117), tuple(46057, 6297), tuple(146, 34226), tuple(62101, 45872), tuple(50973, 81711), tuple(28437, 90316), tuple(87949, 33752), tuple(97761, 97260), tuple(33866, 10748), tuple(43546, 29031), tuple(94371, 94717), tuple(23676, 36834), tuple(78530, 40195), tuple(9449, 90868), tuple(4688, 18113), tuple(6478, 91149), tuple(5770, 53359), tuple(67467, 45123), tuple(99961, 85269), tuple(16771, 9423), tuple(86093, 12345), tuple(57781, 91310), tuple(64235, 75090), tuple(44983, 14518), tuple(97295, 86692), tuple(9084, 63091), tuple(95536, 58091), tuple(19196, 84870), tuple(30135, 5353), tuple(8031, 34065), tuple(30428, 11126), tuple(64190, 14178), tuple(31159, 97833), tuple(52514, 36312), tuple(39470, 95767), tuple(56521, 58481), tuple(67491, 58112), tuple(93348, 30378), tuple(68108, 71570), tuple(1098, 967), tuple(38546, 81282), tuple(56825, 65979), tuple(59069, 72390), tuple(2824, 13703), tuple(48333, 14500), tuple(9893, 25478), tuple(2911, 47477), tuple(58177, 26157), tuple(82958, 11140), tuple(29280, 90771), tuple(87961, 77195), tuple(41212, 31814), tuple(57596, 21467), tuple(95701, 96920), tuple(42264, 95301), tuple(40893, 490), tuple(85295, 66203), tuple(82996, 55114), tuple(7692, 97829), tuple(29833, 90468), tuple(11917, 11893), tuple(29516, 81375), tuple(89726, 44500), tuple(8932, 49236), tuple(46908, 11625), tuple(37562, 86013), tuple(77455, 10903), tuple(71128, 96631), tuple(14398, 25421), tuple(22273, 24791), tuple(53432, 89441), tuple(65639, 13703), tuple(87847, 91934), tuple(79196, 83398), tuple(93841, 8300), tuple(98217, 31103), tuple(74206, 10777), tuple(87556, 26040), tuple(3488, 46363), tuple(5052, 37977), tuple(13148, 84770), tuple(57802, 44741), tuple(52562, 89370), tuple(45417, 28092), tuple(10850, 53735), tuple(55528, 13265), tuple(49639, 60069), tuple(24297, 65409), tuple(17691, 74780), tuple(14056, 91384), tuple(26678, 79745), tuple(20819, 65318), tuple(5109, 36921), tuple(32429, 44351), tuple(49300, 87487), tuple(91490, 17942), tuple(91165, 36259), tuple(19889, 92478), tuple(32525, 24314), - tuple(32207, 12383), tuple(18946, 80847), tuple(26629, 99576), tuple(23663, 33753), tuple(78661, 43289), tuple(42971, 85652), tuple(13148, 37633), tuple(7460, 31621), tuple(7106, 19344), tuple(56623, 40597), tuple(3931, 46383), tuple(40928, 80864), tuple(54411, 42893), tuple(97994, 37208), tuple(98704, 34056), tuple(28221, 65071), tuple(17035, 74621), tuple(44980, 99037), tuple(74685, 40941), tuple(51689, 87959), tuple(1603, 88894), tuple(58816, 80911), tuple(6600, 88186), tuple(51598, 56572), tuple(94570, 53971), tuple(36829, 22411), tuple(78272, 19485), tuple(27782, 91187), tuple(50738, 34908), tuple(77246, 37199), tuple(54696, 25448), tuple(22518, 20075), tuple(93517, 9215), tuple(63893, 61738), tuple(1803, 66542), tuple(8740, 44384), tuple(50214, 2536), tuple(82978, 22366), tuple(30379, 1906), tuple(69739, 14688), tuple(32367, 37176), tuple(94639, 5870), tuple(28089, 44553), tuple(23425, 12581), tuple(55612, 25746), tuple(10872, 85235), tuple(78490, 47300), tuple(53808, 99715), tuple(73003, 81707), tuple(69544, 44704), tuple(49921, 45937), tuple(53055, 64486), tuple(29788, 41235), tuple(66977, 32399), tuple(14272, 19156), tuple(52628, 43512), tuple(84512, 95549), tuple(37407, 73000), tuple(97454, 5645), tuple(94560, 22177), tuple(43121, 24751), tuple(16392, 71632), tuple(57061, 95049), tuple(78509, 12622), tuple(19152, 31950), tuple(60772, 58980), tuple(80874, 81688), tuple(57277, 94240), tuple(72106, 83377), tuple(96685, 38772), tuple(67254, 25148), tuple(55927, 21049), tuple(60288, 62456), tuple(70931, 77751), tuple(79197, 16179), tuple(3655, 47431), tuple(46830, 73545), tuple(45050, 41379), tuple(27458, 17406), tuple(39099, 99237), tuple(67490, 80382), tuple(62723, 79115), tuple(8353, 32032), tuple(23488, 12738), tuple(4013, 67892), tuple(26464, 50462), tuple(35943, 94375), tuple(44186, 34987), tuple(60927, 88755), tuple(8465, 63630), tuple(66062, 34368), tuple(88036, 58046), tuple(58249, 81975), tuple(18237, 47105), tuple(6733, 82935), tuple(78749, 39033), tuple(15131, 26574), tuple(33524, 8929), tuple(41839, 16312), tuple(55673, 27094), tuple(49538, 11713), tuple(88371, 69776), tuple(94252, 13464), tuple(5832, 84375), tuple(89357, 64815), tuple(46801, 11748), tuple(30666, 81254), tuple(16789, 10599), tuple(42935, 98646), tuple(66143, 20772), tuple(44556, 23422), tuple(46018, 16312), tuple(22259, 77171), tuple(56983, 86832), tuple(49921, 22125), tuple(92296, 45486), tuple(69764, 34023), tuple(9637, 96817), tuple(51206, 30615), tuple(10348, 92983), tuple(14472, 99323), tuple(40262, 97192), tuple(2191, 68285), tuple(22924, 25679), tuple(5067, 66212), tuple(69487, 49838), tuple(20052, 16120), tuple(95935, 71293), tuple(1776, 51796), tuple(23090, 30667), tuple(52957, 44078), tuple(91342, 24546), tuple(50425, 89005), tuple(53426, 47583), tuple(6584, 10139), tuple(3118, 13516), tuple(55369, 99320), tuple(63349, 89873), tuple(64571, 74984), tuple(26517, 2840), tuple(33325, 27702), tuple(43660, 87008), tuple(25645, 63104), tuple(26144, 93808), tuple(38030, 8593), tuple(46594, 22518), tuple(17472, 60358), tuple(23471, 75904), tuple(64653, 25665), tuple(58930, 2800), tuple(9714, 21659), tuple(90877, 15172), tuple(24169, 33165), tuple(40301, 28982), tuple(5835, 58106), tuple(47425, 55922), tuple(85182, 8833), tuple(54791, 76061), tuple(43334, 95233), tuple(88895, 11611), tuple(67974, 99700), tuple(42359, 7619), tuple(46091, 3188), tuple(96126, 1150), tuple(67300, 70263), tuple(15504, 6792), tuple(43488, 51128), tuple(59882, 82061), tuple(53674, 82213), tuple(13942, 71867), tuple(37229, 44172), tuple(8234, 10116), tuple(63955, 93663), tuple(90556, 81422), tuple(36156, 23916), tuple(80004, 32883), tuple(97483, 91439), tuple(40380, 75895), tuple(49030, 13388), tuple(10112, 23706), tuple(12693, 54634), tuple(89114, 44239), tuple(79449, 97096), tuple(93945, 17715), tuple(84610, 91409), tuple(77676, 32755), tuple(96528, 43811), tuple(32771, 17368), tuple(72726, 51584), tuple(68373, 27912), tuple(13120, 2918), tuple(32440, 89244), tuple(94188, 42530), tuple(47136, 16569), tuple(38629, 72378), tuple(6704, 33393), tuple(29123, 2559), tuple(4782, 19801), tuple(67017, 24504), tuple(10924, 38797), tuple(8704, 22146), tuple(32103, 77336), tuple(38053, 7912), tuple(6562, 22610), tuple(27942, 63645), tuple(40426, 37003), tuple(21523, 64033), tuple(46299, 23083), tuple(48752, 46938), tuple(28350, 22135), tuple(14308, 14980), tuple(14573, 24398), tuple(7228, 37742), tuple(82025, 86225), tuple(53716, 36196), tuple(81985, 16387), tuple(57584, 81974), tuple(99131, 7574), tuple(62847, 67483), tuple(86114, 25787), tuple(53211, 78386), tuple(80778, 49571), tuple(32747, 39211), tuple(71223, 56413), tuple(60888, 54163), tuple(7192, 51136), tuple(53334, 59228), tuple(4421, 66662), tuple(33413, 84705), tuple(56666, 78956), tuple(61801, 42678), tuple(43053, 77774), tuple(63820, 28596), tuple(1077, 97015), tuple(64021, 64781), tuple(92706, 3502), tuple(10677, 85713), tuple(25226, 89936), tuple(72837, 6353), tuple(48761, 89256), tuple(65947, 66655), tuple(72098, 73523), tuple(90795, 85045), tuple(55030, 92320), tuple(55194, 86101), tuple(10745, 5734), tuple(88677, 87133), tuple(73661, 34366), tuple(45949, 27357), tuple(88662, 12417), tuple(8489, 13536), tuple(27549, 4543), tuple(92939, 3478), tuple(54350, 80071), tuple(18846, 12230), tuple(44026, 16272), tuple(61412, 29030), tuple(19174, 95986), tuple(95690, 96752), tuple(89204, 59178), tuple(88740, 16634), tuple(86216, 73138), tuple(78274, 75895), tuple(33616, 9671), tuple(34033, 95200), tuple(34068, 66630), tuple(86998, 11248), tuple(33926, 18414), tuple(70477, 34726), tuple(78159, 51516), tuple(39498, 51001), tuple(8129, 45722), tuple(80034, 74923), tuple(72313, 14607), tuple(24587, 66307), tuple(1705, 17417), tuple(72039, 30982), tuple(38633, 26195), tuple(28199, 39357), tuple(38581, 11807), tuple(86457, 96618), tuple(26962, 65955), tuple(91876, 59903), tuple(73754, 98220), tuple(52133, 25146), tuple(66110, 69603), tuple(1793, 33205), tuple(15034, 44701), tuple(51124, 76916), tuple(39534, 36662), tuple(25979, 69668), tuple(57384, 67311), tuple(18545, 60762), tuple(19046, 92399), tuple(38249, 4653), tuple(72527, 59270), tuple(33966, 37148), tuple(27590, 117), tuple(70374, 4742), tuple(16192, 75527), tuple(80174, 75518), tuple(48577, 98517), tuple(94414, 14445), tuple(67138, 13404), tuple(92294, 29477), tuple(20990, 61280), tuple(6082, 29420), tuple(43509, 34811), tuple(58769, 96326), tuple(47885, 53134), tuple(85917, 57373), tuple(64717, 50457), tuple(91239, 92881), tuple(76074, 79095), tuple(59275, 17799), tuple(75883, 62491), tuple(39406, 78272), tuple(70235, 10333), tuple(42309, 18408), tuple(53739, 27090), tuple(98800, 48751), tuple(99156, 65930), tuple(6271, 73084), tuple(83758, 55709), tuple(76940, 71591), tuple(37834, 27418), tuple(46486, 84091), tuple(82677, 5668), tuple(55483, 14400), tuple(64749, 41246), tuple(56980, 81320), tuple(46656, 22155), tuple(85260, 3342), tuple(56103, 57517), tuple(15434, 77526), tuple(90927, 99005), tuple(30948, 35441), tuple(75700, 35428), tuple(46759, 50091), tuple(58497, 88465), tuple(4893, 48180), tuple(69671, 63922), tuple(53917, 42248), tuple(49692, 75520), tuple(70709, 69868), tuple(54172, 66609), tuple(61142, 39487), tuple(70390, 96503), tuple(70529, 96890), tuple(50848, 54706), tuple(62574, 78585), tuple(40976, 40546), tuple(61448, 25487), tuple(58813, 79694), tuple(7978, 46692), tuple(84810, 47407), tuple(93301, 86788), tuple(63513, 40417), tuple(50816, 14830), tuple(40051, 22510), tuple(58231, 10222), tuple(22087, 58099), tuple(9757, 76220), tuple(75890, 86576), tuple(78978, 41357), tuple(93283, 24850), tuple(97099, 31675), tuple(73642, 95617), tuple(12154, 57246), tuple(67695, 45009), tuple(24512, 19561), tuple(32912, 57975), tuple(67101, 86402), tuple(97078, 61699), tuple(35404, 84433), tuple(18387, 5574), tuple(1180, 56954), tuple(31887, 11863), tuple(35632, 74282), tuple(97946, 71382), tuple(6358, 42197), tuple(3005, 63694), tuple(33955, 21482), tuple(81930, 4333), tuple(91114, 65927), tuple(47204, 48539), tuple(79942, 34686), tuple(77140, 46671), tuple(24959, 43593), tuple(26774, 12024), tuple(17931, 38033), tuple(18367, 18250), tuple(40538, 12655), tuple(10024, 2337), tuple(13012, 93366), tuple(33214, 99392), tuple(48339, 83815), tuple(2573, 98741), tuple(82426, 99315), tuple(29161, 8227), tuple(75276, 43314), tuple(11764, 85873), tuple(67647, 12433), tuple(31911, 90471), tuple(9087, 22041), tuple(10617, 3373), tuple(82133, 44117), tuple(31131, 3256), tuple(99076, 13086), tuple(96657, 56552), tuple(23455, 55109), tuple(5669, 90191), tuple(63460, 25790), tuple(85510, 22302), tuple(12906, 98397), tuple(18329, 44604), tuple(15748, 14050), tuple(21155, 62893), tuple(37100, 83871), tuple(25674, 92250), tuple(53171, 44750), tuple(46127, 54563), tuple(44166, 66056), tuple(64908, 74534), tuple(78505, 79266), tuple(93470, 55884), tuple(11989, 62985), tuple(37465, 50339), tuple(22878, 79000), tuple(95605, 98821), tuple(99575, 49974), tuple(41632, 45612), tuple(12260, 36028), tuple(50671, 19437), tuple(91453, 49879), tuple(12875, 83736), tuple(64102, 71173), tuple(48782, 46491), tuple(83745, 47638), tuple(88148, 76992), tuple(48165, 43667), tuple(92415, 18463), tuple(21414, 88902), tuple(78393, 87130), tuple(98155, 18890), tuple(58021, 92700), tuple(44621, 68966), tuple(7039, 68126), tuple(56673, 25907), tuple(33837, 57344), tuple(87382, 7033), tuple(67819, 29142), tuple(15461, 61937), tuple(5731, 90942), tuple(95838, 74174), tuple(82453, 30836), tuple(77643, 4537), tuple(70433, 44347), tuple(31774, 69412), tuple(72181, 53595), tuple(92692, 68594), tuple(18251, 47790), tuple(58848, 6565), tuple(1826, 80845), tuple(44441, 73811), tuple(16752, 29291), tuple(96792, 64536), tuple(18171, 37623), tuple(55235, 61816), tuple(20040, 83863), tuple(32432, 85684), tuple(4782, 77614), tuple(63342, 59121), tuple(38926, 85577), tuple(84429, 6450), tuple(95889, 92209), tuple(79472, 39518), tuple(30545, 68613), tuple(16797, 18913), - tuple(57243, 45824), tuple(8263, 41386), tuple(44982, 25137), tuple(51794, 44064), tuple(40504, 59817), tuple(11905, 14718), tuple(68061, 89924), tuple(43733, 93778), tuple(16215, 98598), tuple(84708, 29188), tuple(63541, 86310), tuple(44070, 14118), tuple(289, 75590), tuple(45870, 49980), tuple(18753, 17336), tuple(24732, 68239), tuple(27871, 52742), tuple(14696, 68693), tuple(99672, 72079), tuple(71245, 39241), tuple(67389, 16601), tuple(21752, 72806), tuple(96913, 30350), tuple(54185, 84992), tuple(57931, 11103), tuple(48704, 71007), tuple(99423, 36848), tuple(41315, 42065), tuple(87326, 72929), tuple(25384, 89587), tuple(59014, 60053), tuple(62267, 54542), tuple(12610, 32715), tuple(68975, 19985), tuple(44486, 69639), tuple(82302, 6338), tuple(63215, 87639), tuple(96904, 81511), tuple(69636, 10922), tuple(88663, 59485), tuple(59135, 3754), tuple(75988, 60625), tuple(64120, 45694), tuple(70324, 80636), tuple(15785, 77984), tuple(51352, 57485), tuple(2805, 39888), tuple(74622, 13408), tuple(20818, 1699), tuple(52553, 75450), tuple(10931, 27164), tuple(25541, 66990), tuple(40600, 10237), tuple(37780, 18746), tuple(58451, 29777), tuple(99485, 87814), tuple(35490, 54578), tuple(27880, 7151), tuple(5621, 11448), tuple(16276, 60088), tuple(80061, 79868), tuple(79495, 85729), tuple(5786, 28078), tuple(7505, 68947), tuple(3754, 73982), tuple(83095, 13882), tuple(13625, 48305), tuple(65566, 46143), tuple(28436, 47606), tuple(41318, 73446), tuple(70295, 17014), tuple(70052, 53076), tuple(75069, 22060), tuple(38749, 80432), tuple(5609, 18240), tuple(73251, 78035), tuple(44085, 34842), tuple(64413, 19036), tuple(76628, 13364), tuple(77620, 44200), tuple(51408, 58569), tuple(63273, 12356), tuple(73860, 29016), tuple(1540, 65413), tuple(4154, 73503), tuple(8522, 57385), tuple(24021, 60714), tuple(53183, 28103), tuple(9298, 20623), tuple(35572, 72750), tuple(20062, 88412), tuple(53090, 54056), tuple(75500, 24693), tuple(63463, 23751), tuple(17602, 18766), tuple(36512, 54793), tuple(83806, 80218), tuple(13965, 576), tuple(18995, 7730), tuple(28727, 8773), tuple(90604, 21980), tuple(89414, 40461), tuple(61108, 29240), tuple(30381, 96560), tuple(32285, 26545), tuple(38149, 85647), tuple(51471, 24524), tuple(42153, 32188), tuple(12747, 84522), tuple(96507, 99501), tuple(39908, 54706), tuple(37524, 46449), tuple(98286, 72361), tuple(32855, 96561), tuple(67644, 50836), tuple(73095, 33548), tuple(3239, 57909), tuple(59527, 51138), tuple(61862, 15544), tuple(84048, 4884), tuple(17440, 53732), tuple(3534, 42048), tuple(66894, 37347), tuple(34323, 11139), tuple(64183, 38919), tuple(87672, 76883), tuple(99127, 44364), tuple(41417, 86134), tuple(51181, 7902), tuple(55456, 98977), tuple(52953, 51347), tuple(54164, 45398), tuple(36894, 60364), tuple(16911, 44245), tuple(92377, 11522), tuple(22925, 43609), tuple(52979, 70952), tuple(98059, 42838), tuple(18178, 76534), tuple(8036, 92888), tuple(88589, 19468), tuple(35901, 19860), tuple(65686, 87008), tuple(32387, 43037), tuple(87245, 20229), tuple(316, 46084), tuple(4111, 2785), tuple(89736, 56537), tuple(5724, 30008), tuple(82292, 28294), tuple(38892, 21915), tuple(51202, 50642), tuple(99865, 33343), - tuple(95804, 70967), tuple(12070, 43458), tuple(6220, 56015), tuple(74329, 56204), tuple(21399, 75467), tuple(88203, 43858), tuple(38704, 47055), tuple(19362, 60219), tuple(14634, 72618), tuple(41448, 6543), tuple(87651, 18534), tuple(34327, 75539), tuple(3541, 25818), tuple(20404, 80339), tuple(44944, 55964), tuple(45651, 49213), tuple(65846, 85412), tuple(51675, 22001), tuple(74879, 97711), tuple(32455, 26455), tuple(5056, 69437), tuple(46238, 41978), tuple(11156, 81627), tuple(40029, 70165), tuple(87185, 7409), tuple(53171, 90875), tuple(38789, 40512), tuple(61386, 8745), tuple(81736, 4812), tuple(71196, 92427), tuple(13554, 32085), tuple(41154, 59222), tuple(58043, 66198), tuple(21838, 40563), tuple(33897, 65211), tuple(28903, 30143), tuple(21409, 47493), tuple(26059, 95417), tuple(73900, 48368), tuple(18774, 30490), tuple(73012, 44125), tuple(58456, 61155), tuple(60554, 28996), tuple(8827, 36373), tuple(30404, 97981), tuple(30881, 46098), tuple(13258, 80907), tuple(72012, 13691), tuple(23651, 30968), tuple(2969, 50097), tuple(33733, 79191), tuple(10223, 90306), tuple(98149, 11670), tuple(45547, 25317), tuple(77139, 63654), tuple(55598, 73329), tuple(22887, 43966), tuple(73233, 39855), tuple(71344, 61217), tuple(4060, 79956), tuple(73925, 30838), tuple(7635, 14926), tuple(53804, 37839), tuple(69335, 59926), tuple(34807, 32895), tuple(78509, 73239), tuple(36494, 91580), tuple(66250, 40428), tuple(92495, 92721), tuple(13503, 8974), tuple(1533, 7446), tuple(84074, 88477), tuple(17188, 48916), tuple(88681, 88849), tuple(96162, 65641), tuple(71517, 35507), tuple(53803, 30244), tuple(54097, 55628), tuple(58528, 34100), tuple(15910, 46623), tuple(89311, 69918), tuple(34706, 33424), tuple(52525, 7174), tuple(36409, 22829), tuple(16575, 59807), tuple(26247, 36935), tuple(58999, 98875), tuple(73380, 90788), tuple(11800, 64690), tuple(71312, 64235), tuple(35708, 76049), tuple(65621, 59223), tuple(94220, 15399), tuple(32073, 46262), tuple(38017, 57787), tuple(85453, 57087), tuple(57354, 18076), tuple(15620, 9885), tuple(54585, 11470), tuple(86532, 11214), tuple(90392, 36339), tuple(41222, 63156), tuple(41956, 46957), tuple(11257, 59047), tuple(7904, 91165), tuple(33757, 89829), tuple(36561, 76484), tuple(24535, 70034), tuple(58932, 52035), tuple(44009, 86647), tuple(21710, 66730), tuple(19750, 19959), tuple(12488, 23120), tuple(3857, 92163), tuple(1605, 57740), tuple(9460, 45243), tuple(63737, 91447), tuple(97379, 73859), tuple(24109, 75972), tuple(88375, 99620), tuple(11175, 97854), tuple(78504, 60445), tuple(63669, 89304), tuple(37661, 27971), tuple(56797, 37186), tuple(49704, 37165), tuple(58568, 72097), tuple(66051, 28778), tuple(37075, 75947), tuple(38543, 51647), tuple(75077, 39030), tuple(77878, 37117), tuple(75140, 60743), tuple(18900, 6472), tuple(48578, 88913), tuple(26467, 92904), tuple(55006, 10313), tuple(20860, 923), tuple(96408, 80209), tuple(68515, 29809), tuple(11686, 36655), tuple(37673, 68001), tuple(71190, 98343), tuple(9866, 26660), tuple(52912, 14692), tuple(71503, 17364), tuple(22754, 80041), tuple(20097, 45934), tuple(85767, 40813), tuple(8326, 21813), tuple(71122, 42467), tuple(11149, 52952), tuple(99239, 73844), tuple(46925, 31154), tuple(4219, 6051), tuple(4349, 18054), tuple(57355, 88606), tuple(79627, 89547), tuple(35391, 52278), tuple(7921, 5095), tuple(84435, 58925), tuple(23209, 80993), tuple(6392, 64395), tuple(26778, 62624), tuple(4484, 76470), tuple(85595, 21682), tuple(43801, 57907), tuple(21360, 55981), tuple(90765, 19734), tuple(59965, 32734), tuple(21698, 54400), tuple(3202, 51301), tuple(50398, 27389), tuple(11195, 59086), tuple(80810, 68107), tuple(80246, 86109), tuple(8264, 67640), tuple(45129, 49779), tuple(3741, 39591), tuple(92596, 44524), tuple(61323, 7060), tuple(85650, 4974), tuple(57933, 64074), tuple(82675, 91822), tuple(88157, 69528), tuple(15295, 64351), tuple(38370, 6787), tuple(32886, 73349), tuple(64461, 91272), tuple(61885, 12074), tuple(66868, 69079), tuple(97050, 88063), tuple(17423, 65097), tuple(98155, 52111), tuple(30406, 53315), tuple(77111, 31673), tuple(88833, 29556), tuple(17834, 49818), tuple(93996, 77258), tuple(27859, 8227), tuple(50422, 99996), tuple(72852, 25549), tuple(50960, 30223), tuple(26847, 86597), tuple(57065, 75358), tuple(17500, 59618), tuple(71963, 7160), tuple(82261, 26132), tuple(57310, 33759), tuple(74850, 36130), tuple(80679, 12358), tuple(25146, 69695), tuple(51169, 36809), tuple(34345, 42666), tuple(25529, 79964), tuple(16957, 2056), tuple(11384, 73397), tuple(87339, 16821), tuple(36838, 38944), tuple(33609, 29887), tuple(81930, 27309), tuple(85427, 98289), tuple(56553, 34578), tuple(28022, 64719), tuple(48137, 50016), tuple(64006, 99858), tuple(96683, 2581), tuple(64215, 63294), tuple(80480, 59268), tuple(37552, 74897), tuple(68924, 33400), tuple(12320, 3485), tuple(46342, 47198), tuple(10486, 84214), tuple(10637, 37979), tuple(82102, 8674), tuple(45272, 18937), tuple(85813, 9828), tuple(42952, 81515), tuple(36109, 60929), tuple(49908, 81308), tuple(77480, 79720), tuple(47309, 93694), tuple(92133, 64187), tuple(59797, 76215), tuple(57715, 88948), tuple(34251, 75387), tuple(98032, 9443), tuple(82908, 50870), tuple(36667, 90760), tuple(11497, 70446), tuple(55263, 9996), tuple(95530, 15624), tuple(61374, 49255), tuple(47466, 11077), tuple(42810, 44265), tuple(78332, 84057), tuple(95033, 41926), tuple(24384, 51662), tuple(47128, 47040), tuple(14895, 37303), tuple(6995, 23571), tuple(30773, 73534), tuple(69169, 94373), tuple(29022, 92890), tuple(46660, 69109), tuple(39814, 75285), tuple(57314, 90534), tuple(63237, 59127), tuple(69470, 9152), tuple(71565, 81652), tuple(42371, 77809), tuple(66206, 24490), tuple(53101, 69390), tuple(56648, 69898), tuple(25813, 5835), tuple(82233, 2630), tuple(8386, 52693), tuple(85310, 85937), tuple(87403, 83920), tuple(290, 80083), tuple(9562, 46994), tuple(15806, 73988), tuple(54094, 2149), tuple(6324, 79545), tuple(97932, 65717), tuple(33267, 33685), tuple(49599, 23793), tuple(97471, 77574), tuple(36005, 7441), tuple(80654, 94174), tuple(63236, 7016), tuple(93694, 87168), tuple(14169, 63653), tuple(79452, 21186), tuple(6879, 93530), tuple(44992, 4491), tuple(55716, 78989), tuple(81229, 83954), tuple(63203, 84403), tuple(50575, 77597), tuple(97412, 87338), tuple(26386, 54156), tuple(35465, 54200), tuple(42993, 1096), tuple(19501, 87529), tuple(23563, 89524), tuple(85276, 24678), tuple(28839, 73007), tuple(54050, 80260), tuple(41862, 45747), tuple(45532, 4796), tuple(59766, 78513), tuple(42873, 27842), tuple(14066, 14788), tuple(42226, 19459), tuple(66168, 97420), tuple(73275, 78209), tuple(755, 75149), tuple(10424, 17589), tuple(59752, 5362), tuple(16612, 48631), tuple(97558, 55845), tuple(77602, 94389), tuple(17033, 91672), tuple(81010, 47646), tuple(8792, 74197), tuple(62409, 86387), tuple(23940, 73202), tuple(74326, 93461), tuple(99700, 28417), tuple(12373, 31471), tuple(19780, 82462), tuple(84100, 66414), tuple(33739, 27879), tuple(65869, 19590), tuple(53768, 80619), tuple(7632, 72890), tuple(13016, 2777), tuple(28709, 51472), tuple(64803, 48135), tuple(2016, 6998), tuple(32601, 51082), tuple(58866, 23304), tuple(87096, 82571), tuple(54567, 12251), tuple(60574, 64548), tuple(32946, 20929), tuple(95087, 91320), tuple(32702, 51680), tuple(71934, 62082), tuple(7598, 45620), tuple(25713, 83796), tuple(22509, 59083), tuple(99368, 66914), tuple(22928, 984), tuple(74309, 35226), tuple(38296, 93051), tuple(28362, 36277), tuple(81873, 9185), tuple(79122, 50838), tuple(31448, 35443), tuple(25046, 97283), tuple(51849, 76572), tuple(83722, 31233), tuple(75316, 70515), tuple(67359, 69686), tuple(80368, 87585), tuple(51714, 92098), tuple(5772, 72330), tuple(96652, 97645), tuple(10531, 86149), tuple(4378, 65728), tuple(80551, 93867), tuple(46660, 8809), tuple(83039, 80501), tuple(38249, 12848), tuple(48679, 54119), tuple(77295, 29795), tuple(65702, 69250), tuple(87195, 3131), tuple(26611, 29868), tuple(88164, 4187), tuple(42078, 63303), tuple(46501, 95311), tuple(7063, 38185), tuple(94346, 35287), tuple(75324, 16799), tuple(49000, 44453), tuple(309, 54608), tuple(16024, 77175), tuple(75300, 29332), tuple(45928, 81737), tuple(28049, 97890), tuple(87565, 86417), tuple(25253, 22620), tuple(77837, 33503), tuple(19857, 67728), tuple(6316, 72927), tuple(33001, 15626), tuple(79133, 25621), tuple(23468, 29945), tuple(10889, 13574), tuple(84771, 66067), tuple(22974, 59756), tuple(25169, 32080), tuple(2763, 69489), tuple(95845, 1960), tuple(8989, 53327), tuple(68781, 39147), tuple(90201, 88456), tuple(59526, 93326), tuple(52344, 75391), tuple(85184, 31369), tuple(94710, 93662), tuple(54889, 49149), tuple(29952, 55792), tuple(33265, 8947), tuple(44096, 26042), tuple(56712, 53763), tuple(55427, 14296), tuple(8731, 11564), tuple(62987, 83), tuple(53575, 58165), tuple(63627, 82316), tuple(50117, 30149), tuple(20026, 57072), tuple(83807, 29051), tuple(77431, 60892), tuple(89918, 41830), tuple(80530, 98080), tuple(98699, 33390), tuple(86743, 62505), tuple(22963, 23417), tuple(76581, 89370), tuple(3725, 51427), tuple(26360, 93305), tuple(65141, 27209), tuple(7445, 52852), tuple(56158, 4399), tuple(87204, 7475), tuple(82189, 11735), tuple(62875, 99644), tuple(94415, 59283), tuple(79059, 42636), tuple(27095, 24606), tuple(22228, 41626), tuple(15766, 33419), tuple(36090, 64191), tuple(33858, 28804), tuple(40697, 70965), tuple(1895, 48621), tuple(55078, 8926), tuple(84940, 99519), tuple(50750, 83936), tuple(63108, 59256), tuple(88855, 52935), tuple(62544, 38446), tuple(76256, 21365), tuple(86161, 70307), tuple(9247, 80836), tuple(91204, 88378), tuple(88362, 91302), tuple(69563, 58603), tuple(76583, 5971), tuple(59824, 38885), tuple(40338, 61292), tuple(99653, 64086), tuple(52198, 77598), tuple(19667, 6), tuple(30378, 41690), tuple(5130, 21465), tuple(32633, 80628), tuple(69869, 59610), tuple(30453, 13017), tuple(59836, 37885), tuple(58131, 42797), tuple(3428, 5984), tuple(17299, 13377), tuple(68491, 26265), - tuple(75600, 36301), tuple(79469, 65256), tuple(98438, 92022), tuple(40135, 69628), tuple(52373, 11613), tuple(12460, 96481), tuple(36740, 9873), tuple(92746, 96113), tuple(32532, 75810), tuple(45871, 37825), tuple(13323, 92457), tuple(11853, 716), tuple(59161, 91872), tuple(33609, 76950), tuple(50145, 9040), tuple(82818, 64780), tuple(92700, 31810), tuple(5745, 30311), tuple(19252, 34721), tuple(91678, 50602), tuple(48847, 98284), tuple(55640, 96281), tuple(60726, 42098), tuple(4494, 48219), tuple(61428, 76363), tuple(91534, 87454), tuple(73201, 9683), tuple(72379, 68604), tuple(30607, 71891), tuple(71963, 41250), tuple(17585, 79804), tuple(64529, 84544), tuple(44429, 46924), tuple(30658, 76431), tuple(18422, 23472), tuple(88171, 11830), tuple(66762, 7895), tuple(74476, 53181), tuple(15386, 51972), tuple(55536, 25039), tuple(68166, 15362), tuple(24986, 96477), tuple(88289, 76764), tuple(48193, 92905), tuple(95456, 25805), tuple(78131, 37308), tuple(32236, 81885), tuple(3083, 33218), tuple(29652, 41185), tuple(42417, 13339), tuple(71362, 33608), tuple(25475, 24287), tuple(20460, 82673), tuple(94769, 76462), tuple(12527, 82290), tuple(21183, 45096), tuple(20544, 30257), tuple(69721, 12886), tuple(4, 36149), tuple(23696, 29088), tuple(86144, 92852), tuple(66152, 22322), tuple(22307, 63335), tuple(25128, 35257), tuple(11046, 4279), tuple(32665, 25699), tuple(28784, 44818), tuple(59793, 40459), tuple(13846, 39333), tuple(59816, 7467), tuple(40106, 36228), tuple(13565, 33667), tuple(17482, 80138), tuple(37698, 95869), tuple(16491, 96398), tuple(53057, 48448), tuple(71592, 23507), tuple(98947, 62014), tuple(85328, 91232), tuple(94951, 11033), tuple(38509, 57252), tuple(70111, 63297), tuple(36914, 97310), tuple(86662, 26580), tuple(36754, 49831), tuple(35316, 42861), tuple(49326, 47462), tuple(47854, 49167), tuple(61206, 69078), tuple(67416, 3064), tuple(33631, 27193), tuple(56786, 41757), tuple(14000, 63627), tuple(98455, 72204), tuple(11796, 3153), tuple(49366, 6099), tuple(16114, 6729), tuple(59942, 41512), tuple(85308, 60723), tuple(60186, 8930), tuple(28450, 20028), tuple(54132, 40793), tuple(20190, 39153), tuple(91793, 59468), tuple(48855, 49249), tuple(3252, 88308), tuple(55024, 19544), tuple(47999, 98668), tuple(3625, 66538), tuple(27774, 17048), tuple(49182, 25298), tuple(10689, 72768), tuple(64034, 38635), tuple(23327, 65875), tuple(18376, 42416), tuple(15686, 35974), tuple(22283, 56738), tuple(14419, 36171), tuple(397, 11730), tuple(50136, 7806), tuple(37842, 47683), tuple(606, 5298), tuple(66652, 8471), tuple(23268, 88344), tuple(8181, 42348), tuple(30015, 82155), tuple(65670, 4409), tuple(29161, 1269), tuple(62267, 51181), tuple(38076, 3852), tuple(14048, 61904), tuple(6132, 3284), tuple(22486, 26772), tuple(76678, 2511), tuple(16212, 13228), tuple(51402, 46487), tuple(99360, 11669), tuple(36934, 58173), tuple(71427, 76232), tuple(14509, 22573), tuple(98403, 76905), tuple(24839, 32100), tuple(71097, 7056), tuple(47286, 70888), tuple(90192, 33568), tuple(38798, 54148), tuple(20350, 66499), tuple(93010, 48622), tuple(13510, 34716), tuple(92779, 14508), tuple(57010, 91250), tuple(25212, 10989), tuple(65459, 91132), - tuple(66251, 59146), tuple(28920, 29336), tuple(86273, 50897), tuple(81311, 53009), tuple(22278, 85352), tuple(54231, 26356), tuple(94250, 51117), tuple(21152, 38875), tuple(39286, 22339), tuple(10290, 37852), tuple(43426, 20385), tuple(13603, 2341), tuple(2314, 12288), tuple(48785, 80096), tuple(16762, 92809), tuple(1689, 16006), tuple(46828, 44433), tuple(58147, 68612), tuple(31702, 34921), tuple(23807, 72511), tuple(63437, 35834), tuple(40643, 62303), tuple(78198, 57748), tuple(11496, 43030), tuple(91150, 85333), tuple(91817, 95369), tuple(1792, 59975), tuple(14816, 41573), tuple(9355, 32450), tuple(77243, 5352), tuple(17681, 11128), tuple(69700, 89110), tuple(37695, 98303), tuple(23160, 29924), tuple(83829, 29122), tuple(13009, 81770), tuple(64481, 60303), tuple(6842, 78532), tuple(16282, 32405), tuple(14204, 67463), tuple(41795, 83324), tuple(98134, 38132), tuple(93686, 89418), tuple(83031, 18065), tuple(96431, 28312), tuple(15488, 90870), tuple(90914, 35142), tuple(96174, 98411), tuple(93075, 61533), tuple(47394, 30134), tuple(46309, 98014), tuple(8574, 38881), tuple(42376, 78153), tuple(45938, 95700), tuple(14026, 14059), tuple(94138, 70460), tuple(62882, 3321), tuple(52628, 25566), tuple(42145, 97919), tuple(57580, 8897), tuple(1766, 74887), tuple(68391, 51311), tuple(16485, 36095), tuple(55867, 988), tuple(34600, 46771), tuple(35786, 23210), tuple(78683, 62604), tuple(11002, 87427), tuple(29747, 6771), tuple(53803, 50526), tuple(52691, 75371), tuple(58028, 36198), tuple(98041, 28071), tuple(81822, 4121), tuple(90995, 93843), tuple(3709, 74313), tuple(23318, 28167), tuple(60162, 87145), tuple(53243, 31382), tuple(69762, 54059), tuple(25151, 95865), tuple(55550, 62947), tuple(45463, 8036), tuple(59949, 91675), tuple(55331, 79612), tuple(13806, 57517), tuple(41328, 94361), tuple(8388, 210), tuple(43458, 1327), tuple(16416, 50975), tuple(64370, 86524), tuple(97121, 65142), tuple(82878, 64545), tuple(58547, 33372), tuple(67494, 4130), tuple(59446, 79185), tuple(83812, 58833), tuple(32172, 93730), tuple(10095, 30319), tuple(94135, 92795), tuple(64893, 12870), tuple(14634, 70083), tuple(23344, 42836), tuple(19624, 29135), tuple(28755, 57738), tuple(58401, 49863), tuple(76809, 91787), tuple(51952, 33167), tuple(31717, 78538), tuple(64811, 55131), tuple(61969, 12339), tuple(4900, 48310), tuple(67609, 55369), tuple(37333, 37245), tuple(91492, 19704), tuple(77592, 62200), tuple(1017, 41815), tuple(41362, 13627), tuple(46703, 92040), tuple(4664, 46637), tuple(25430, 87837), tuple(10270, 32932), tuple(46025, 34792), tuple(80621, 38362), tuple(79949, 31518), tuple(77767, 59212), tuple(11080, 69948), tuple(17376, 92279), tuple(73871, 86715), tuple(83524, 21178), tuple(94368, 61971), tuple(11262, 30062), tuple(12840, 52656), tuple(15300, 64176), tuple(70094, 42565), tuple(84503, 66968), tuple(65824, 63349), tuple(74306, 82791), tuple(85701, 1655), tuple(25190, 70557), tuple(82208, 58145), tuple(97669, 92489), tuple(62730, 52924), tuple(46970, 15748), tuple(71217, 57505), tuple(57336, 87380), tuple(13982, 71979), tuple(55899, 1192), tuple(6890, 69297), tuple(39849, 72896), tuple(60213, 22499), tuple(53820, 79877), tuple(61389, 24954), tuple(1021, 81047), tuple(95389, 90605), tuple(8590, 26855), tuple(92881, 46825), tuple(59567, 91455), tuple(87297, 1567), tuple(86333, 82913), tuple(16285, 32370), tuple(38900, 77251), tuple(96301, 92302), tuple(2799, 91579), tuple(59757, 56681), tuple(49229, 38404), tuple(16880, 51290), tuple(38700, 80456), tuple(51440, 32556), tuple(4507, 83325), tuple(29924, 40923), tuple(9937, 58356), tuple(70895, 52611), tuple(80488, 49196), tuple(93118, 6112), tuple(44058, 20923), tuple(70808, 72928), tuple(78269, 76351), tuple(81081, 63186), tuple(12406, 50247), tuple(91769, 37875), tuple(36197, 11434), tuple(72225, 93141), tuple(59295, 54160), tuple(39342, 10117), tuple(12920, 23240), tuple(91877, 61524), tuple(84807, 5431), tuple(63163, 52152), tuple(90320, 97456), tuple(14899, 12941), tuple(90079, 98004), tuple(46096, 68208), tuple(12176, 72405), tuple(28451, 61964), tuple(89254, 1676), tuple(76242, 5438), tuple(29756, 92658), tuple(14855, 41944), tuple(1519, 65495), tuple(74052, 65006), tuple(1075, 31521), tuple(84877, 98988), tuple(91426, 69442), tuple(39694, 43391), tuple(68328, 3447), tuple(50508, 61610), tuple(30890, 18792), tuple(60706, 87518), tuple(6692, 83260), tuple(92044, 79399), tuple(86846, 53543), tuple(28606, 61656), tuple(35449, 52965), tuple(43421, 13117), tuple(67404, 55238), tuple(80817, 79927), tuple(8908, 46083), tuple(22583, 19705), tuple(13, 25509), tuple(79771, 21210), tuple(42719, 19320), tuple(94091, 35389), tuple(95995, 42652), tuple(6417, 13464), tuple(22290, 34089), tuple(27119, 33408), tuple(7, 68019), tuple(55405, 50766), tuple(6331, 50009), tuple(33, 48235), tuple(3162, 85313), tuple(25897, 91775), tuple(30813, 83408), tuple(36062, 46449), tuple(78820, 78894), tuple(92054, 23008), tuple(10637, 4237), tuple(90702, 62678), tuple(80427, 98641), tuple(20834, 48068), tuple(74443, 29196), tuple(76088, 91823), tuple(39151, 48574), tuple(54050, 5374), tuple(60643, 1979), tuple(4535, 95086), tuple(82101, 67586), tuple(55746, 92488), tuple(88192, 22615), tuple(93282, 87547), tuple(37804, 61180), tuple(13445, 56654), tuple(38913, 10130), tuple(23472, 83441), tuple(94125, 56393), tuple(2967, 25078), tuple(76652, 84599), tuple(3586, 83503), tuple(46272, 62119), tuple(26002, 55790), tuple(75085, 55875), tuple(73786, 22608), tuple(63101, 59758), tuple(9688, 11910), tuple(54041, 38376), tuple(75595, 95722), tuple(36047, 59518), tuple(55041, 69102), tuple(42836, 48225), tuple(30046, 61157), tuple(60608, 94951), tuple(80257, 70300), tuple(40282, 67847), tuple(90735, 56299), tuple(82814, 89675), tuple(2998, 52357), tuple(86333, 5088), tuple(3414, 67855), tuple(28134, 64041), tuple(56871, 38384), tuple(51335, 28574), tuple(73451, 40395), tuple(59696, 49729), tuple(73379, 78187), tuple(67561, 79682), tuple(66114, 9153), tuple(4613, 41137), tuple(96832, 77637), tuple(64216, 22050), tuple(69871, 69883), tuple(78030, 8142), tuple(88000, 98796), tuple(24965, 60211), tuple(61727, 82326), tuple(64623, 21387), tuple(27307, 39907), tuple(47314, 29509), tuple(14676, 73902), tuple(48864, 28948), tuple(15974, 66359), tuple(68154, 2013), tuple(44016, 51609), tuple(95421, 89481), tuple(39985, 22127), tuple(88096, 18483), tuple(18539, 98797), tuple(83613, 26308), tuple(93997, 84284), tuple(35958, 46289), tuple(79263, 50800), tuple(4873, 92142), tuple(69509, 97567), tuple(17733, 9050), tuple(14632, 79901), tuple(94211, 83353), tuple(72112, 79110), tuple(13974, 83858), tuple(76735, 38529), tuple(67084, 67183), tuple(97827, 95135), tuple(55771, 88062), tuple(94630, 45565), tuple(20922, 85088), tuple(9180, 39703), tuple(86146, 21086), tuple(65955, 51015), tuple(77660, 57572), tuple(31577, 56429), tuple(52636, 56363), tuple(74365, 62124), tuple(60971, 13103), tuple(75234, 35529), tuple(26035, 39091), tuple(1145, 38641), tuple(24088, 49898), tuple(83610, 25448), tuple(8868, 32413), tuple(20877, 9306), tuple(40824, 73012), tuple(63105, 93113), tuple(21170, 21234), tuple(6564, 21210), tuple(27020, 39734), tuple(7431, 11643), tuple(56609, 23596), tuple(2023, 39429), tuple(63574, 88918), tuple(5188, 46658), tuple(3290, 24577), tuple(33271, 95049), tuple(72950, 94811), tuple(74876, 73210), tuple(6294, 42155), tuple(13767, 27366), tuple(86746, 54073), tuple(13001, 93376), tuple(13517, 96879), tuple(24380, 59684), tuple(49105, 55403), tuple(90533, 2745), tuple(48651, 23491), tuple(21409, 71113), tuple(7142, 71273), tuple(62952, 88544), tuple(82865, 10938), tuple(65089, 17672), tuple(43173, 47506), tuple(13708, 32610), tuple(26547, 77317), tuple(8676, 7117), tuple(62512, 39512), tuple(42233, 81433), tuple(45416, 63643), tuple(32392, 33632), tuple(83887, 402), tuple(53596, 54897), tuple(59707, 85005), tuple(96343, 20979), tuple(30344, 65226), tuple(34513, 24150), tuple(38715, 11205), tuple(20649, 7679), tuple(27948, 80080), tuple(77430, 49123), tuple(42437, 4729), tuple(90176, 34827), tuple(11039, 59573), tuple(47042, 38460), tuple(48086, 34987), tuple(65457, 5585), tuple(45956, 6198), tuple(22786, 24937), tuple(54287, 77515), tuple(12113, 84710), tuple(29543, 36869), tuple(8388, 60473), tuple(2771, 53359), tuple(35299, 64874), tuple(34132, 86266), tuple(37594, 64297), tuple(33390, 65843), tuple(53057, 46715), tuple(41874, 83250), tuple(26061, 30352), tuple(93351, 16651), tuple(25890, 16570), tuple(67403, 19546), tuple(8608, 51991), tuple(90616, 10018), tuple(46870, 54345), tuple(81919, 3577), tuple(99462, 3562), tuple(83193, 66098), tuple(92054, 70338), tuple(11383, 90149), tuple(42458, 2302), tuple(77850, 47281), tuple(62433, 4959), tuple(52392, 33711), tuple(2033, 4096), tuple(91460, 6795), tuple(27462, 76972), tuple(73087, 40511), tuple(42563, 5924), tuple(21819, 53017), tuple(31309, 62793), tuple(19798, 60725), tuple(90987, 77178), tuple(9949, 43079), tuple(13773, 40312), tuple(97080, 61184), tuple(59679, 78611), tuple(22361, 83227), tuple(70120, 95483), tuple(15529, 5672), tuple(25893, 99672), tuple(61722, 69201), tuple(55012, 37509), tuple(77999, 85866), tuple(77835, 83627), tuple(36792, 80218), tuple(54232, 58138), tuple(74593, 485), tuple(45698, 4153), tuple(96513, 738), tuple(33060, 53078), tuple(4439, 25097), tuple(62007, 64140), tuple(86128, 99499), tuple(66141, 4433), tuple(41032, 54527), tuple(59831, 61170), tuple(1189, 68117), tuple(63914, 26798), tuple(72045, 2999), tuple(6016, 42337), tuple(40611, 39747), tuple(88328, 44836), tuple(88939, 25365), tuple(40010, 43971), tuple(21419, 31975), tuple(27725, 21929), tuple(49459, 74763), tuple(32059, 96546), tuple(70985, 98967), tuple(33034, 36073), tuple(19600, 80524), tuple(59150, 56734), tuple(30433, 38795), tuple(55387, 47581), tuple(45109, 73833), tuple(59487, 5428), tuple(23140, 57618), tuple(36367, 31008), tuple(45983, 85518), tuple(28234, 83843), tuple(73005, 5096), tuple(65602, 83431), tuple(97198, 10971), tuple(4873, 51104), - tuple(49700, 89396), tuple(18900, 20447), tuple(76400, 68088), tuple(28055, 59547), tuple(94737, 96052), tuple(57933, 96132), tuple(26945, 6293), tuple(18559, 1317), tuple(66327, 78332), tuple(47194, 67979), tuple(78039, 58735), tuple(26666, 96144), tuple(91574, 52530), tuple(3216, 43850), tuple(48367, 78987), tuple(78296, 89800), tuple(25015, 1497), tuple(70292, 37629), tuple(39657, 59240), tuple(22576, 93232), tuple(70102, 98407), tuple(35051, 5762), tuple(13700, 82540), tuple(36857, 7528), tuple(84536, 86107), tuple(1258, 90702), tuple(9152, 1127), tuple(3136, 57298), tuple(73203, 93120), tuple(61897, 32903), tuple(20284, 49358), tuple(49444, 65481), tuple(91260, 84792), tuple(5360, 37164), tuple(79212, 26499), tuple(59456, 37025), tuple(67953, 53566), tuple(50253, 71742), tuple(68181, 90923), tuple(88522, 71071), tuple(89615, 46115), tuple(31661, 17673), tuple(37699, 90994), tuple(41974, 9481), tuple(51974, 46364), tuple(47470, 75958), tuple(38427, 78267), tuple(90226, 16121), tuple(33257, 3088), tuple(73044, 79701), tuple(14149, 36868), tuple(6421, 63653), tuple(35772, 56493), tuple(46665, 83651), tuple(82299, 56268), tuple(77397, 71795), tuple(15180, 87135), tuple(73936, 38858), tuple(27060, 14829), tuple(85847, 76756), tuple(63068, 61864), tuple(92040, 43753), tuple(53882, 60715), tuple(12648, 18276), tuple(17873, 94081), tuple(55982, 24983), tuple(52686, 32111), tuple(22555, 13144), tuple(37190, 15039), tuple(47304, 86179), tuple(56214, 83657), tuple(53838, 78111), tuple(80530, 1686), tuple(2976, 26711), tuple(55713, 3487), tuple(21157, 5923), tuple(74613, 76555), tuple(61736, 7322), tuple(99017, 13779), tuple(64133, 61706), tuple(8276, 97269), tuple(99641, 87447), tuple(26292, 62924), tuple(96267, 37877), tuple(98635, 88685), tuple(65598, 68210), tuple(34885, 1063), tuple(42880, 15694), tuple(4058, 72666), tuple(1319, 91582), tuple(41749, 822), tuple(82490, 42783), tuple(17489, 17617), tuple(81109, 16778), tuple(86262, 53756), tuple(15547, 21144), tuple(89799, 70465), tuple(99358, 87334), tuple(65501, 81932), tuple(30351, 64230), tuple(55974, 46985), tuple(24343, 93628), tuple(4624, 56038), tuple(15183, 38765), tuple(21562, 5240), tuple(36215, 51533), tuple(49649, 74392), tuple(7607, 91204), tuple(86233, 85341), tuple(35494, 44178), tuple(78808, 11896), tuple(45446, 12897), tuple(4810, 20096), tuple(3643, 10550), tuple(12022, 14391), tuple(99014, 87891), tuple(67214, 29437), tuple(7015, 54810), tuple(84991, 75251), tuple(45526, 82687), tuple(43608, 89083), tuple(96191, 12911), tuple(36902, 50415), tuple(90548, 66839), tuple(54007, 5480), tuple(57191, 39709), tuple(73364, 73556), tuple(12671, 64072), tuple(5047, 12887), tuple(22326, 88360), tuple(59259, 63167), tuple(39237, 9311), tuple(47043, 11512), tuple(32399, 22634), tuple(89485, 95279), tuple(30489, 43327), tuple(70966, 18184), tuple(28514, 12686), tuple(1717, 89938), tuple(14871, 76911), tuple(47862, 97093), tuple(97572, 77612), tuple(88600, 36501), tuple(9240, 96264), tuple(96629, 98292), tuple(4838, 72694), tuple(52835, 73039), tuple(63836, 26228), tuple(26035, 74526), tuple(23617, 6104), tuple(6243, 54603), tuple(20038, 81195), tuple(56789, 96284), - tuple(97009, 18989), tuple(36765, 94831), tuple(42002, 40820), tuple(31838, 16186), tuple(91127, 70783), tuple(1684, 38076), tuple(40928, 41294), tuple(97107, 76517), tuple(1797, 24093), tuple(99175, 29445), tuple(5788, 64502), tuple(66972, 23772), tuple(97394, 95256), tuple(26705, 92412), tuple(10676, 6686), tuple(28464, 74051), tuple(17598, 51675), tuple(76449, 50050), tuple(98812, 59818), tuple(92058, 6625), tuple(49804, 99637), tuple(28545, 32790), tuple(72167, 93636), tuple(29012, 95623), tuple(58249, 54541), tuple(98926, 71489), tuple(75062, 9546), tuple(47555, 5802), tuple(61606, 30979), tuple(10467, 46108), tuple(30491, 52750), tuple(52934, 38533), tuple(54616, 37276), tuple(55118, 2330), tuple(60402, 14806), tuple(25730, 88759), tuple(5209, 30946), tuple(92017, 41987), tuple(59747, 62415), tuple(6306, 53297), tuple(35770, 31254), tuple(13106, 99865), tuple(63519, 41637), tuple(7416, 68154), tuple(70874, 95983), tuple(20074, 2173), tuple(33740, 86880), tuple(45020, 65869), tuple(7302, 53494), tuple(98814, 2118), tuple(88287, 80646), tuple(18771, 30449), tuple(44557, 37984), tuple(45415, 36032), tuple(36584, 58485), tuple(57843, 52354), tuple(85647, 12150), tuple(33990, 89380), tuple(98597, 41936), tuple(19427, 53990), tuple(1457, 69269), tuple(68438, 14471), tuple(67175, 16881), tuple(82982, 78546), tuple(47661, 36294), tuple(63229, 48108), tuple(34390, 76095), tuple(32101, 57268), tuple(25322, 3312), tuple(17037, 2900), tuple(14342, 70149), tuple(10518, 1377), tuple(13553, 89073), tuple(97084, 80365), tuple(48340, 87192), tuple(84969, 21195), tuple(1031, 72944), tuple(78322, 68583), tuple(67238, 48374), tuple(77366, 55850), tuple(4383, 46759), tuple(51088, 76488), tuple(92049, 55847), tuple(32750, 16422), tuple(51788, 53619), tuple(56976, 88951), tuple(73323, 78435), tuple(3489, 15945), tuple(84231, 22302), tuple(25695, 881), tuple(19391, 11660), tuple(92578, 78349), tuple(52677, 17339), tuple(99132, 35975), tuple(25089, 70476), tuple(34144, 66544), tuple(59589, 59839), tuple(47333, 87623), tuple(4200, 6542), tuple(91781, 77813), tuple(77419, 17342), tuple(388, 90753), tuple(43401, 7351), tuple(81829, 74989), tuple(22381, 95695), tuple(90302, 61687), tuple(13534, 54534), tuple(43527, 47703), tuple(47856, 1186), tuple(10112, 16747), tuple(9508, 26179), tuple(96536, 57565), tuple(91052, 48922), tuple(38575, 45523), tuple(11602, 85317), tuple(22595, 21610), tuple(97849, 15638), tuple(95060, 20111), tuple(96011, 27148), tuple(37596, 10426), tuple(68667, 49057), tuple(68657, 90882), tuple(52704, 33574), tuple(54727, 59704), tuple(15853, 48283), tuple(47021, 60021), tuple(96208, 11440), tuple(6068, 49421), tuple(50363, 17684), tuple(53218, 19), tuple(82568, 3816), tuple(51, 7612), tuple(7073, 59063), tuple(92318, 38587), tuple(72388, 64952), tuple(33318, 5594), tuple(72806, 18573), tuple(1175, 88747), tuple(67044, 86039), tuple(17376, 41265), tuple(26356, 89524), tuple(55678, 15627), tuple(61156, 15723), tuple(43239, 59010), tuple(29054, 15261), tuple(57669, 21395), tuple(68469, 14849), tuple(92654, 69664), tuple(23975, 20021), tuple(73688, 12134), tuple(58053, 23849), tuple(56234, 18559), tuple(25051, 68957), tuple(15181, 91615), tuple(87633, 31040), tuple(17494, 78515), tuple(71971, 60923), tuple(44170, 21724), tuple(7088, 72107), tuple(12924, 47933), tuple(98139, 6819), tuple(57842, 2906), tuple(90278, 3534), tuple(61695, 82885), tuple(58831, 15129), tuple(32033, 34634), tuple(63477, 31357), tuple(29077, 28166), tuple(86531, 89574), tuple(46943, 79113), tuple(15667, 63523), tuple(65818, 93225), tuple(83392, 50497), tuple(7157, 88909), tuple(95101, 17111), tuple(8981, 35791), tuple(28328, 82722), tuple(70215, 66862), tuple(23442, 39128), tuple(21218, 27773), tuple(76215, 39265), tuple(56858, 62397), tuple(3610, 8766), tuple(7423, 48886), tuple(12235, 88792), tuple(44839, 55196), tuple(4896, 10080), tuple(87090, 73997), tuple(56745, 71875), tuple(58478, 57472), tuple(79423, 8771), tuple(76202, 95733), tuple(81635, 48857), tuple(82519, 5321), tuple(48756, 37198), tuple(20101, 61177), tuple(79047, 81682), tuple(23287, 88578), tuple(50984, 80946), tuple(855, 49074), tuple(11452, 56655), tuple(6289, 8916), tuple(33570, 636), tuple(28703, 80592), tuple(11820, 92299), tuple(75590, 80188), tuple(91365, 2787), tuple(22963, 43710), tuple(3996, 48007), tuple(59047, 96418), tuple(96710, 52447), tuple(65677, 99927), tuple(60727, 15115), tuple(8161, 65729), tuple(75253, 45731), tuple(88130, 79264), tuple(52087, 32657), tuple(89678, 22834), tuple(49244, 36835), tuple(74032, 5096), tuple(94142, 39297), tuple(51061, 21311), tuple(59391, 43458), tuple(90918, 95081), tuple(6058, 37018), tuple(29917, 54214), tuple(65986, 5543), tuple(99777, 69683), tuple(9447, 11273), tuple(54873, 47138), tuple(1306, 68306), tuple(15316, 55995), tuple(69280, 73432), tuple(63054, 28502), tuple(67161, 11333), tuple(40800, 26415), tuple(72350, 50225), tuple(7232, 67500), tuple(60631, 40338), tuple(96765, 51671), tuple(29375, 11235), tuple(52707, 93742), tuple(1317, 55926), tuple(51844, 29351), tuple(35754, 16733), tuple(23014, 56950), tuple(33119, 47531), tuple(27806, 39940), tuple(20119, 73119), tuple(18458, 45982), tuple(16605, 28759), tuple(43488, 86201), tuple(43560, 82883), tuple(85085, 1977), tuple(7129, 64077), tuple(36070, 80679), tuple(78638, 97212), tuple(71251, 5268), tuple(33610, 63341), tuple(16722, 15882), tuple(53876, 11734), tuple(35855, 14481), tuple(55072, 7889), tuple(37213, 73390), tuple(28899, 55948), tuple(51996, 77777), tuple(26621, 51914), tuple(44492, 36420), tuple(36437, 24452), tuple(158, 77935), tuple(3730, 7692), tuple(81372, 29050), tuple(29491, 3912), tuple(57098, 73393), tuple(55630, 53165), tuple(79003, 19421), tuple(47306, 40128), tuple(96332, 49995), tuple(54107, 83940), tuple(11203, 76678), tuple(53854, 284), tuple(96087, 22398), tuple(25481, 69301), tuple(2770, 77586), tuple(53004, 44915), tuple(56264, 41165), tuple(12335, 22090), tuple(47188, 40678), tuple(41823, 71805), tuple(81810, 47825), tuple(98892, 50696), tuple(74323, 93133), tuple(82867, 40800), tuple(28884, 29276), tuple(55697, 20888), tuple(96901, 37428), tuple(95509, 78780), tuple(6443, 65188), tuple(17669, 43924), tuple(29933, 97244), tuple(1024, 71265), tuple(47229, 99610), tuple(72546, 40811), tuple(43407, 41566), tuple(44692, 53743), tuple(76823, 95950), tuple(2509, 57091), tuple(85077, 96439), tuple(87650, 87716), tuple(3822, 84513), tuple(90864, 75439), tuple(22043, 21770), tuple(98371, 75482), tuple(63823, 48100), tuple(29999, 40875), tuple(97696, 33865), tuple(50880, 16402), tuple(21412, 70936), tuple(7150, 85970), tuple(73104, 57073), tuple(40099, 89138), tuple(6430, 36791), tuple(47677, 23485), tuple(21073, 86912), tuple(65018, 6092), tuple(89932, 58178), tuple(1264, 51681), tuple(42044, 40135), tuple(21774, 12190), tuple(13844, 87188), tuple(25807, 89548), tuple(74682, 31603), tuple(18482, 41232), tuple(906, 21841), tuple(10897, 39238), tuple(62134, 6083), tuple(80871, 66850), tuple(49659, 28587), tuple(61260, 22769), tuple(53795, 66637), tuple(89211, 32453), tuple(4271, 69472), tuple(1082, 32636), tuple(62012, 47870), tuple(7376, 77166), tuple(13150, 73275), tuple(66410, 85959), tuple(19018, 57311), tuple(30561, 58838), tuple(19981, 32087), tuple(91580, 42990), tuple(77139, 23774), tuple(10055, 99890), tuple(92160, 54537), tuple(75088, 70397), tuple(2361, 61066), tuple(81368, 40411), tuple(84946, 42564), tuple(41591, 42929), tuple(50399, 26116), tuple(51504, 63772), tuple(99828, 69531), tuple(19977, 17806), tuple(71259, 76567), tuple(78606, 25105), tuple(8859, 46469), tuple(19542, 73988), tuple(49804, 17349), tuple(65360, 96632), tuple(57740, 30812), tuple(9258, 26185), tuple(90573, 4815), tuple(39397, 10229), tuple(35300, 81687), tuple(5681, 22828), tuple(6274, 38340), tuple(44281, 67912), tuple(38385, 26507), tuple(95324, 84765), tuple(39288, 79779), tuple(4513, 28158), tuple(46060, 40293), tuple(70348, 87460), tuple(67754, 19569), tuple(70481, 94407), tuple(67636, 69316), tuple(14167, 81744), tuple(18928, 29706), tuple(32034, 45677), tuple(19801, 5902), tuple(42401, 62712), tuple(35220, 22823), tuple(33690, 22046), tuple(91281, 55953), tuple(4197, 96279), tuple(32901, 27074), tuple(35861, 5478), tuple(99166, 36251), tuple(70898, 80994), tuple(87491, 52808), tuple(59681, 30266), tuple(2944, 18646), tuple(77874, 7536), tuple(24534, 5416), tuple(90962, 47145), tuple(95367, 34461), tuple(10075, 74120), tuple(74276, 1837), tuple(38399, 63918), tuple(10526, 42662), tuple(33001, 37773), tuple(13642, 25741), tuple(5373, 82728), tuple(9830, 54625), tuple(13, 18937), tuple(90113, 52300), tuple(20973, 89640), tuple(44930, 1598), tuple(8100, 47745), tuple(80659, 78877), tuple(74154, 33417), tuple(18691, 37573), tuple(12477, 76628), tuple(73018, 89583), tuple(59029, 24879), tuple(85237, 31017), tuple(27701, 11025), tuple(24010, 76942), tuple(15627, 25276), tuple(89050, 16118), tuple(19776, 19318), tuple(24620, 1487), tuple(50157, 95158), tuple(53030, 72020), tuple(85571, 10817), tuple(29196, 92799), tuple(63501, 22445), tuple(26241, 54318), tuple(33769, 80112), tuple(25312, 50574), tuple(70583, 56662), tuple(74366, 82575), tuple(48461, 80545), tuple(18834, 39444), tuple(36597, 42715), tuple(86547, 38453), tuple(41067, 60718), tuple(33350, 25404), tuple(5532, 41438), tuple(45918, 84038), tuple(39040, 63724), tuple(37629, 66002), tuple(58037, 36196), tuple(21400, 17330), tuple(46491, 22602), tuple(88057, 89249), tuple(34188, 35948), tuple(8555, 42479), tuple(32961, 76606), tuple(22395, 93711), tuple(8058, 19591), tuple(72063, 64136), tuple(83360, 12607), tuple(20671, 78285), tuple(51887, 65373), tuple(71966, 39341), tuple(55953, 99780), tuple(56205, 98138), tuple(45986, 44708), tuple(62541, 28259), tuple(49450, 12641), tuple(5192, 36601), tuple(55295, 44352), tuple(83762, 4343), tuple(96203, 8870), tuple(21786, 73828), tuple(88830, 43827), tuple(96669, 84761), tuple(53061, 50061), tuple(52150, 47463), tuple(32090, 17570), - tuple(71495, 45027), tuple(30215, 23717), tuple(37599, 19789), tuple(85208, 48465), tuple(83599, 78596), tuple(82141, 37105), tuple(74943, 37580), tuple(10322, 11520), tuple(550, 55062), tuple(80506, 57713), tuple(55489, 80453), tuple(64174, 49274), tuple(75521, 51005), tuple(26033, 58801), tuple(78661, 50218), tuple(31043, 17090), tuple(70612, 88736), tuple(42991, 49396), tuple(86331, 2300), tuple(66156, 6331), tuple(34534, 78649), tuple(9828, 40937), tuple(35852, 46533), tuple(68435, 12070), tuple(26443, 75772), tuple(50802, 16400), tuple(69413, 89875), tuple(2372, 97252), tuple(88334, 76754), tuple(29504, 25950), tuple(10792, 12935), tuple(3655, 90667), tuple(35951, 16244), tuple(73312, 82435), tuple(60914, 58748), tuple(89407, 86955), tuple(47210, 84195), tuple(77084, 4213), tuple(15419, 5719), tuple(12483, 39092), tuple(61890, 83360), tuple(94785, 37525), tuple(69219, 83790), tuple(79946, 42672), tuple(36898, 9693), tuple(10103, 82234), tuple(71679, 18297), tuple(88171, 43635), tuple(94828, 71283), tuple(76485, 56217), tuple(70048, 92642), tuple(12567, 8082), tuple(10011, 76160), tuple(31060, 30787), tuple(199, 65728), tuple(371, 74295), tuple(25505, 76411), tuple(70522, 8776), tuple(58390, 48835), tuple(88649, 10695), tuple(21754, 5143), tuple(62512, 77235), tuple(837, 57129), tuple(86979, 33448), tuple(21219, 78593), tuple(10048, 22504), tuple(82658, 80394), tuple(79451, 81707), tuple(5940, 72284), tuple(1555, 70284), tuple(65988, 40439), tuple(48165, 92837), tuple(33680, 39653), tuple(96852, 38612), tuple(72548, 82912), tuple(78383, 68910), tuple(69985, 12164), tuple(24334, 14856), tuple(27180, 67558), tuple(62131, 29120), tuple(28744, 40472), tuple(79896, 25579), tuple(7172, 42815), tuple(81070, 85827), tuple(76227, 51604), tuple(70056, 61128), tuple(58609, 12641), tuple(30207, 33772), tuple(7940, 82930), tuple(45176, 28620), tuple(43176, 9513), tuple(55515, 5743), tuple(92762, 34063), tuple(26540, 95690), tuple(69518, 13909), tuple(24403, 65731), tuple(41317, 12732), tuple(78020, 50738), tuple(64622, 2001), tuple(43604, 83548), tuple(46739, 79104), tuple(57132, 2737), tuple(16823, 37703), tuple(59239, 68217), tuple(49630, 75156), tuple(19535, 63429), tuple(19685, 63032), tuple(93214, 97701), tuple(21809, 44089), tuple(42700, 64026), tuple(37310, 12041), tuple(84416, 39080), tuple(70589, 59876), tuple(14692, 82526), tuple(36230, 38375), tuple(7345, 48208), tuple(65006, 85251), tuple(31754, 26359), tuple(65198, 7876), tuple(38917, 16682), tuple(39527, 24043), tuple(28543, 48728), tuple(38065, 98259), tuple(57509, 71565), tuple(7706, 85866), tuple(13059, 22966), tuple(91722, 83066), tuple(45269, 68810), tuple(82194, 85898), tuple(78369, 11390), tuple(79637, 70777), tuple(87544, 74018), tuple(73661, 4252), tuple(98305, 21715), tuple(19375, 35375), tuple(28324, 48444), tuple(97548, 89589), tuple(73031, 60766), tuple(34818, 1986), tuple(6206, 26488), tuple(67681, 6380), tuple(98946, 84665), tuple(98568, 37447), tuple(48649, 89141), tuple(72561, 2077), tuple(47055, 63199), tuple(3956, 93341), tuple(69392, 51121), tuple(71277, 40181), tuple(8509, 18405), tuple(77921, 54923), tuple(92137, 60652), tuple(30271, 32980), - tuple(60001, 93265), tuple(69240, 45514), tuple(57985, 21681), tuple(35096, 6101), tuple(49506, 76284), tuple(57253, 21325), tuple(98280, 15826), tuple(1605, 80735), tuple(74930, 30151), tuple(58035, 93534), tuple(17384, 31491), tuple(10971, 7060), tuple(73339, 68186), tuple(88781, 52103), tuple(11265, 46720), tuple(5115, 30293), tuple(75078, 87304), tuple(43439, 10072), tuple(27578, 33361), tuple(90981, 10158), tuple(24465, 57967), tuple(49938, 61510), tuple(55637, 44188), tuple(93963, 32210), tuple(68414, 40035), tuple(77261, 88709), tuple(55900, 34360), tuple(39675, 19672), tuple(66218, 19215), tuple(74660, 74886), tuple(82862, 19255), tuple(78221, 82950), tuple(71132, 13770), tuple(3866, 56030), tuple(78929, 78110), tuple(48142, 5965), tuple(80128, 31846), tuple(72670, 2531), tuple(14391, 67079), tuple(60542, 10720), tuple(91632, 40402), tuple(607, 14720), tuple(49500, 13967), tuple(70778, 83672), tuple(46041, 37708), tuple(83720, 574), tuple(57212, 80758), tuple(28854, 46858), tuple(39588, 19052), tuple(50803, 71521), tuple(66830, 71799), tuple(21002, 61070), tuple(96634, 39051), tuple(4459, 26782), tuple(63650, 82860), tuple(24099, 69429), tuple(80492, 18147), tuple(41087, 995), tuple(41768, 79625), tuple(48190, 75710), tuple(43300, 78132), tuple(53778, 79772), tuple(30657, 89149), tuple(75840, 5676), tuple(75810, 52995), tuple(36563, 23231), tuple(25721, 55136), tuple(15, 23168), tuple(63731, 64535), tuple(67136, 4648), tuple(25464, 28946), tuple(95215, 98695), tuple(64093, 28899), tuple(62809, 40760), tuple(44188, 52915), tuple(82358, 98065), tuple(24244, 56401), tuple(61636, 63848), tuple(95428, 93970), tuple(93539, 88332), tuple(57401, 68773), tuple(53502, 33067), tuple(3147, 65487), tuple(75811, 11287), tuple(58826, 7349), tuple(57494, 66861), tuple(99762, 33031), tuple(68299, 20951), tuple(26524, 96915), tuple(14698, 22392), tuple(55683, 85344), tuple(25084, 10755), tuple(52343, 73138), tuple(99839, 61148), tuple(33383, 66190), tuple(18810, 10835), tuple(918, 55695), tuple(2664, 46566), tuple(93105, 21138), tuple(27088, 16730), tuple(70156, 4974), tuple(10294, 62096), tuple(30808, 18639), tuple(25091, 43231), tuple(17920, 90076), tuple(40104, 54257), tuple(98729, 93113), tuple(92419, 16006), tuple(14446, 8769), tuple(24875, 34761), tuple(99525, 34543), tuple(67461, 47601), tuple(91998, 5899), tuple(19623, 58521), tuple(97278, 63650), tuple(8396, 20069), tuple(29854, 22735), tuple(33695, 25257), tuple(70338, 3989), tuple(14296, 96530), tuple(13790, 1350), tuple(33144, 55649), tuple(29072, 26337), tuple(68611, 7960), tuple(81765, 15614), tuple(16633, 80649), tuple(89942, 31234), tuple(50827, 73426), tuple(2282, 12998), tuple(8566, 65109), tuple(65979, 23638), tuple(24802, 56043), tuple(15638, 54100), tuple(51431, 80669), tuple(30744, 9349), tuple(80148, 87322), tuple(49673, 22775), tuple(2096, 72107), tuple(29876, 11845), tuple(99687, 4215), tuple(68797, 41891), tuple(11363, 79333), tuple(22351, 68211), tuple(41273, 34990), tuple(11414, 58625), tuple(83047, 28825), tuple(44579, 31219), tuple(8753, 35994), tuple(8321, 20399), tuple(97524, 48382), tuple(81485, 69822), tuple(38382, 42853), tuple(97045, 81294), tuple(95109, 9825), tuple(24648, 92398), tuple(23245, 28557), tuple(9114, 30750), tuple(66151, 5307), tuple(28704, 42997), tuple(76185, 18506), tuple(39182, 28385), tuple(12660, 487), tuple(81939, 70723), tuple(78752, 4369), tuple(49729, 16078), tuple(27760, 78394), tuple(27935, 65985), tuple(97170, 17974), tuple(45484, 51849), tuple(37792, 42775), tuple(50788, 50243), tuple(29730, 5771), tuple(8337, 32970), tuple(87282, 34536), tuple(82654, 40374), tuple(25936, 99591), tuple(87105, 44686), tuple(61326, 23201), tuple(48961, 39310), tuple(72404, 34074), tuple(47084, 53440), tuple(44566, 21551), tuple(27348, 31756), tuple(14445, 57694), tuple(3090, 78935), tuple(37165, 19519), tuple(42821, 24648), tuple(4987, 91817), tuple(15902, 33578), tuple(63028, 39291), tuple(9567, 41818), tuple(68851, 3311), tuple(1641, 59714), tuple(51932, 83190), tuple(40656, 19271), tuple(44882, 35988), tuple(94241, 78634), tuple(15970, 79214), tuple(11262, 56304), tuple(46638, 79764), tuple(9206, 54131), tuple(77132, 24211), tuple(38417, 34891), tuple(55196, 29011), tuple(64734, 42845), tuple(33857, 44248), tuple(40460, 66582), tuple(34542, 273), tuple(84169, 65245), tuple(83434, 25030), tuple(10937, 1189), tuple(35213, 89760), tuple(48231, 5149), tuple(49920, 97597), tuple(56463, 13926), tuple(35160, 4587), tuple(91526, 85881), tuple(38624, 54542), tuple(17138, 93601), tuple(16139, 72928), tuple(93357, 75097), tuple(84733, 99796), tuple(39474, 98875), tuple(97908, 58734), tuple(97237, 91217), tuple(77132, 32920), tuple(85467, 32730), tuple(83289, 18742), tuple(2980, 4330), tuple(44920, 11778), tuple(30191, 82779), tuple(34431, 45207), tuple(75529, 10325), tuple(4657, 36429), tuple(97464, 18604), tuple(29451, 14997), tuple(40066, 22598), tuple(49471, 66259), tuple(65164, 98515), tuple(64098, 47692), tuple(87542, 84786), tuple(41170, 49570), tuple(11476, 14536), tuple(82271, 79916), tuple(45889, 42059), tuple(84965, 90533), tuple(88733, 20381), tuple(86349, 66190), tuple(83280, 32409), tuple(38436, 35910), tuple(94362, 2535), tuple(13565, 12567), tuple(72551, 58564), tuple(886, 87999), tuple(73958, 94309), tuple(34030, 60715), tuple(25432, 92697), tuple(85922, 24244), tuple(46779, 52990), tuple(33807, 44621), tuple(39513, 51405), tuple(16668, 32353), tuple(60629, 14813), tuple(73095, 12227), tuple(17801, 38366), tuple(65927, 52833), tuple(33630, 50134), tuple(19975, 71026), tuple(93145, 90036), tuple(73555, 6383), tuple(71789, 81791), tuple(67429, 79036), tuple(20944, 39911), tuple(75838, 56480), tuple(29835, 80194), tuple(19570, 28901), tuple(16023, 82165), tuple(74259, 15283), tuple(11749, 92274), tuple(73537, 52362), tuple(54537, 37595), tuple(79004, 72436), tuple(49229, 21498), tuple(24364, 97373), tuple(81149, 61392), tuple(22421, 6442), tuple(62138, 86081), tuple(16098, 23700), tuple(66604, 86467), tuple(66505, 26263), tuple(57215, 12659), tuple(61267, 77421), tuple(58024, 98298), tuple(44273, 89699), tuple(62339, 73107), tuple(32540, 17768), tuple(31574, 77296), tuple(63336, 23439), tuple(26723, 45141), tuple(61380, 58474), tuple(6188, 2084), tuple(88225, 6631), tuple(10970, 70428), tuple(91223, 64312), tuple(89643, 76635), tuple(42796, 77154), tuple(34827, 21131), tuple(83014, 2253), tuple(34547, 12384), tuple(53922, 57591), tuple(12066, 44400), tuple(89686, 21284), tuple(81875, 3306), tuple(45972, 11207), tuple(96807, 28447), tuple(87662, 42334), tuple(68578, 37404), tuple(21195, 35583), tuple(97730, 41852), tuple(76425, 22959), tuple(63724, 64143), tuple(79182, 50739), tuple(81472, 79682), tuple(26980, 62332), tuple(60691, 50596), tuple(38526, 10488), tuple(74569, 46431), tuple(62677, 60189), tuple(35133, 21147), tuple(3271, 93368), tuple(14651, 76080), tuple(91349, 87952), tuple(7873, 51144), tuple(72431, 54715), tuple(15600, 69868), tuple(46998, 23805), tuple(7519, 75777), tuple(71758, 71673), tuple(23282, 37297), tuple(35646, 89327), tuple(44890, 58293), tuple(34223, 99155), tuple(87015, 18477), tuple(20511, 30210), tuple(82120, 13850), tuple(6382, 41946), tuple(97741, 86276), tuple(15521, 44501), tuple(87240, 57472), tuple(65334, 51695), tuple(34599, 97721), tuple(45366, 9007), tuple(91971, 7384), tuple(74698, 40735), tuple(73706, 9025), tuple(42569, 18928), tuple(62946, 62362), tuple(89350, 83255), tuple(26044, 85080), tuple(27725, 3814), tuple(45787, 32352), tuple(16115, 51701), tuple(3339, 72851), tuple(60161, 34406), tuple(24699, 43473), tuple(28681, 573), tuple(95106, 16784), tuple(74232, 5831), tuple(55361, 1366), tuple(7020, 37807), tuple(52490, 8558), tuple(91203, 64944), tuple(90783, 64700), tuple(76499, 12592), tuple(97070, 9395), tuple(46602, 12688), tuple(79052, 31675), tuple(29316, 93503), tuple(14095, 3100), tuple(15066, 86579), tuple(24651, 10981), tuple(90683, 70162), tuple(60604, 89175), tuple(55172, 34997), tuple(61098, 17843), tuple(2376, 21399), tuple(38186, 60680), tuple(58637, 11541), tuple(11101, 69351), tuple(70200, 67918), tuple(49245, 85252), tuple(4201, 24436), tuple(56636, 87099), tuple(21406, 33757), tuple(29044, 43846), tuple(93900, 68223), tuple(34429, 92683), tuple(76720, 79334), tuple(4035, 30364), tuple(20145, 44853), tuple(46689, 21352), tuple(49446, 77224), tuple(71241, 85720), tuple(27177, 88332), tuple(60151, 49376), tuple(67102, 74396), tuple(242, 26346), tuple(22004, 95064), tuple(2709, 71452), tuple(71673, 86838), tuple(36918, 17777), tuple(37414, 88149), tuple(2356, 70290), tuple(84416, 35509), tuple(32285, 54585), tuple(9937, 23465), tuple(3385, 25949), tuple(97379, 87409), tuple(72714, 43570), tuple(54831, 64883), tuple(80885, 81082), tuple(89673, 5765), tuple(84585, 51627), tuple(64497, 72931), tuple(71948, 60922), tuple(70011, 37446), tuple(81022, 43696), tuple(11413, 58709), tuple(89710, 85350), tuple(96004, 36589), tuple(99270, 82834), tuple(52367, 12117), tuple(62108, 56693), tuple(69090, 34612), tuple(56109, 22008), tuple(36036, 34493), tuple(88561, 4651), tuple(44403, 60712), tuple(91912, 19116), tuple(40545, 94625), tuple(47973, 46963), tuple(30704, 99285), tuple(9596, 21976), tuple(84723, 81323), tuple(98123, 90434), tuple(35842, 29225), tuple(91271, 14003), tuple(42320, 16108), tuple(14171, 88861), tuple(15139, 84030), tuple(31008, 58743), tuple(86149, 98215), tuple(59768, 15217), tuple(85628, 82450), tuple(14256, 2254), tuple(44961, 44326), tuple(86313, 69953), tuple(99151, 37777), tuple(46152, 77035), tuple(36868, 62695), tuple(37233, 49305), tuple(20706, 56446), tuple(75773, 90240), tuple(88130, 91556), tuple(89753, 38172), tuple(31191, 98583), tuple(31412, 81334), tuple(57662, 15890), tuple(9195, 3322), tuple(94618, 22144), tuple(24709, 24446), tuple(54382, 68062), tuple(17085, 30960), tuple(53538, 12827), tuple(12512, 56172), tuple(26611, 90242), tuple(92812, 98619), tuple(82930, 90399), tuple(2299, 45362), tuple(48505, 88997), - tuple(58709, 77683), tuple(85901, 33672), tuple(45070, 24491), tuple(79005, 44787), tuple(7154, 14290), tuple(56684, 35166), tuple(76459, 60609), tuple(6006, 47402), tuple(54755, 19015), tuple(5139, 33177), tuple(13725, 50051), tuple(95929, 58683), tuple(91114, 68401), tuple(88846, 23283), tuple(37153, 36426), tuple(36564, 32035), tuple(49852, 49501), tuple(2052, 59285), tuple(69847, 54966), tuple(82614, 10395), tuple(72068, 35582), tuple(50427, 19892), tuple(75612, 3545), tuple(75480, 26985), tuple(24227, 50112), tuple(22718, 52206), tuple(35801, 41679), tuple(17753, 44416), tuple(4688, 82170), tuple(36935, 6606), tuple(71549, 86720), tuple(53592, 68769), tuple(92671, 84617), tuple(97726, 13655), tuple(65006, 17474), tuple(25287, 80826), tuple(22202, 25703), tuple(98743, 71876), tuple(72914, 31392), tuple(54184, 1294), tuple(9353, 10518), tuple(93822, 74538), tuple(85863, 56357), tuple(56088, 14448), tuple(12427, 27727), tuple(1846, 350), tuple(96301, 21623), tuple(55225, 23813), tuple(76947, 60946), tuple(65400, 17489), tuple(80586, 79099), tuple(56913, 46914), tuple(49218, 53295), tuple(14085, 52313), tuple(71158, 14144), tuple(3641, 69327), tuple(43705, 93045), tuple(67880, 95849), tuple(88054, 66156), tuple(17425, 69320), tuple(16391, 271), tuple(31768, 8817), tuple(63156, 21471), tuple(97831, 43504), tuple(54300, 54402), tuple(8740, 27894), tuple(93805, 30183), tuple(76233, 26322), tuple(60307, 8348), tuple(63019, 32334), tuple(7086, 44838), tuple(93127, 94482), tuple(20146, 84889), tuple(33674, 2513), tuple(66860, 65753), tuple(69174, 35906), tuple(93800, 41975), tuple(31206, 93599), tuple(75620, 60674), tuple(77082, 60773), tuple(75564, 20627), tuple(53060, 67879), tuple(52568, 22722), tuple(4102, 42037), tuple(66549, 71191), tuple(20817, 80946), tuple(13469, 96144), tuple(29852, 713), tuple(72644, 98510), tuple(12567, 54057), tuple(28746, 72197), tuple(80235, 51566), tuple(11953, 73225), tuple(42732, 91659), tuple(19673, 29661), tuple(94438, 41029), tuple(21884, 91078), tuple(29280, 68018), tuple(973, 75825), tuple(83476, 79334), tuple(99077, 625), tuple(63309, 91304), tuple(11626, 33709), tuple(33735, 6897), tuple(12585, 17114), tuple(45256, 57549), tuple(65150, 2524), tuple(10399, 34571), tuple(32783, 6045), tuple(11900, 12875), tuple(4393, 3129), tuple(61420, 63521), tuple(11799, 9092), tuple(45887, 36931), tuple(84256, 71195), tuple(51949, 32406), tuple(34640, 11424), tuple(84866, 46109), tuple(23449, 51873), tuple(22371, 86773), tuple(32155, 25902), tuple(75058, 66016), tuple(21217, 19002), tuple(44067, 59363), tuple(55901, 87102), tuple(71213, 82390), tuple(88408, 22154), tuple(18231, 86852), tuple(96803, 72776), tuple(58608, 53500), tuple(40747, 8542), tuple(60870, 41475), tuple(39578, 66560), tuple(49522, 25492), tuple(3485, 13917), tuple(60177, 43310), tuple(70317, 69166), tuple(71910, 92946), tuple(14427, 320), tuple(5224, 79108), tuple(20986, 81899), tuple(34637, 53572), tuple(14919, 27426), tuple(8052, 10082), tuple(27936, 24128), tuple(73732, 97758), tuple(31869, 5719), tuple(22135, 86985), tuple(85977, 79381), tuple(41587, 34012), tuple(75912, 66428), tuple(62117, 37249), tuple(70197, 91261), - tuple(99703, 96452), tuple(33036, 30249), tuple(33709, 99448), tuple(30343, 2962), tuple(54245, 80785), tuple(13866, 99804), tuple(49392, 62886), tuple(8530, 38228), tuple(86249, 60394), tuple(96787, 88788), tuple(42724, 88066), tuple(97051, 85589), tuple(41414, 88972), tuple(4384, 2025), tuple(46524, 87620), tuple(5823, 36079), tuple(78720, 13519), tuple(59092, 88699), tuple(24120, 6388), tuple(51664, 71039), tuple(28756, 43890), tuple(13729, 27176), tuple(86717, 82448), tuple(65598, 34805), tuple(74975, 52791), tuple(65429, 97173), tuple(92472, 53334), tuple(18778, 40576), tuple(72776, 48956), tuple(61833, 85936), tuple(81344, 48055), tuple(89467, 91099), tuple(82333, 15367), tuple(6019, 98391), tuple(52943, 15338), tuple(29662, 21975), tuple(71935, 47606), tuple(54633, 72216), tuple(51648, 52203), tuple(84649, 31791), tuple(26281, 82065), tuple(83163, 43394), tuple(79859, 55897), tuple(84706, 38368), tuple(97175, 88009), tuple(92800, 82164), tuple(79819, 13645), tuple(42754, 6273), tuple(97025, 79579), tuple(35694, 44160), tuple(96053, 24801), tuple(60315, 14411), tuple(25961, 82303), tuple(1791, 53355), tuple(15508, 43236), tuple(35667, 84655), tuple(73764, 82190), tuple(14465, 72390), tuple(92464, 30555), tuple(14121, 31360), tuple(27630, 78672), tuple(35483, 47439), tuple(79467, 73233), tuple(33010, 49718), tuple(43095, 2002), tuple(3755, 4070), tuple(25460, 21281), tuple(52718, 27575), tuple(97091, 78423), tuple(76536, 86761), tuple(10682, 27325), tuple(55401, 99488), tuple(34196, 88793), tuple(88024, 37693), tuple(4586, 62614), tuple(74857, 37796), tuple(19724, 59109), tuple(17642, 34748), tuple(87983, 11996), tuple(6814, 68602), tuple(63566, 16836), tuple(21026, 42675), tuple(3528, 81994), tuple(58141, 81954), tuple(79520, 99518), tuple(52627, 62974), tuple(42056, 1000), tuple(85464, 63420), tuple(30434, 86139), tuple(59754, 88493), tuple(41386, 76058), tuple(27894, 78093), tuple(63842, 72458), tuple(6928, 37187), tuple(34478, 5193), tuple(57116, 24009), tuple(24858, 84226), tuple(462, 93589), tuple(86510, 80240), tuple(78044, 98234), tuple(30812, 40060), tuple(423, 46196), tuple(47139, 89017), tuple(26066, 21081), tuple(45463, 37260), tuple(34366, 23814), tuple(45937, 79795), tuple(89975, 20405), tuple(21792, 43980), tuple(13675, 11145), tuple(98902, 47577), tuple(20383, 49241), tuple(97905, 16457), tuple(85839, 23279), tuple(33931, 13532), tuple(91486, 54207), tuple(46981, 41204), tuple(49524, 10080), tuple(21201, 3854), tuple(70178, 71393), tuple(26825, 35670), tuple(91350, 92038), tuple(82297, 81201), tuple(47908, 26073), tuple(6456, 40194), tuple(45104, 62038), tuple(75781, 55421), tuple(49561, 35610), tuple(47838, 93109), tuple(25109, 10837), tuple(16666, 99790), tuple(54882, 29259), tuple(28169, 76030), tuple(99605, 78334), tuple(19151, 86372), tuple(48747, 64053), tuple(78764, 38705), tuple(43762, 3756), tuple(22725, 14026), tuple(85174, 6263), tuple(76327, 1072), tuple(92817, 63030), tuple(59923, 28851), tuple(39614, 77354), tuple(54467, 80792), tuple(37309, 8679), tuple(42247, 28830), tuple(60892, 36834), tuple(54108, 62952), tuple(33682, 52992), tuple(87318, 63605), tuple(3180, 91762), tuple(24774, 12744), tuple(4431, 74084), tuple(11712, 7577), tuple(31451, 18294), tuple(68722, 16450), tuple(52185, 92005), tuple(97312, 33963), tuple(43882, 84266), tuple(39958, 52806), tuple(4759, 32862), tuple(33017, 81530), tuple(88475, 9613), tuple(72656, 43214), tuple(27288, 91542), tuple(67232, 47120), tuple(12714, 59507), tuple(17175, 58197), tuple(1719, 97181), tuple(2806, 5682), tuple(22373, 80608), tuple(72129, 33750), tuple(97168, 75472), tuple(16157, 5484), tuple(93177, 59589), tuple(7019, 19832), tuple(63810, 80313), tuple(23551, 45065), tuple(59657, 37189), tuple(12995, 84512), tuple(44630, 12434), tuple(28833, 8088), tuple(56795, 10570), tuple(22385, 4138), tuple(39502, 41865), tuple(67720, 31597), tuple(54837, 8933), tuple(19906, 12496), tuple(69993, 30745), tuple(94886, 98186), tuple(6474, 37743), tuple(62063, 5596), tuple(52064, 33592), tuple(57476, 84891), tuple(88798, 43044), tuple(55029, 97222), tuple(73911, 11862), tuple(1205, 76381), tuple(13388, 4303), tuple(73635, 55410), tuple(97777, 32985), tuple(94358, 49709), tuple(62607, 76061), tuple(62525, 8598), tuple(10327, 11235), tuple(18778, 991), tuple(16602, 1008), tuple(6311, 36329), tuple(6464, 33473), tuple(71418, 15828), tuple(49777, 9630), tuple(67644, 45616), tuple(97118, 54851), tuple(79498, 75515), tuple(45056, 12838), tuple(51110, 53572), tuple(4941, 80166), tuple(16052, 81909), tuple(24336, 25459), tuple(60437, 70317), tuple(51544, 8762), tuple(44978, 27145), tuple(78113, 27193), tuple(45148, 7278), tuple(78858, 90800), tuple(29864, 14191), tuple(67835, 78395), tuple(45893, 74512), tuple(81523, 270), tuple(51436, 75334), tuple(39456, 65552), tuple(89548, 42368), tuple(8953, 17953), tuple(47296, 52855), tuple(36236, 70545), tuple(99705, 30645), tuple(58038, 43017), tuple(95851, 60592), tuple(50658, 98282), tuple(48614, 74538), tuple(29026, 6898), tuple(57506, 97917), tuple(33553, 11205), tuple(92292, 53328), tuple(37861, 10865), tuple(70306, 43839), tuple(52283, 94600), tuple(68802, 95126), tuple(92765, 91310), tuple(2574, 45229), tuple(12463, 45681), tuple(73331, 83847), tuple(22565, 66014), tuple(52454, 22571), tuple(17530, 55911), tuple(7358, 36106), tuple(54186, 27674), tuple(55476, 166), tuple(12926, 8902), tuple(63782, 1616), tuple(60085, 35021), tuple(86605, 99026), tuple(7432, 57161), tuple(37268, 11832), tuple(3860, 95662), tuple(30339, 54518), tuple(26163, 96043), tuple(26092, 71977), tuple(63899, 7792), tuple(9804, 66302), tuple(81779, 54515), tuple(2861, 42418), tuple(44736, 75647), tuple(50912, 53848), tuple(72120, 3651), tuple(91526, 72999), tuple(71642, 69795), tuple(40664, 37695), tuple(57357, 83958), tuple(41666, 52277), tuple(49209, 99223), tuple(47274, 75571), tuple(64326, 88981), tuple(67848, 52903), tuple(44013, 43796), tuple(21678, 8103), tuple(74827, 132), tuple(88279, 49672), tuple(10747, 85149), tuple(74506, 99115), tuple(89615, 95102), tuple(14077, 79858), tuple(55139, 17814), tuple(5519, 48761), tuple(62278, 39931), tuple(58406, 38267), tuple(58705, 86071), tuple(63443, 11013), tuple(53144, 22114), tuple(78535, 70561), tuple(85423, 36012), tuple(3285, 72479), tuple(73658, 19340), tuple(18511, 10477), tuple(60780, 75674), tuple(88032, 88205), tuple(35386, 23855), tuple(49977, 32673), tuple(93206, 71515), tuple(3482, 73115), tuple(48423, 52857), tuple(11899, 30649), tuple(59730, 15323), tuple(89442, 87402), tuple(439, 50184), tuple(98106, 42819), tuple(25941, 82842), tuple(82980, 56368), tuple(71420, 11196), tuple(20860, 78799), tuple(71131, 53182), tuple(45795, 85122), tuple(34635, 83218), tuple(78160, 79377), tuple(8534, 26595), tuple(35248, 17819), tuple(54060, 99480), tuple(36087, 84130), tuple(16001, 59701), tuple(63490, 15318), tuple(41259, 62966), tuple(79583, 94979), tuple(49892, 92957), tuple(8542, 94075), tuple(66339, 33975), tuple(21084, 67889), tuple(14718, 20930), tuple(1760, 60204), tuple(42318, 36873), tuple(2306, 42786), tuple(1667, 59179), tuple(29425, 64430), tuple(64436, 4268), tuple(52666, 79958), tuple(11248, 35590), tuple(37352, 72786), tuple(79273, 24409), tuple(47965, 59976), tuple(61729, 87562), tuple(22673, 52091), tuple(58668, 33485), tuple(60556, 6194), tuple(9974, 17532), tuple(91645, 12490), tuple(16690, 75552), tuple(30983, 79572), tuple(73896, 87577), tuple(82325, 6966), tuple(46769, 19266), tuple(6691, 2816), tuple(65074, 51276), tuple(83174, 210), tuple(38395, 19191), tuple(83871, 939), tuple(9543, 23700), tuple(18316, 3882), tuple(18604, 6191), tuple(53431, 2917), tuple(71291, 3352), tuple(46424, 29885), tuple(44395, 57382), tuple(95028, 33049), tuple(96372, 2057), tuple(26911, 14241), tuple(35535, 27007), tuple(25527, 36480), tuple(13612, 95407), tuple(56176, 82284), tuple(28597, 48562), tuple(76438, 43785), tuple(17631, 15131), tuple(18728, 76676), tuple(49606, 60663), tuple(64878, 57243), tuple(10925, 14798), tuple(72592, 95876), tuple(8144, 58293), tuple(36923, 57214), tuple(82893, 42678), tuple(15533, 63570), tuple(66619, 96607), tuple(32491, 70158), tuple(25175, 70456), tuple(54365, 64639), tuple(56529, 44275), tuple(85598, 13051), tuple(82052, 78051), tuple(6802, 14950), tuple(18964, 77404), tuple(69285, 33899), tuple(42496, 817), tuple(80092, 73354), tuple(44095, 57405), tuple(6715, 98298), tuple(82698, 65201), tuple(25536, 48241), tuple(60721, 38389), tuple(72591, 3716), tuple(76974, 58694), tuple(39723, 66127), tuple(21781, 27885), tuple(57871, 26565), tuple(57327, 1713), tuple(43406, 68141), tuple(65690, 21025), tuple(77459, 59220), tuple(78038, 40571), tuple(44248, 86386), tuple(17714, 38469), tuple(14993, 99711), tuple(18231, 62648), tuple(8081, 2797), tuple(73996, 60186), tuple(98808, 9291), tuple(93219, 32512), tuple(45831, 71316), tuple(9315, 81476), tuple(31933, 97130), tuple(20920, 2404), tuple(31015, 11982), tuple(1251, 81196), tuple(35575, 4027), tuple(81650, 61732), tuple(10241, 79907), tuple(53046, 95973), tuple(68668, 48509), tuple(656, 1992), tuple(10061, 89424), tuple(11047, 71227), tuple(10089, 90496), tuple(57613, 95766), tuple(38775, 44707), tuple(10140, 62292), tuple(91276, 50321), tuple(75890, 59520), tuple(32977, 39392), tuple(43150, 88441), tuple(51314, 81453), tuple(57440, 53885), tuple(85712, 10329), tuple(52723, 17377), tuple(29648, 83895), tuple(41142, 80706), tuple(4167, 43740), tuple(49249, 91247), tuple(15721, 53638), tuple(95764, 35654), tuple(21318, 81252), tuple(97676, 77111), tuple(27613, 35504), tuple(20973, 47975), tuple(60292, 36376), tuple(78092, 59608), tuple(53682, 29368), tuple(95342, 22174), tuple(2289, 80842), tuple(97276, 49917), tuple(21421, 37624), tuple(50678, 98609), tuple(49240, 25174), tuple(64603, 13614), tuple(75402, 61541), tuple(80112, 45397), tuple(14289, 87479), tuple(16888, 98577), tuple(14999, 80972), tuple(64689, 57967), tuple(79391, 54976), tuple(37446, 39412), tuple(86682, 70785), - tuple(21709, 57340), tuple(53757, 39880), tuple(33201, 94136), tuple(27854, 30229), tuple(14865, 56637), tuple(75573, 68915), tuple(84594, 1520), tuple(71261, 69867), tuple(84203, 43708), tuple(76407, 88476), tuple(84395, 77050), tuple(53934, 83635), tuple(32870, 27302), tuple(46646, 46399), tuple(26417, 60940), tuple(87513, 88720), tuple(12542, 32018), tuple(45969, 62284), tuple(26733, 90539), tuple(50556, 13388), tuple(60882, 56222), tuple(48253, 22792), tuple(68209, 18501), tuple(58073, 48960), tuple(6360, 4482), tuple(53997, 31868), tuple(15727, 78331), tuple(81299, 84008), tuple(99495, 89688), tuple(504, 3147), tuple(93985, 77265), tuple(78723, 60673), tuple(30278, 63220), tuple(33434, 26642), tuple(71481, 73333), tuple(49805, 59269), tuple(90657, 93630), tuple(28814, 83954), tuple(91466, 63542), tuple(43843, 69657), tuple(11466, 24136), tuple(63826, 6209), tuple(29687, 95777), tuple(65616, 74144), tuple(62798, 883), tuple(54174, 88934), tuple(35129, 40939), tuple(42224, 54784), tuple(80876, 68479), tuple(80812, 51383), tuple(39443, 11689), tuple(15438, 66488), tuple(32856, 96950), tuple(80525, 99128), tuple(25968, 28334), tuple(91925, 37414), tuple(84823, 58164), tuple(17795, 78409), tuple(29912, 53931), tuple(1369, 92329), tuple(36176, 34133), tuple(79057, 62016), tuple(74457, 51743), tuple(90604, 95280), tuple(18107, 81811), tuple(26994, 78521), tuple(4762, 53724), tuple(8229, 90056), tuple(60962, 1202), tuple(80485, 44934), tuple(58917, 36716), tuple(71770, 42524), tuple(94551, 98259), tuple(39500, 41373), tuple(50410, 70615), tuple(63782, 25832), tuple(74940, 15450), tuple(90724, 44531), tuple(69088, 22535), tuple(40581, 3354), tuple(1756, 86005), tuple(93128, 31200), tuple(14139, 8370), tuple(65646, 40141), tuple(31537, 89271), tuple(7635, 66721), tuple(67463, 97745), tuple(12391, 36807), tuple(6311, 30232), tuple(20818, 91220), tuple(53449, 11632), tuple(21802, 93687), tuple(75581, 79739), tuple(5514, 57540), tuple(67063, 99383), tuple(98230, 18983), tuple(50929, 35978), tuple(16069, 59906), tuple(93594, 94696), tuple(13344, 34575), tuple(30331, 84457), tuple(78047, 97558), tuple(5507, 41722), tuple(81016, 8762), tuple(91323, 59598), tuple(67184, 8555), tuple(76659, 67276), tuple(6511, 94844), tuple(1063, 39110), tuple(40795, 16854), tuple(40694, 32666), tuple(14734, 95257), tuple(6818, 30227), tuple(32211, 56294), tuple(31129, 97716), tuple(93866, 6117), tuple(42144, 91974), tuple(64560, 9708), tuple(5803, 92811), tuple(7240, 42166), tuple(97514, 19116), tuple(59904, 64965), tuple(73031, 69137), tuple(63313, 82886), tuple(58529, 84361), tuple(47365, 96118), tuple(27999, 83969), tuple(98211, 52135), tuple(86460, 86408), tuple(21315, 59646), tuple(98469, 99928), tuple(24453, 79050), tuple(97595, 41780), tuple(35575, 22842), tuple(18219, 72673), tuple(4001, 89495), tuple(19563, 18097), tuple(566, 5304), tuple(37517, 63100), tuple(11251, 58908), tuple(2474, 55788), tuple(83078, 65059), tuple(68185, 94587), tuple(33305, 36706), tuple(81350, 72951), tuple(37115, 41580), tuple(6005, 44393), tuple(4318, 67309), tuple(21346, 75943), tuple(57955, 33395), tuple(75723, 89514), tuple(2661, 42938), tuple(89643, 48828), - tuple(3409, 13157), tuple(13667, 78006), tuple(56518, 86796), tuple(38971, 28234), tuple(38060, 77284), tuple(68626, 83168), tuple(69324, 67649), tuple(48994, 40388), tuple(87024, 14379), tuple(12475, 27192), tuple(41271, 41939), tuple(50304, 38146), tuple(8682, 25307), tuple(93898, 48129), tuple(34600, 25005), tuple(48022, 22298), tuple(25683, 46046), tuple(24839, 69086), tuple(50505, 93439), tuple(90497, 50297), tuple(16054, 68182), tuple(69768, 67239), tuple(35909, 84118), tuple(64986, 21267), tuple(32550, 33801), tuple(8884, 66830), tuple(10690, 75679), tuple(59327, 9389), tuple(16594, 37706), tuple(54110, 76448), tuple(22052, 90416), tuple(18104, 20971), tuple(78917, 19589), tuple(48799, 38897), tuple(55663, 11112), tuple(24255, 19761), tuple(90224, 59023), tuple(51893, 61626), tuple(57840, 2070), tuple(37436, 84968), tuple(55242, 52606), tuple(71391, 70783), tuple(84796, 72467), tuple(61673, 90112), tuple(61880, 10015), tuple(84560, 21382), tuple(91734, 52105), tuple(94237, 68942), tuple(36055, 26633), tuple(43694, 33404), tuple(60266, 17069), tuple(63045, 2539), tuple(39314, 49430), tuple(66845, 42602), tuple(76834, 50742), tuple(26684, 64231), tuple(83102, 88337), tuple(43541, 16088), tuple(53516, 23759), tuple(21092, 43649), tuple(71242, 31515), tuple(62253, 81538), tuple(19670, 48947), tuple(24097, 83600), tuple(11636, 70960), tuple(55314, 99976), tuple(56296, 68456), tuple(25537, 64586), tuple(45649, 53747), tuple(9712, 85370), tuple(82073, 71622), tuple(97381, 26322), tuple(4927, 47703), tuple(8999, 33464), tuple(5875, 80932), tuple(88591, 63062), tuple(40824, 93164), tuple(55753, 10733), tuple(56196, 8875), tuple(13427, 66714), tuple(22780, 69557), tuple(37212, 13763), tuple(61668, 95614), tuple(79050, 55391), tuple(92892, 94037), tuple(4942, 23252), tuple(34025, 31194), tuple(96074, 59364), tuple(11520, 11652), tuple(94439, 19618), tuple(64758, 41196), tuple(73397, 72389), tuple(57282, 73894), tuple(79700, 78450), tuple(65052, 54392), tuple(72453, 23422), tuple(49093, 48337), tuple(9863, 89449), tuple(60397, 40836), tuple(18147, 51195), tuple(39787, 61479), tuple(63062, 62242), tuple(67547, 5426), tuple(25316, 76298), tuple(16168, 12116), tuple(91884, 94406), tuple(57385, 89464), tuple(93062, 71189), tuple(97612, 38802), tuple(85128, 1852), tuple(80643, 41827), tuple(59109, 50692), tuple(28077, 24715), tuple(67583, 40643), tuple(54073, 32408), tuple(97704, 17820), tuple(15884, 69143), tuple(66420, 36961), tuple(60442, 34685), tuple(3109, 47213), tuple(27157, 42282), tuple(18909, 52184), tuple(16638, 51215), tuple(66044, 40079), tuple(67856, 15021), tuple(26942, 58162), tuple(76866, 73547), tuple(3999, 47414), tuple(5114, 74975), tuple(85820, 10632), tuple(71972, 59810), tuple(66488, 16840), tuple(50779, 93807), tuple(40068, 12260), tuple(67715, 72553), tuple(45275, 57705), tuple(45019, 86665), tuple(97033, 97611), tuple(69136, 54593), tuple(1879, 87594), tuple(49292, 92238), tuple(69064, 7240), tuple(60753, 5294), tuple(36350, 67918), tuple(48440, 43264), tuple(70554, 79751), tuple(40310, 64039), tuple(48171, 75441), tuple(53305, 72396), tuple(44027, 58150), tuple(67485, 38325), tuple(87333, 75101), tuple(2509, 54526), tuple(40794, 18097), tuple(17674, 84451), tuple(30175, 65221), tuple(71744, 57300), tuple(91603, 35695), tuple(33369, 1387), tuple(51808, 2008), tuple(42659, 11574), tuple(39492, 79947), tuple(95089, 47048), tuple(57022, 73399), tuple(72707, 8845), tuple(66843, 61434), tuple(7174, 16342), tuple(50709, 76713), tuple(45560, 1378), tuple(80907, 97774), tuple(651, 93255), tuple(42650, 82788), tuple(2875, 24267), tuple(73437, 67551), tuple(6685, 81749), tuple(43495, 12190), tuple(63932, 69464), tuple(86694, 91717), tuple(91608, 59712), tuple(50833, 91382), tuple(18301, 39265), tuple(48304, 34276), tuple(24633, 56311), tuple(10463, 40342), tuple(17645, 64242), tuple(52245, 96838), tuple(6606, 87413), tuple(75088, 39449), tuple(6247, 97599), tuple(57124, 39047), tuple(49579, 80395), tuple(52959, 14523), tuple(73129, 3675), tuple(78880, 16111), tuple(90870, 27122), tuple(11166, 67671), tuple(48048, 41486), tuple(65329, 98702), tuple(4801, 92915), tuple(88765, 51873), tuple(59370, 1440), tuple(41316, 88008), tuple(92238, 79140), tuple(18523, 32178), tuple(36124, 49818), tuple(84251, 37274), tuple(92085, 96091), tuple(65382, 7189), tuple(30130, 32066), tuple(86434, 46207), tuple(5500, 11160), tuple(88366, 36983), tuple(48685, 47634), tuple(93314, 57448), tuple(95978, 97924), tuple(20017, 85908), tuple(82568, 31256), tuple(44633, 78762), tuple(41625, 11793), tuple(77646, 2315), tuple(67604, 29898), tuple(1492, 55783), tuple(33177, 5891), tuple(93884, 41363), tuple(90527, 43977), tuple(36905, 59152), tuple(37750, 19264), tuple(58020, 1360), tuple(22974, 3246), tuple(41403, 80614), tuple(4479, 37045), tuple(27477, 47093), tuple(79899, 65264), tuple(12625, 46993), tuple(78884, 10896), tuple(49043, 36064), tuple(22725, 18991), tuple(4279, 11656), tuple(58193, 27118), tuple(48315, 26221), tuple(86372, 4638), tuple(86413, 43777), tuple(74216, 60197), tuple(89547, 68700), tuple(618, 62861), tuple(88483, 17054), tuple(59378, 75565), tuple(15001, 59042), tuple(15858, 10735), tuple(42583, 57159), tuple(27836, 12597), tuple(87465, 37449), tuple(95717, 59515), tuple(63369, 36932), tuple(14736, 61367), tuple(14779, 30481), tuple(33108, 95749), tuple(5304, 7878), tuple(74344, 65457), tuple(3519, 98197), tuple(34669, 96365), tuple(31376, 21260), tuple(75715, 11580), tuple(14317, 29528), tuple(10151, 13185), tuple(6975, 59407), tuple(7910, 71250), tuple(21761, 8936), tuple(11683, 43452), tuple(97556, 87506), tuple(87371, 16179), tuple(46806, 74511), tuple(737, 82723), tuple(1197, 77799), tuple(9835, 85745), tuple(66026, 16556), tuple(24784, 20015), tuple(53234, 58687), tuple(4393, 27429), tuple(11484, 48640), tuple(24376, 998), tuple(76115, 38), tuple(72364, 3765), tuple(97109, 9842), tuple(36814, 37745), tuple(58829, 16139), tuple(4165, 93524), tuple(51052, 5215), tuple(69771, 11456), tuple(13263, 75724), tuple(60779, 11359), tuple(9924, 2846), tuple(70887, 4526), tuple(66054, 93309), tuple(15486, 40658), tuple(18780, 1579), tuple(89386, 24351), tuple(33682, 62095), tuple(661, 84125), tuple(13480, 97300), tuple(2408, 25599), tuple(99168, 10687), tuple(84860, 17842), tuple(38292, 12704), tuple(97468, 83719), tuple(38360, 25940), tuple(97314, 65483), tuple(95688, 44394), tuple(47420, 96156), tuple(66394, 61773), tuple(98637, 46400), tuple(69962, 57775), tuple(83857, 45874), tuple(74811, 5741), tuple(96157, 62317), tuple(57902, 79905), tuple(73760, 21345), tuple(58109, 65145), tuple(58678, 5406), tuple(96538, 16329), tuple(42244, 63758), tuple(17613, 66642), tuple(72398, 5307), tuple(12872, 94227), tuple(65046, 1439), tuple(90217, 10756), tuple(58185, 63169), tuple(50247, 86728), tuple(98822, 4390), tuple(82566, 26006), tuple(3574, 42077), tuple(43701, 59837), tuple(85579, 48641), tuple(47792, 34429), tuple(77410, 8515), tuple(97749, 11160), tuple(57462, 33927), tuple(32836, 57768), tuple(26898, 80133), tuple(52210, 57444), tuple(33639, 288), tuple(30309, 29233), tuple(14867, 24307), tuple(67420, 29214), tuple(29889, 86125), tuple(68662, 93027), tuple(30716, 64262), tuple(9145, 66627), tuple(83817, 78000), tuple(49074, 52390), tuple(52974, 72121), tuple(84006, 2545), tuple(27803, 68907), tuple(79008, 5828), tuple(74872, 89207), tuple(26302, 23622), tuple(26263, 25254), tuple(65652, 38273), tuple(11673, 21629), tuple(70494, 86207), tuple(59074, 85729), tuple(2011, 38108), tuple(70239, 9100), tuple(37062, 67038), tuple(52681, 44407), tuple(79718, 59481), tuple(39506, 32648), tuple(76903, 76789), tuple(45162, 63035), tuple(13344, 3835), tuple(96239, 59063), tuple(24507, 82181), tuple(77403, 89247), tuple(73887, 41747), tuple(5639, 27352), tuple(46718, 96169), tuple(94630, 83108), tuple(37999, 68318), tuple(97717, 53272), tuple(80506, 42486), tuple(7677, 75413), tuple(45129, 2230), tuple(11721, 21720), tuple(88499, 48784), tuple(23501, 58439), tuple(70767, 72086), tuple(78533, 90007), tuple(72580, 80411), tuple(36564, 98451), tuple(89444, 69448), tuple(48038, 26523), tuple(5123, 78026), tuple(29168, 41429), tuple(36287, 72619), tuple(75350, 27908), tuple(7381, 96048), tuple(30090, 51055), tuple(73670, 11691), tuple(39082, 72957), tuple(84785, 52400), tuple(80436, 94637), tuple(17776, 61224), tuple(36848, 71607), tuple(90182, 50522), tuple(78350, 71698), tuple(91120, 27247), tuple(39638, 38298), tuple(22945, 23976), tuple(52034, 72847), tuple(97888, 33382), tuple(20825, 5844), tuple(303, 5273), tuple(62672, 96465), tuple(46951, 93410), tuple(57258, 108), tuple(11116, 34876), tuple(40588, 20691), tuple(61906, 68626), tuple(48319, 90189), tuple(20255, 4601), tuple(80998, 30793), tuple(28741, 2714), tuple(42195, 25873), tuple(17046, 46739), tuple(15319, 54014), tuple(11773, 11673), tuple(73012, 41575), tuple(21747, 59925), tuple(16088, 24272), tuple(38326, 8140), tuple(68006, 23594), tuple(78052, 77991), tuple(45820, 79226), tuple(11143, 88881), tuple(59251, 76272), tuple(18953, 10412), tuple(67075, 57195), tuple(1386, 54226), tuple(42759, 59791), tuple(5696, 15215), tuple(79633, 55487), tuple(3546, 11417), tuple(97976, 8323), tuple(55197, 53843), tuple(12182, 16579), tuple(55706, 87702), tuple(88871, 45930), tuple(98978, 50942), tuple(55874, 87465), tuple(53256, 28916), tuple(90605, 43673), tuple(23906, 75994), tuple(12746, 66785), tuple(91372, 53574), tuple(72302, 69561), tuple(38833, 59338), tuple(98528, 22616), tuple(10045, 25111), tuple(34397, 69161), tuple(86106, 23720), tuple(82705, 67810), tuple(48691, 14861), tuple(41392, 64552), tuple(83105, 6557), tuple(62123, 39827), tuple(94840, 54871), tuple(35778, 26572), tuple(16194, 14615), tuple(27, 8380), tuple(33704, 44750), tuple(86884, 51173), tuple(60330, 80082), tuple(56180, 47098), tuple(6249, 36349), tuple(32268, 12733), tuple(86808, 40162), tuple(27643, 75454), tuple(36783, 30874), tuple(54827, 7607), tuple(54697, 57582), tuple(83316, 18631), - tuple(44081, 33952), tuple(89780, 86498), tuple(63456, 27382), tuple(5654, 13927), tuple(41301, 35640), tuple(4502, 70840), tuple(4238, 62879), tuple(66516, 67091), tuple(96156, 8355), tuple(2902, 23789), tuple(98837, 77623), tuple(86898, 91764), tuple(75147, 6127), tuple(8274, 46182), tuple(7422, 18222), tuple(86509, 25754), tuple(72400, 55881), tuple(84459, 68079), tuple(5654, 76270), tuple(43017, 78770), tuple(45609, 55504), tuple(67256, 51832), tuple(42204, 15653), tuple(52363, 44095), tuple(11760, 24927), tuple(53286, 20903), tuple(97482, 76094), tuple(43809, 8144), tuple(82974, 74859), tuple(65921, 42364), tuple(11938, 55421), tuple(92195, 10809), tuple(91371, 77402), tuple(17644, 86490), tuple(4424, 27504), tuple(56380, 99969), tuple(1731, 80243), tuple(8879, 23354), tuple(94574, 42743), tuple(78875, 2965), tuple(1029, 61788), tuple(64768, 61914), tuple(54149, 90155), tuple(83048, 46305), tuple(80640, 10062), tuple(12372, 94961), tuple(84549, 84061), tuple(99122, 78998), tuple(91039, 95054), tuple(19628, 18651), tuple(52348, 42569), tuple(58086, 76882), tuple(49881, 13151), tuple(8064, 47655), tuple(4457, 63438), tuple(35500, 82994), tuple(47962, 43653), tuple(5050, 36247), tuple(96631, 67316), tuple(49101, 96157), tuple(15785, 96404), tuple(56283, 36312), tuple(31695, 24307), tuple(81897, 59720), tuple(51016, 45776), tuple(92118, 47354), tuple(16024, 38406), tuple(57737, 68887), tuple(28408, 46608), tuple(65055, 1161), tuple(60430, 21682), tuple(22659, 74001), tuple(35202, 60211), tuple(38308, 52926), tuple(74264, 26330), tuple(17038, 41144), tuple(27775, 24875), tuple(49666, 73774), tuple(28516, 34297), tuple(21873, 11504), tuple(3676, 38168), tuple(90003, 25107), tuple(45103, 7031), tuple(71883, 40867), tuple(6274, 71251), tuple(60307, 49056), tuple(58402, 76742), tuple(10010, 36003), tuple(67059, 75300), tuple(36996, 57460), tuple(61413, 54691), tuple(99608, 49572), tuple(87512, 44123), tuple(43942, 87151), tuple(18543, 43029), tuple(886, 83140), tuple(75529, 69749), tuple(54090, 8088), tuple(43094, 35468), tuple(76460, 45310), tuple(64690, 46953), tuple(29561, 93370), tuple(97598, 21618), tuple(28166, 8888), tuple(12234, 771), tuple(65670, 1561), tuple(85467, 97276), tuple(98868, 57050), tuple(14619, 53684), tuple(924, 82181), tuple(65602, 36977), tuple(7888, 8938), tuple(32792, 1046), tuple(18719, 99863), tuple(34993, 11678), tuple(28837, 64924), tuple(83079, 48559), tuple(5806, 92117), tuple(79239, 8754), tuple(89819, 16162), tuple(99505, 94650), tuple(8636, 27356), tuple(6056, 5682), tuple(1921, 15164), tuple(45536, 10747), tuple(56628, 62991), tuple(51456, 54815), tuple(13030, 47920), tuple(27330, 48623), tuple(21393, 96651), tuple(51282, 5317), tuple(78200, 42975), tuple(18082, 38478), tuple(32594, 4205), tuple(49208, 22593), tuple(80079, 11765), tuple(65186, 60836), tuple(11601, 9377), tuple(95550, 52242), tuple(86838, 96932), tuple(17508, 91440), tuple(73427, 92075), tuple(70969, 37180), tuple(7091, 48708), tuple(35444, 42181), tuple(91735, 29746), tuple(11924, 89935), tuple(34418, 61018), tuple(68065, 96782), tuple(30869, 53390), tuple(99315, 78165), tuple(97205, 90309), tuple(88481, 24681), - tuple(17847, 75359), tuple(73636, 11167), tuple(91431, 62190), tuple(28478, 6770), tuple(97305, 77873), tuple(56987, 22599), tuple(40879, 63647), tuple(1974, 67839), tuple(88651, 99581), tuple(67087, 51740), tuple(24861, 74794), tuple(92730, 14161), tuple(52639, 78795), tuple(37163, 80079), tuple(87445, 7053), tuple(27234, 79447), tuple(43352, 66903), tuple(96267, 10657), tuple(57181, 24266), tuple(84236, 83872), tuple(56551, 30387), tuple(4972, 87269), tuple(98922, 1624), tuple(48398, 19886), tuple(40570, 79648), tuple(92239, 98232), tuple(86789, 55696), tuple(64626, 44726), tuple(82391, 95837), tuple(74414, 93972), tuple(97542, 73957), tuple(76038, 54044), tuple(66087, 24170), tuple(25731, 50138), tuple(39800, 75428), tuple(21730, 88783), tuple(53518, 83466), tuple(50784, 72242), tuple(23969, 85392), tuple(43772, 70437), tuple(87869, 33052), tuple(15531, 61368), tuple(22519, 38591), tuple(37709, 35030), tuple(74030, 5134), tuple(18667, 50524), tuple(84691, 21182), tuple(50932, 66070), tuple(27898, 10408), tuple(79554, 49192), tuple(2517, 7746), tuple(71312, 22108), tuple(15511, 37874), tuple(21623, 77591), tuple(10809, 46240), tuple(40913, 29935), tuple(78969, 56158), tuple(46274, 75491), tuple(87163, 49581), tuple(88417, 86223), tuple(41666, 28061), tuple(17503, 13021), tuple(16476, 50547), tuple(56184, 32943), tuple(89243, 61225), tuple(89844, 81092), tuple(19846, 44485), tuple(25999, 46684), tuple(67283, 76235), tuple(64393, 53397), tuple(41565, 93212), tuple(82296, 54617), tuple(62466, 54078), tuple(97942, 92201), tuple(18205, 67342), tuple(44364, 51466), tuple(72744, 4796), tuple(72526, 68979), tuple(50015, 88033), tuple(19680, 47415), tuple(65486, 46344), tuple(21531, 42236), tuple(3452, 94720), tuple(44902, 89762), tuple(13814, 85842), tuple(98337, 42500), tuple(74870, 31262), tuple(72964, 10884), tuple(64408, 43927), tuple(97024, 66176), tuple(55999, 75231), tuple(72794, 37516), tuple(67787, 70137), tuple(4960, 2377), tuple(22048, 86320), tuple(9964, 86377), tuple(30766, 39958), tuple(68684, 29197), tuple(43864, 72887), tuple(90838, 11639), tuple(97183, 10594), tuple(99649, 22933), tuple(91770, 4554), tuple(97882, 60479), tuple(91041, 72702), tuple(47685, 56715), tuple(51900, 61103), tuple(90821, 27638), tuple(95748, 57574), tuple(79106, 14366), tuple(10560, 97465), tuple(12009, 55707), tuple(65879, 34837), tuple(67934, 69351), tuple(58075, 96424), tuple(26898, 13830), tuple(2074, 46887), tuple(47317, 28361), tuple(97566, 43461), tuple(8150, 8518), tuple(21546, 42240), tuple(75546, 59160), tuple(65267, 35547), tuple(32762, 8539), tuple(31559, 21127), tuple(31913, 36056), tuple(80988, 11524), tuple(41145, 62648), tuple(23225, 40076), tuple(17544, 39206), tuple(1553, 91048), tuple(16940, 78156), tuple(7990, 70275), tuple(14929, 46391), tuple(92515, 27289), tuple(77071, 8542), tuple(29061, 27539), tuple(61002, 28411), tuple(88830, 96749), tuple(52211, 71717), tuple(19450, 46195), tuple(5895, 47099), tuple(29098, 35266), tuple(75499, 90090), tuple(76936, 30217), tuple(97220, 51472), tuple(63272, 35659), tuple(62885, 64591), tuple(95983, 97721), tuple(63118, 93206), tuple(98618, 82875), tuple(89990, 44842), tuple(75664, 44348), tuple(86664, 44960), tuple(50835, 42026), tuple(26637, 6312), tuple(99637, 96642), tuple(59176, 14821), tuple(50542, 9553), tuple(76633, 65029), tuple(25409, 65198), tuple(3352, 69819), tuple(63711, 60406), tuple(95437, 53364), tuple(37840, 77585), tuple(46865, 9097), tuple(19420, 46524), tuple(88308, 21695), tuple(32788, 12773), tuple(42860, 16774), tuple(20890, 85130), tuple(91438, 59944), tuple(18399, 28949), tuple(20946, 76691), tuple(46617, 83686), tuple(34107, 99019), tuple(49597, 44158), tuple(33564, 36586), tuple(87590, 72014), tuple(59107, 18546), tuple(44421, 43424), tuple(21333, 27720), tuple(1135, 18434), tuple(69321, 80520), tuple(28921, 41288), tuple(18914, 53626), tuple(63676, 98378), tuple(29612, 9956), tuple(3908, 2160), tuple(78213, 53772), tuple(45709, 90664), tuple(77042, 41054), tuple(86883, 36641), tuple(99497, 83975), tuple(98822, 39177), tuple(38175, 78142), tuple(8236, 62618), tuple(11422, 92281), tuple(24292, 13446), tuple(14489, 14652), tuple(89100, 73028), tuple(84314, 64770), tuple(93912, 25955), tuple(33612, 87481), tuple(12263, 39940), tuple(38184, 58202), tuple(77635, 79922), tuple(78339, 97371), tuple(47955, 29169), tuple(51554, 40923), tuple(74047, 67244), tuple(85365, 31911), tuple(56786, 6734), tuple(82342, 51354), tuple(82818, 80508), tuple(61409, 85614), tuple(25675, 10634), tuple(17091, 62250), tuple(84446, 23721), tuple(267, 14486), tuple(71998, 69455), tuple(46656, 67266), tuple(32788, 84839), tuple(42014, 98189), tuple(49293, 8851), tuple(66248, 62924), tuple(19458, 74293), tuple(90351, 62521), tuple(83067, 20364), tuple(47665, 30564), tuple(4391, 85292), tuple(96286, 26682), tuple(38393, 19001), tuple(54383, 21667), tuple(57586, 88071), tuple(61809, 69649), tuple(52249, 43209), tuple(43878, 30047), tuple(85943, 32125), tuple(14229, 12697), tuple(92247, 72822), tuple(97179, 86933), tuple(63243, 84200), tuple(42053, 43557), tuple(95748, 9682), tuple(43850, 72883), tuple(3576, 48689), tuple(1517, 48614), tuple(52726, 32475), tuple(93527, 61058), tuple(96804, 21108), tuple(78109, 63584), tuple(23551, 15738), tuple(38196, 68934), tuple(48406, 42638), tuple(52135, 73232), tuple(64063, 91934), tuple(16822, 37151), tuple(14686, 92843), tuple(11115, 26382), tuple(98109, 99834), tuple(86192, 45754), tuple(43093, 97836), tuple(34061, 5654), tuple(71966, 21209), tuple(34289, 45307), tuple(24639, 91324), tuple(58483, 71864), tuple(88435, 91876), tuple(31995, 36723), tuple(60461, 90431), tuple(81765, 37792), tuple(44888, 29738), tuple(76600, 12587), tuple(37134, 48440), tuple(69151, 38382), tuple(51397, 43750), tuple(4293, 98001), tuple(27450, 28456), tuple(99384, 24614), tuple(981, 89456), tuple(59721, 47304), tuple(60616, 98154), tuple(98744, 11293), tuple(83417, 71831), tuple(32729, 41250), tuple(46234, 15350), tuple(39317, 40016), tuple(10667, 89674), tuple(50749, 56788), tuple(2889, 90006), tuple(20397, 68884), tuple(32976, 67095), tuple(72045, 90810), tuple(73914, 29464), tuple(36641, 44630), tuple(21878, 385), tuple(95246, 23783), tuple(57354, 29305), tuple(26745, 84026), tuple(36892, 58119), tuple(1149, 95862), tuple(86209, 56761), tuple(84711, 48878), tuple(25394, 56006), tuple(31581, 30386), tuple(34987, 34411), tuple(41423, 44982), tuple(74735, 61128), tuple(58589, 76665), tuple(42483, 94983), tuple(54883, 69038), tuple(4802, 55544), tuple(47092, 31410), tuple(6642, 91583), tuple(93798, 25259), tuple(58424, 38132), tuple(43946, 97030), tuple(93700, 68673), tuple(99034, 34439), tuple(66998, 75560), tuple(64453, 50462), tuple(12208, 87262), tuple(8175, 69014), tuple(91514, 85630), tuple(23685, 748), tuple(88106, 51369), tuple(27510, 26419), tuple(83983, 5604), tuple(66703, 57061), tuple(72865, 67896), tuple(94029, 2147), tuple(95321, 30188), tuple(24240, 67512), tuple(21532, 29481), tuple(69257, 9492), tuple(59958, 62068), tuple(18491, 93721), tuple(37525, 12039), tuple(95626, 86236), tuple(49136, 11328), tuple(71565, 98832), tuple(77767, 34118), tuple(64925, 21897), tuple(73541, 79422), tuple(92001, 79259), tuple(8309, 34891), tuple(66449, 55242), tuple(85889, 88359), tuple(90559, 87521), tuple(92585, 92004), tuple(21799, 17117), tuple(98134, 53406), tuple(40978, 17010), tuple(55063, 73235), tuple(7601, 13970), tuple(46646, 31351), tuple(7085, 97956), tuple(45998, 98665), tuple(40992, 82168), tuple(4552, 54096), tuple(77766, 7561), tuple(66864, 26750), tuple(88596, 49245), tuple(82930, 33251), tuple(36666, 90013), tuple(71198, 85282), tuple(30600, 59309), tuple(20617, 9735), tuple(96180, 42996), tuple(71846, 84832), tuple(10602, 94421), tuple(83237, 85278), tuple(63713, 14908), tuple(10950, 42931), tuple(33645, 74769), tuple(69501, 8603), tuple(63596, 44221), tuple(75895, 36447), tuple(43990, 4721), tuple(19047, 91357), tuple(35225, 30088), tuple(78898, 87190), tuple(9961, 52820), tuple(6085, 61213), tuple(98275, 84058), tuple(61081, 91315), tuple(28381, 30910), tuple(15828, 72673), tuple(79008, 65337), tuple(60317, 77055), tuple(78534, 50282), tuple(9752, 74831), tuple(62049, 63249), tuple(31455, 74843), tuple(2530, 28581), tuple(68894, 84015), tuple(12791, 14820), tuple(37148, 48411), tuple(88220, 90241), tuple(60582, 70112), tuple(49890, 26900), tuple(94049, 49226), tuple(19582, 98549), tuple(87964, 73614), tuple(54195, 95007), tuple(73593, 29007), tuple(35362, 47461), tuple(93549, 82201), tuple(74305, 49248), tuple(7030, 46073), tuple(69488, 56250), tuple(63045, 25229), tuple(44038, 24680), tuple(65848, 36124), tuple(53985, 95308), tuple(65103, 82209), tuple(26563, 58468), tuple(33153, 12174), tuple(29634, 21978), tuple(26461, 3090), tuple(88683, 4514), tuple(45782, 83561), tuple(56318, 92969), tuple(5587, 61631), tuple(92117, 78865), tuple(78775, 72654), tuple(47395, 49073), tuple(20541, 46928), tuple(71357, 58929), tuple(45184, 76268), tuple(59359, 77092), tuple(5298, 10020), tuple(580, 98499), tuple(9119, 16986), tuple(45518, 86386), tuple(63375, 76589), tuple(31660, 86697), tuple(35851, 65479), tuple(69920, 42815), tuple(30208, 56290), tuple(26343, 37505), tuple(56509, 63902), tuple(85757, 64286), tuple(78627, 79973), tuple(53080, 73913), tuple(49000, 2312), tuple(47315, 57178), tuple(13033, 26209), tuple(53799, 58615), tuple(54508, 22862), tuple(33971, 13889), tuple(22815, 37914), tuple(37426, 99282), tuple(54022, 81696), tuple(17550, 28739), tuple(99122, 99584), tuple(97931, 6054), tuple(46163, 94552), tuple(58328, 70123), tuple(79181, 99242), tuple(64725, 8569), tuple(69146, 39323), tuple(63563, 27537), tuple(96076, 21338), tuple(22936, 20737), tuple(69747, 58505), tuple(52140, 91592), tuple(17198, 19845), tuple(11003, 48066), tuple(1758, 76734), tuple(39891, 17323), tuple(25673, 35578), tuple(83704, 61339), tuple(71674, 51463), tuple(20522, 73773), tuple(37708, 70488), tuple(55492, 76397), tuple(36380, 58022), - tuple(31085, 23886), tuple(84428, 56183), tuple(61034, 65835), tuple(78297, 58728), tuple(992, 11080), tuple(26764, 59393), tuple(3987, 81148), tuple(57894, 50133), tuple(57393, 54819), tuple(639, 83329), tuple(37777, 47517), tuple(15526, 79423), tuple(88248, 9629), tuple(84464, 59890), tuple(56601, 14257), tuple(67283, 14213), tuple(16041, 41528), tuple(43407, 69763), tuple(51687, 8960), tuple(12121, 10409), tuple(60598, 29848), tuple(7839, 25189), tuple(40941, 3224), tuple(96406, 58803), tuple(61378, 82788), tuple(34541, 19703), tuple(98061, 97148), tuple(742, 7655), tuple(61975, 65313), tuple(64854, 93019), tuple(80244, 18168), tuple(92943, 6105), tuple(73969, 74411), tuple(47856, 26644), tuple(94126, 18220), tuple(81074, 61925), tuple(26953, 28483), tuple(65894, 12345), tuple(54749, 51964), tuple(14147, 41570), tuple(47147, 41809), tuple(8340, 35829), tuple(18897, 32342), tuple(36808, 22226), tuple(18644, 68853), tuple(4559, 12594), tuple(96970, 17395), tuple(50909, 77942), tuple(72027, 49411), tuple(98425, 79478), tuple(45899, 24474), tuple(46824, 48975), tuple(15479, 28870), tuple(45397, 76361), tuple(11420, 22096), tuple(74134, 37467), tuple(25579, 59803), tuple(74687, 44244), tuple(71744, 71562), tuple(49666, 32239), tuple(34931, 56027), tuple(50850, 78241), tuple(71040, 91240), tuple(61550, 47475), tuple(26200, 78373), tuple(54646, 87616), tuple(28950, 6489), tuple(51887, 75308), tuple(3912, 42532), tuple(82742, 42016), tuple(94497, 36681), tuple(62494, 3933), tuple(81131, 18766), tuple(51641, 94651), tuple(72285, 53143), tuple(73943, 73716), tuple(66264, 17309), tuple(43156, 99528), tuple(43697, 78216), tuple(84733, 50821), tuple(75899, 63920), tuple(18455, 7660), tuple(30431, 879), tuple(92204, 44911), tuple(30237, 57641), tuple(33407, 98166), tuple(47862, 73576), tuple(68097, 8833), tuple(2454, 49159), tuple(85772, 67530), tuple(19732, 4536), tuple(93827, 39013), tuple(47927, 1682), tuple(69143, 51598), tuple(87471, 99956), tuple(66362, 70450), tuple(34353, 94326), tuple(15692, 81083), tuple(36376, 91470), tuple(71608, 32098), tuple(38115, 56236), tuple(38822, 64266), tuple(87707, 52553), tuple(97973, 23102), tuple(49267, 68907), tuple(22649, 95175), tuple(33001, 75648), tuple(39808, 55971), tuple(5608, 41816), tuple(40313, 20564), tuple(72994, 53489), tuple(16983, 19806), tuple(46114, 57880), tuple(78622, 59284), tuple(78285, 63449), tuple(84552, 19702), tuple(34234, 63531), tuple(50848, 34774), tuple(87144, 18212), tuple(68214, 1005), tuple(60080, 42398), tuple(60514, 65965), tuple(75134, 65529), tuple(74107, 21666), tuple(59817, 68782), tuple(58940, 16958), tuple(19975, 92583), tuple(38480, 83257), tuple(44510, 44997), tuple(59488, 37263), tuple(71209, 47719), tuple(52080, 94252), tuple(87128, 14831), tuple(74001, 37313), tuple(47448, 76625), tuple(20205, 93608), tuple(30249, 75697), tuple(82584, 44654), tuple(54091, 4298), tuple(47736, 84084), tuple(34052, 55781), tuple(76012, 73648), tuple(63204, 52778), tuple(33692, 54616), tuple(91839, 55044), tuple(71674, 28475), tuple(8517, 3272), tuple(35810, 89905), tuple(35146, 62215), tuple(77262, 18227), tuple(34439, 87732), tuple(82690, 83051), - tuple(13687, 19633), tuple(66238, 44809), tuple(53103, 29620), tuple(93365, 87319), tuple(50385, 4755), tuple(89627, 50902), tuple(51790, 15255), tuple(48989, 19631), tuple(16759, 3431), tuple(90814, 19064), tuple(27831, 70254), tuple(75037, 52472), tuple(35010, 24454), tuple(31155, 22730), tuple(87603, 59121), tuple(1323, 23506), tuple(56458, 11866), tuple(21201, 50026), tuple(66671, 36029), tuple(21700, 69519), tuple(85239, 77201), tuple(95141, 57606), tuple(3242, 19997), tuple(52852, 15715), tuple(49676, 70126), tuple(73970, 9214), tuple(86637, 18585), tuple(14571, 65017), tuple(65446, 43663), tuple(20121, 50571), tuple(88634, 87895), tuple(65246, 62716), tuple(27591, 85239), tuple(33623, 14256), tuple(66412, 77902), tuple(16008, 6217), tuple(979, 1666), tuple(22123, 54052), tuple(21596, 7241), tuple(42224, 60675), tuple(23345, 4908), tuple(33780, 16632), tuple(45087, 11756), tuple(10951, 69020), tuple(46138, 47932), tuple(69936, 20048), tuple(5871, 96808), tuple(45595, 67151), tuple(11993, 21010), tuple(20462, 16667), tuple(41417, 88964), tuple(97285, 44958), tuple(20062, 41444), tuple(50808, 18976), tuple(43350, 88847), tuple(34906, 58686), tuple(18132, 2775), tuple(45165, 45298), tuple(75000, 4332), tuple(62157, 9375), tuple(88002, 89214), tuple(84772, 59883), tuple(78078, 35447), tuple(37222, 79319), tuple(43545, 20731), tuple(30761, 13677), tuple(27691, 25767), tuple(97750, 154), tuple(87313, 16187), tuple(78338, 15942), tuple(61168, 16886), tuple(73848, 48436), tuple(74553, 13738), tuple(50645, 37279), tuple(11352, 63404), tuple(85650, 43058), tuple(9553, 2374), tuple(5443, 50670), tuple(55599, 64207), tuple(74136, 41008), tuple(12098, 83456), tuple(98240, 65916), tuple(59743, 64811), tuple(53244, 68834), tuple(66230, 81024), tuple(13734, 83786), tuple(40505, 76939), tuple(93029, 55536), tuple(29943, 53386), tuple(33838, 94514), tuple(71750, 96644), tuple(30017, 8255), tuple(4931, 38730), tuple(2317, 55953), tuple(52389, 67128), tuple(70794, 44324), tuple(24102, 85008), tuple(46435, 65609), tuple(19704, 92026), tuple(99929, 86339), tuple(67622, 13853), tuple(95573, 72963), tuple(99732, 75024), tuple(60437, 5554), tuple(78047, 64444), tuple(95426, 63150), tuple(45798, 26879), tuple(45561, 12951), tuple(95873, 14825), tuple(36695, 13697), tuple(88796, 31855), tuple(52103, 53754), tuple(34496, 56371), tuple(33575, 17436), tuple(39528, 83385), tuple(36153, 48372), tuple(15, 80583), tuple(39015, 36980), tuple(44747, 11011), tuple(7314, 54178), tuple(37252, 52551), tuple(92128, 52377), tuple(66571, 16799), tuple(35841, 92438), tuple(85840, 1365), tuple(39413, 15660), tuple(14759, 58002), tuple(40158, 36685), tuple(68936, 15130), tuple(69488, 30639), tuple(40633, 23355), tuple(93700, 49305), tuple(23456, 96541), tuple(24357, 99667), tuple(25072, 2364), tuple(34734, 17869), tuple(72064, 40226), tuple(22712, 41911), tuple(77545, 19335), tuple(33945, 13408), tuple(63813, 44097), tuple(21816, 77633), tuple(93359, 9036), tuple(47001, 85666), tuple(21934, 56004), tuple(19148, 75033), tuple(88715, 15387), tuple(79158, 53971), tuple(59300, 19502), tuple(66517, 80891), tuple(13859, 63857), tuple(81036, 38595), tuple(8876, 37315), tuple(26819, 54318), tuple(32428, 13471), tuple(2615, 24036), tuple(56894, 82103), tuple(59056, 50983), tuple(15479, 66848), tuple(75766, 27048), tuple(52141, 81023), tuple(94385, 34233), tuple(93361, 79520), tuple(30867, 80795), tuple(46204, 9262), tuple(7946, 4084), tuple(78272, 87422), tuple(33639, 20782), tuple(31276, 45969), tuple(77720, 1335), tuple(16701, 70082), tuple(64963, 19854), tuple(7812, 8856), tuple(16170, 81834), tuple(22877, 7520), tuple(7132, 80996), tuple(1917, 27657), tuple(24737, 91605), tuple(8195, 31354), tuple(22561, 70343), tuple(67166, 74828), tuple(39705, 79118), tuple(43241, 67180), tuple(63526, 22190), tuple(67442, 18172), tuple(38470, 51567), tuple(48664, 43895), tuple(90585, 51544), tuple(11842, 9287), tuple(4231, 61462), tuple(13104, 65739), tuple(52455, 27122), tuple(32318, 92494), tuple(77028, 57920), tuple(52314, 80381), tuple(96749, 22065), tuple(50502, 39753), tuple(99787, 70903), tuple(97786, 28008), tuple(30462, 94898), tuple(59403, 38638), tuple(29827, 16423), tuple(23066, 87865), tuple(61889, 7941), tuple(23948, 78242), tuple(32395, 9480), tuple(36150, 31784), tuple(83146, 10647), tuple(68557, 26863), tuple(93482, 94228), tuple(28512, 89595), tuple(89424, 17693), tuple(68947, 47792), tuple(77918, 25182), tuple(31272, 59863), tuple(10836, 23758), tuple(76913, 75785), tuple(97969, 35902), tuple(55507, 58629), tuple(918, 8356), tuple(66592, 31055), tuple(95309, 59336), tuple(51917, 63380), tuple(79369, 21373), tuple(82766, 76689), tuple(90893, 53940), tuple(31697, 95784), tuple(78683, 89271), tuple(29009, 41573), tuple(9294, 6063), tuple(80573, 79144), tuple(59240, 77056), tuple(99089, 4209), tuple(90406, 92148), tuple(48879, 74391), tuple(22792, 13661), tuple(52282, 74677), tuple(48330, 54418), tuple(6927, 75154), tuple(35007, 12002), tuple(86885, 50845), tuple(74200, 7643), tuple(57915, 3525), tuple(89763, 13455), tuple(98115, 11696), tuple(56291, 17002), tuple(22153, 27947), tuple(85961, 14263), tuple(74214, 58127), tuple(21084, 3965), tuple(86912, 49029), tuple(43018, 83709), tuple(67265, 85823), tuple(68600, 15226), tuple(25932, 81914), tuple(73795, 41791), tuple(3158, 90348), tuple(7664, 81621), tuple(81479, 60118), tuple(93519, 76982), tuple(75440, 3594), tuple(88965, 91886), tuple(92353, 12231), tuple(39378, 69012), tuple(71183, 88536), tuple(36041, 65794), tuple(6724, 26825), tuple(5123, 57858), tuple(2529, 85540), tuple(33448, 94400), tuple(30462, 37765), tuple(30025, 78210), tuple(74349, 5545), tuple(73389, 88842), tuple(78939, 62188), tuple(27948, 62059), tuple(90829, 67200), tuple(44234, 57891), tuple(30700, 61417), tuple(44025, 71190), tuple(22931, 17654), tuple(23476, 87369), tuple(9619, 78967), tuple(24405, 67927), tuple(84754, 91358), tuple(14649, 71404), tuple(4410, 74364), tuple(61417, 87647), tuple(73297, 46647), tuple(32082, 33309), tuple(44590, 47039), tuple(67518, 99908), tuple(22881, 99699), tuple(36657, 17859), tuple(17556, 91128), tuple(58478, 75834), tuple(57048, 73419), tuple(90902, 11840), tuple(4126, 69894), tuple(30361, 54290), tuple(31297, 28595), tuple(54659, 98844), tuple(71265, 86805), tuple(49708, 38180), tuple(24851, 15906), tuple(86094, 67606), tuple(13603, 68975), tuple(3601, 93501), tuple(93480, 17205), tuple(82020, 17859), tuple(68339, 66849), tuple(66423, 9217), tuple(84188, 55471), tuple(28167, 29187), tuple(51166, 46084), tuple(88799, 39664), tuple(67897, 91643), tuple(43015, 68398), tuple(7181, 78174), tuple(6810, 46804), tuple(6034, 55550), tuple(51391, 29469), tuple(76157, 96247), tuple(54973, 75826), tuple(83858, 53576), tuple(87315, 76427), tuple(44051, 44348), tuple(78313, 73128), tuple(44289, 62279), tuple(61897, 87062), tuple(40767, 93761), tuple(91660, 32072), tuple(5223, 36184), tuple(30709, 51066), tuple(40581, 38123), tuple(27886, 39104), tuple(40119, 20754), tuple(27423, 22588), tuple(57235, 49005), tuple(88733, 11711), tuple(4282, 60158), tuple(18632, 3837), tuple(68953, 17510), tuple(21256, 60637), tuple(71727, 176), tuple(25865, 77991), tuple(71032, 40412), tuple(91622, 20288), tuple(49014, 99799), tuple(13599, 5764), tuple(30979, 2454), tuple(40338, 41233), tuple(57336, 50430), tuple(72959, 57286), tuple(85827, 44852), tuple(71907, 33794), tuple(95577, 89084), tuple(72485, 24104), tuple(20306, 76152), tuple(26079, 29381), tuple(46907, 70771), tuple(50365, 90449), tuple(47884, 95986), tuple(88953, 37962), tuple(17407, 91977), tuple(91616, 97270), tuple(85966, 44410), tuple(90389, 12295), tuple(80529, 64553), tuple(21003, 37956), tuple(32923, 30387), tuple(78916, 68050), tuple(15505, 9628), tuple(59050, 38770), tuple(30091, 35499), tuple(30658, 70636), tuple(3160, 24877), tuple(8277, 49676), tuple(74352, 93399), tuple(54074, 53202), tuple(1643, 13321), tuple(71865, 70329), tuple(15251, 5871), tuple(63695, 36988), tuple(96887, 2370), tuple(43098, 14907), tuple(51946, 670), tuple(53294, 72621), tuple(57964, 99634), tuple(17761, 42977), tuple(77571, 38986), tuple(50417, 76385), tuple(80499, 52080), tuple(24301, 56623), tuple(43462, 34196), tuple(49381, 50953), tuple(34053, 74119), tuple(62887, 15142), tuple(69260, 13561), tuple(42848, 87275), tuple(91274, 37268), tuple(87880, 18740), tuple(61314, 8320), tuple(59707, 95633), tuple(38875, 94226), tuple(1415, 62574), tuple(25521, 90678), tuple(48366, 41803), tuple(497, 78256), tuple(26209, 34736), tuple(32946, 44203), tuple(85348, 59409), tuple(25037, 86769), tuple(12753, 78083), tuple(7901, 34859), tuple(70495, 40252), tuple(93318, 95446), tuple(64601, 60123), tuple(18929, 93512), tuple(90647, 85301), tuple(24280, 1900), tuple(82057, 44496), tuple(72227, 33583), tuple(7109, 24591), tuple(37709, 22897), tuple(1237, 16650), tuple(3383, 97434), tuple(72245, 64486), tuple(10201, 46701), tuple(70246, 27545), tuple(98647, 31169), tuple(21901, 75927), tuple(50188, 21441), tuple(89577, 37821), tuple(98823, 69937), tuple(19263, 97734), tuple(92937, 44136), tuple(626, 95680), tuple(12956, 24497), tuple(454, 41351), tuple(40585, 99858), tuple(26190, 82119), tuple(83467, 58869), tuple(39533, 27469), tuple(84276, 26501), tuple(29838, 24478), tuple(78589, 73649), tuple(85241, 37456), tuple(37739, 27890), tuple(30935, 18054), tuple(44388, 47592), tuple(46287, 11414), tuple(41275, 7284), tuple(61172, 59962), tuple(69172, 14436), tuple(11827, 43746), tuple(70455, 93926), tuple(35662, 19338), tuple(24436, 53824), tuple(28435, 33323), tuple(54767, 30980), tuple(91379, 46216), tuple(2656, 98555), tuple(17703, 75278), tuple(52299, 81894), tuple(88460, 76874), tuple(29298, 37649), tuple(87510, 3320), tuple(55513, 94080), tuple(77940, 95554), tuple(15605, 18918), tuple(49452, 85395), tuple(85737, 18109), tuple(73279, 56989), tuple(17461, 32967), tuple(19605, 43424), tuple(93999, 7945), tuple(29865, 94947), tuple(39164, 19448), tuple(88080, 7422), - tuple(436, 99800), tuple(27482, 44994), tuple(73495, 66844), tuple(47160, 66210), tuple(98801, 18553), tuple(33222, 8808), tuple(31037, 15092), tuple(92621, 47184), tuple(54888, 65801), tuple(30873, 33071), tuple(61182, 77132), tuple(81992, 40879), tuple(10268, 38847), tuple(26012, 24149), tuple(85131, 81923), tuple(10950, 55371), tuple(17788, 48443), tuple(39646, 51743), tuple(36019, 76895), tuple(9763, 36927), tuple(19582, 16771), tuple(68211, 84210), tuple(9412, 55904), tuple(44002, 9077), tuple(36583, 1638), tuple(97906, 20583), tuple(12863, 74173), tuple(29698, 34767), tuple(34469, 78263), tuple(87561, 58797), tuple(77844, 10556), tuple(68239, 63155), tuple(41560, 93635), tuple(52548, 95710), tuple(11161, 632), tuple(91657, 97784), tuple(93088, 89618), tuple(42604, 94389), tuple(4198, 51324), tuple(96597, 97093), tuple(77382, 89074), tuple(41381, 89801), tuple(25857, 67775), tuple(41422, 59757), tuple(39574, 93308), tuple(85990, 91621), tuple(49182, 92967), tuple(84187, 73625), tuple(92495, 93334), tuple(12471, 33289), tuple(49105, 76813), tuple(26330, 94103), tuple(70805, 33031), tuple(49193, 18360), tuple(18002, 29159), tuple(70447, 50454), tuple(44081, 3675), tuple(96495, 16455), tuple(98675, 5876), tuple(2913, 30032), tuple(91048, 91939), tuple(50496, 3621), tuple(7244, 20490), tuple(89364, 30152), tuple(43588, 25656), tuple(42086, 70055), tuple(90150, 95763), tuple(31727, 49598), tuple(92625, 29569), tuple(54148, 81753), tuple(77721, 57094), tuple(67706, 83472), tuple(26935, 9073), tuple(29554, 1952), tuple(33394, 13797), tuple(77797, 84190), tuple(65692, 40517), tuple(86640, 91307), tuple(56461, 17709), tuple(30204, 2620), tuple(72787, 87380), tuple(51439, 56294), tuple(25033, 30268), tuple(27379, 59569), tuple(11651, 62401), tuple(69998, 96886), tuple(17278, 56849), tuple(71326, 41808), tuple(50372, 4986), tuple(1711, 73335), tuple(71837, 63661), tuple(78810, 60882), tuple(62918, 6111), tuple(76267, 27414), tuple(12536, 747), tuple(24221, 44906), tuple(2136, 84763), tuple(7736, 18765), tuple(71822, 70209), tuple(55828, 97876), tuple(27029, 80557), tuple(47068, 2260), tuple(47441, 81681), tuple(95204, 83508), tuple(94992, 98690), tuple(86479, 34930), tuple(51759, 64962), tuple(23363, 25795), tuple(50675, 29266), tuple(54059, 35176), tuple(37985, 70844), tuple(270, 33485), tuple(88135, 28988), tuple(97160, 54235), tuple(71087, 50356), tuple(42552, 11173), tuple(58508, 4623), tuple(15663, 61919), tuple(7672, 63865), tuple(14458, 76549), tuple(94036, 65780), tuple(76076, 98795), tuple(65158, 94233), tuple(38869, 33986), tuple(25926, 48043), tuple(8196, 61978), tuple(93547, 83000), tuple(13218, 70869), tuple(40220, 153), tuple(40683, 8335), tuple(38208, 71925), tuple(63199, 70045), tuple(3199, 10537), tuple(16665, 29551), tuple(91333, 59035), tuple(60598, 74055), tuple(18820, 76617), tuple(31822, 14637), tuple(1508, 13279), tuple(99959, 73482), tuple(69854, 34920), tuple(59650, 1074), tuple(1538, 52026), tuple(84941, 6002), tuple(82892, 20921), tuple(88023, 79990), tuple(6945, 92700), tuple(2389, 62469), tuple(38515, 78260), tuple(34721, 56417), tuple(37208, 71058), tuple(78134, 76540), tuple(18831, 53863), - tuple(81277, 26395), tuple(57176, 69906), tuple(55080, 20726), tuple(65843, 9495), tuple(27703, 24879), tuple(60594, 53447), tuple(72896, 95513), tuple(87884, 50244), tuple(25281, 16427), tuple(14122, 6399), tuple(65765, 45679), tuple(38160, 38650), tuple(76820, 91595), tuple(74663, 37143), tuple(35220, 16865), tuple(54664, 88840), tuple(7671, 19781), tuple(7255, 87276), tuple(92182, 47030), tuple(98922, 49381), tuple(21954, 20742), tuple(54674, 19730), tuple(74694, 81672), tuple(31070, 97794), tuple(22759, 3530), tuple(6085, 72621), tuple(17392, 26996), tuple(82220, 41192), tuple(48225, 41272), tuple(42850, 54369), tuple(10817, 25978), tuple(18889, 81907), tuple(33157, 51203), tuple(64325, 5137), tuple(54695, 50597), tuple(32244, 99170), tuple(64264, 23137), tuple(22912, 47267), tuple(70570, 51635), tuple(96224, 20571), tuple(91110, 65969), tuple(2598, 66368), tuple(80926, 52434), tuple(1538, 77141), tuple(10149, 21484), tuple(4981, 58501), tuple(5154, 55414), tuple(66791, 35619), tuple(9905, 90667), tuple(83595, 75525), tuple(31989, 37492), tuple(41740, 48920), tuple(33876, 10878), tuple(48654, 50120), tuple(85890, 46200), tuple(67100, 30387), tuple(76475, 38916), tuple(1654, 1657), tuple(93842, 71472), tuple(14560, 97747), tuple(24866, 74187), tuple(11477, 2492), tuple(94590, 95965), tuple(29363, 58015), tuple(58783, 5304), tuple(27337, 87412), tuple(17695, 41885), tuple(91837, 75700), tuple(271, 49400), tuple(33438, 84607), tuple(78520, 46982), tuple(38817, 66995), tuple(2031, 58147), tuple(80354, 75561), tuple(7752, 55141), tuple(22715, 29219), tuple(10069, 69521), tuple(1711, 23325), tuple(88218, 94290), tuple(44895, 32844), tuple(62152, 964), tuple(32945, 57419), tuple(19030, 51353), tuple(53097, 36525), tuple(76592, 2389), tuple(53949, 27282), tuple(10569, 90204), tuple(94318, 48527), tuple(13855, 27099), tuple(41107, 11962), tuple(39143, 81444), tuple(66004, 38387), tuple(55906, 61394), tuple(34933, 33952), tuple(53868, 82829), tuple(86213, 80445), tuple(40351, 56305), tuple(97656, 90533), tuple(48021, 7262), tuple(49406, 38294), tuple(57170, 7675), tuple(7040, 8039), tuple(65129, 76668), tuple(7554, 74457), tuple(65930, 2421), tuple(12739, 37328), tuple(17174, 68325), tuple(63207, 21315), tuple(70066, 44503), tuple(19112, 8081), tuple(80329, 65152), tuple(76480, 39600), tuple(75262, 45432), tuple(24382, 67054), tuple(75360, 90438), tuple(21786, 19182), tuple(2050, 82428), tuple(34831, 92670), tuple(69746, 44939), tuple(6130, 58630), tuple(62478, 14374), tuple(35730, 71761), tuple(20641, 66145), tuple(86551, 64076), tuple(37559, 79379), tuple(8215, 82203), tuple(33701, 9187), tuple(22679, 38196), tuple(17476, 25994), tuple(4391, 62658), tuple(87726, 32997), tuple(22552, 65818), tuple(18115, 42587), tuple(64374, 97689), tuple(64896, 49572), tuple(98479, 75547), tuple(881, 32287), tuple(25305, 16000), tuple(295, 67454), tuple(67642, 87363), tuple(50945, 31544), tuple(95966, 9972), tuple(44139, 28252), tuple(83682, 88471), tuple(34615, 665), tuple(13190, 85527), tuple(32195, 81130), tuple(22310, 12209), tuple(27910, 60272), tuple(40133, 5905), tuple(29352, 36686), tuple(76443, 64096), tuple(79522, 79616), tuple(63465, 96631), tuple(95604, 90216), tuple(88548, 12411), tuple(63732, 50843), tuple(1478, 11113), tuple(41472, 43329), tuple(35207, 78855), tuple(95093, 22581), tuple(67812, 1348), tuple(96472, 75947), tuple(18984, 63538), tuple(84782, 72178), tuple(53524, 46634), tuple(95525, 15476), tuple(82732, 89272), tuple(59603, 2032), tuple(74431, 78243), tuple(47712, 51835), tuple(82671, 67076), tuple(63302, 96469), tuple(77655, 95524), tuple(11099, 31832), tuple(77967, 32724), tuple(85070, 36190), tuple(93260, 32825), tuple(51348, 34162), tuple(40912, 26841), tuple(27227, 21822), tuple(15979, 96933), tuple(76990, 90541), tuple(36121, 90437), tuple(85571, 9605), tuple(85987, 46881), tuple(86738, 82375), tuple(80188, 48238), tuple(64608, 41916), tuple(79866, 9442), tuple(28100, 95607), tuple(10136, 19628), tuple(6078, 59019), tuple(8971, 11986), tuple(75703, 209), tuple(81982, 98701), tuple(30455, 66367), tuple(92825, 84403), tuple(73144, 4923), tuple(62835, 75533), tuple(90925, 56381), tuple(83853, 86786), tuple(66906, 28576), tuple(69683, 74216), tuple(2171, 22531), tuple(6591, 52559), tuple(59362, 88732), tuple(49865, 38016), tuple(82881, 9151), tuple(71356, 27365), tuple(91463, 45944), tuple(71040, 12264), tuple(62116, 27681), tuple(84941, 82284), tuple(57515, 60713), tuple(45227, 74196), tuple(77502, 17765), tuple(54055, 29484), tuple(33708, 46220), tuple(37344, 75102), tuple(80480, 37942), tuple(95782, 36781), tuple(14092, 82457), tuple(25284, 82297), tuple(85150, 50659), tuple(90037, 99122), tuple(49584, 48321), tuple(8028, 65210), tuple(8162, 32091), tuple(1609, 22211), tuple(38548, 1023), tuple(35864, 43021), tuple(54196, 30754), tuple(88535, 90185), tuple(5518, 97226), tuple(79272, 57758), tuple(61381, 86419), tuple(38344, 86877), tuple(72629, 24694), tuple(12563, 11747), tuple(81456, 44023), tuple(55536, 65195), tuple(67741, 12182), tuple(64250, 29636), tuple(45252, 9834), tuple(16576, 40098), tuple(65924, 11123), tuple(14090, 83443), tuple(95180, 92697), tuple(92452, 7957), tuple(21244, 29717), tuple(50253, 62030), tuple(21401, 70735), tuple(86803, 89625), tuple(17066, 52757), tuple(74570, 89660), tuple(11647, 74336), tuple(22463, 68191), tuple(41693, 67542), tuple(12994, 21955), tuple(3180, 27966), tuple(37236, 51099), tuple(68568, 62778), tuple(2671, 68542), tuple(42771, 4268), tuple(7578, 12919), tuple(33838, 61136), tuple(87904, 48076), tuple(3781, 57164), tuple(83289, 14414), tuple(29626, 91409), tuple(92536, 13946), tuple(88125, 34961), tuple(71541, 41249), tuple(21045, 85183), tuple(21628, 62294), tuple(17768, 22390), tuple(5892, 10231), tuple(85902, 84414), tuple(44078, 87944), tuple(64981, 31018), tuple(85690, 11077), tuple(4711, 9309), tuple(39077, 26339), tuple(85313, 99603), tuple(20267, 31090), tuple(92684, 11726), tuple(17887, 17415), tuple(23064, 83141), tuple(40925, 87246), tuple(17575, 71065), tuple(29396, 86192), tuple(10859, 2881), tuple(50457, 42309), tuple(86972, 41824), tuple(80954, 76970), tuple(11664, 94322), tuple(4286, 93562), tuple(9093, 32589), tuple(81261, 96519), tuple(67886, 3086), tuple(74132, 43197), tuple(94549, 26192), tuple(55498, 75374), tuple(93813, 32911), tuple(6727, 51629), tuple(93543, 55373), tuple(48682, 83316), tuple(32468, 77339), tuple(60028, 4896), tuple(34177, 67214), tuple(35429, 16686), tuple(72814, 39944), tuple(82791, 97364), tuple(21249, 36007), tuple(85536, 84815), tuple(32784, 34349), tuple(2391, 45661), tuple(62595, 30434), tuple(56513, 65051), tuple(22536, 99421), tuple(80603, 62636), tuple(23609, 19082), tuple(33262, 63428), tuple(45307, 96049), tuple(75079, 62835), tuple(52361, 19400), tuple(43845, 89033), tuple(54257, 33298), tuple(31133, 35836), tuple(56725, 10158), tuple(73650, 10456), tuple(77548, 28249), tuple(57998, 79546), tuple(97895, 93196), tuple(472, 18004), tuple(67178, 2684), tuple(34612, 36352), tuple(84970, 98816), tuple(32474, 5895), tuple(60388, 75957), tuple(81643, 26457), tuple(30070, 55744), tuple(14725, 58396), tuple(66451, 69932), tuple(59264, 59569), tuple(98045, 63704), tuple(11925, 87468), tuple(24701, 6280), tuple(47797, 84240), tuple(998, 13575), tuple(37865, 72036), tuple(71339, 10339), tuple(28248, 7481), tuple(33945, 97829), tuple(85194, 8904), tuple(39669, 73192), tuple(24326, 65934), tuple(14438, 72729), tuple(76676, 35176), tuple(97604, 95931), tuple(36032, 61056), tuple(65788, 20291), tuple(73067, 60718), tuple(75742, 27362), tuple(45393, 74792), tuple(49586, 13048), tuple(57961, 68534), tuple(78171, 47271), tuple(76064, 41030), tuple(88738, 10155), tuple(87757, 63134), tuple(95783, 76438), tuple(77935, 85529), tuple(79345, 69555), tuple(94483, 58668), tuple(9432, 40485), tuple(43585, 376), tuple(61737, 97784), tuple(1593, 37078), tuple(11129, 23793), tuple(94341, 64063), tuple(41896, 70446), tuple(77473, 33532), tuple(66748, 65844), tuple(17802, 27920), tuple(22891, 12901), tuple(40435, 43348), tuple(27956, 16989), tuple(29550, 63725), tuple(49453, 61216), tuple(25570, 97985), tuple(39599, 83449), tuple(96373, 12817), tuple(48357, 40473), tuple(99949, 33182), tuple(85434, 45421), tuple(28033, 46182), tuple(80932, 26691), tuple(99522, 29180), tuple(70868, 31903), tuple(63217, 51504), tuple(70267, 29484), tuple(36096, 28371), tuple(45710, 2823), tuple(85697, 3912), tuple(25410, 70358), tuple(73558, 82834), tuple(28617, 80585), tuple(81874, 57512), tuple(76797, 45045), tuple(22091, 84124), tuple(68372, 42189), tuple(66320, 83215), tuple(23013, 22530), tuple(33735, 97800), tuple(54318, 99061), tuple(18025, 39513), tuple(69059, 39780), tuple(58425, 39790), tuple(10222, 96791), tuple(17061, 3138), tuple(92416, 5773), tuple(20023, 31736), tuple(47846, 74511), tuple(52598, 26398), tuple(3738, 44490), tuple(53346, 79223), tuple(31991, 57557), tuple(34592, 98693), tuple(86926, 20881), tuple(14108, 87654), tuple(4889, 34982), tuple(77283, 73201), tuple(13668, 22633), tuple(50763, 95152), tuple(97292, 54613), tuple(22782, 30081), tuple(21536, 46551), tuple(45965, 4172), tuple(75537, 79254), tuple(41267, 86819), tuple(57723, 75200), tuple(60404, 27252), tuple(80305, 57892), tuple(50103, 24015), tuple(69409, 13132), tuple(30717, 56384), tuple(54568, 79877), tuple(8584, 8359), tuple(41416, 64392), tuple(32898, 92944), tuple(5262, 35907), tuple(88652, 77951), tuple(16619, 2797), tuple(42494, 65581), tuple(21009, 18555), tuple(29084, 28068), tuple(29464, 35568), tuple(7288, 52066), tuple(32378, 48725), tuple(95300, 62168), tuple(52295, 97110), tuple(36862, 87786), tuple(12224, 86684), tuple(85883, 34167), tuple(45369, 76651), tuple(95460, 42855), tuple(34354, 87238), tuple(7214, 96375), tuple(58720, 87947), tuple(55009, 10506), tuple(98506, 6081), tuple(46068, 81113), tuple(99065, 58657), tuple(81820, 28081), tuple(90611, 57235), tuple(7661, 72835), tuple(93195, 90418), tuple(34452, 20146), - tuple(39857, 78712), tuple(47185, 52074), tuple(9879, 51807), tuple(92457, 92377), tuple(36082, 17723), tuple(21905, 43398), tuple(35902, 54380), tuple(21791, 6804), tuple(50774, 35200), tuple(11540, 83934), tuple(54574, 57602), tuple(99878, 35568), tuple(28171, 68210), tuple(58132, 17222), tuple(91755, 80585), tuple(18944, 65785), tuple(78535, 70725), tuple(42599, 65291), tuple(62274, 97160), tuple(76487, 23798), tuple(34741, 36988), tuple(7286, 61119), tuple(36824, 29665), tuple(82478, 60722), tuple(90995, 28025), tuple(71438, 40230), tuple(83152, 45104), tuple(1812, 91619)) - - - DROP TABLE IF EXISTS test_in - + tuple(11741, 36315), tuple(22742, 17581), tuple(35808, 3110), tuple(98904, 30407), tuple(4584, 13383), tuple(28585, 69669), tuple(94823, 29715), tuple(9551, 36389), tuple(77997, 45746), tuple(49894, 55722), tuple(23415, 69459), tuple(58246, 85144), tuple(74136, 18102), tuple(97366, 85724), tuple(34271, 51601), tuple(47535, 70883), tuple(59443, 90103), tuple(45213, 45811), tuple(62741, 86898), tuple(17324, 50034), tuple(62080, 25193), tuple(89524, 4421), tuple(13476, 51456), tuple(69198, 56718), tuple(58024, 22969), tuple(65210, 67941), tuple(32561, 44881), tuple(62295, 67448), tuple(66135, 95453), tuple(9417, 20443), tuple(82486, 23745), tuple(19185, 99041), tuple(40662, 91714), tuple(3423, 58624), tuple(4512, 74502), tuple(67772, 98023), tuple(69575, 75779), tuple(69107, 62805), tuple(517, 33801), tuple(47406, 7581), tuple(81108, 10546), tuple(12976, 47001), tuple(16742, 83811), tuple(44593, 82124), tuple(52731, 34642), tuple(81725, 20555), tuple(94126, 91919), tuple(24800, 59302), tuple(97253, 39249), tuple(71692, 10769), tuple(88721, 56321), tuple(7019, 69771), tuple(31464, 61774), tuple(29597, 19263), tuple(65557, 31875), tuple(28653, 69636), tuple(58074, 76848), tuple(15906, 80620), tuple(18259, 40193), tuple(99991, 4769), tuple(98935, 99269), tuple(12123, 60124), tuple(20787, 47346), tuple(13526, 33592), tuple(95370, 40350), tuple(17479, 42884), tuple(58368, 83218), tuple(63290, 74406), tuple(97030, 35102), tuple(45298, 27660), tuple(64593, 21262), tuple(76268, 82641), tuple(1107, 44044), tuple(21427, 79959), tuple(85180, 62412), tuple(7359, 1318), tuple(83618, 9762), tuple(1425, 55804), tuple(32874, 97943), tuple(68191, 38742), tuple(41715, 17902), tuple(3771, 15032), tuple(7848, 74950), tuple(33881, 40904), tuple(75295, 26151), tuple(75775, 13760), tuple(90262, 89822), tuple(88169, 18679), tuple(57506, 32356), tuple(94983, 44281), tuple(37385, 37432), tuple(18248, 48162), tuple(45573, 66278), tuple(25277, 72788), tuple(26977, 36778), tuple(26254, 61758), tuple(12860, 48026), tuple(96819, 3339), tuple(13134, 1173), tuple(26822, 53374), tuple(15989, 29698), tuple(11258, 54515), tuple(37866, 34928), tuple(22996, 26577), tuple(39952, 42732), tuple(6754, 70595), tuple(86245, 44669), tuple(47044, 34170), tuple(6789, 45220), tuple(31706, 2090), tuple(42582, 40023), tuple(35147, 46591), tuple(88210, 11307), tuple(53644, 7680), tuple(11280, 91075), tuple(42961, 65122), tuple(40066, 52185), tuple(20050, 6154), tuple(98440, 20393), tuple(88992, 75432), tuple(32386, 66731), tuple(36952, 34149), tuple(18453, 32715), tuple(84413, 10378), tuple(59440, 2374), tuple(45354, 85009), tuple(50382, 66510), tuple(64428, 95401), tuple(9336, 41760), tuple(26317, 91416), tuple(81941, 99504), tuple(26600, 53522), tuple(81069, 40236), tuple(51126, 27911), tuple(97144, 14243), tuple(62738, 50287), tuple(37372, 28962), tuple(12053, 9090), tuple(69492, 95524), tuple(68141, 52931), tuple(17276, 16487), tuple(69227, 25949), tuple(14143, 70193), tuple(7077, 53032), tuple(65463, 74082), tuple(94997, 66496), tuple(80443, 55832), tuple(66796, 5970), tuple(15852, 95662), tuple(81559, 97272), tuple(55851, 18977), tuple(91142, 48976), tuple(91143, 950), tuple(79225, 31004), tuple(61310, 20760), tuple(74541, 90842), tuple(80322, 11630), tuple(84631, 544), tuple(66785, 86591), tuple(25650, 63252), tuple(59635, 18586), tuple(2964, 6741), tuple(37091, 71148), tuple(11984, 43077), tuple(87505, 62049), tuple(61925, 92290), tuple(18808, 3937), tuple(8300, 33268), tuple(70850, 50661), tuple(86024, 73730), tuple(85161, 47116), tuple(50193, 89155), tuple(37773, 40845), tuple(9251, 41688), tuple(6940, 65399), tuple(42479, 95630), tuple(19401, 43102), tuple(48069, 36040), tuple(62760, 95013), tuple(394, 2641), tuple(32567, 29306), tuple(13870, 58835), tuple(98248, 47291), tuple(49803, 4523), tuple(40222, 12883), tuple(53576, 73105), tuple(88265, 23629), tuple(67865, 67875), tuple(33473, 27144), tuple(80219, 53893), tuple(74878, 47341), tuple(78070, 84803), tuple(30003, 5600), tuple(41103, 6145), tuple(83490, 81076), tuple(55059, 66736), tuple(45015, 10239), tuple(79555, 85819), tuple(81808, 34970), tuple(19235, 85480), tuple(91807, 52177), tuple(40887, 87009), tuple(5003, 2687), tuple(64964, 88122), tuple(765, 94893), tuple(93573, 20504), tuple(28854, 38438), tuple(94244, 93475), tuple(72996, 84801), tuple(75427, 81692), tuple(63161, 98637), tuple(18814, 61343), tuple(22863, 60110), tuple(8949, 12694), tuple(19675, 94313), tuple(43857, 74073), tuple(15737, 58218), tuple(48895, 68474), tuple(22220, 92926), tuple(69055, 50282), tuple(40532, 74934), tuple(59062, 66405), tuple(85784, 87704), tuple(58494, 88222), tuple(2260, 20401), tuple(73112, 99666), tuple(46739, 95433), tuple(21179, 85119), tuple(11545, 38801), tuple(59993, 50866), tuple(10086, 4709), tuple(70560, 29611), tuple(27095, 89017), tuple(6896, 2279), tuple(92506, 5013), tuple(48600, 90491), tuple(18782, 54638), tuple(54337, 82734), tuple(52054, 13481), tuple(38297, 56559), tuple(15998, 30591), tuple(89789, 7522), tuple(18149, 28725), tuple(3532, 28625), tuple(70934, 49617), tuple(84599, 55664), tuple(74229, 52269), tuple(55431, 11893), tuple(32807, 72543), tuple(83882, 53025), tuple(11490, 83442), tuple(14844, 88612), tuple(12526, 45953), tuple(906, 2231), tuple(68240, 95612), tuple(18818, 31535), tuple(57774, 91290), tuple(67250, 67400), tuple(77332, 23550), tuple(42332, 57775), tuple(28792, 11539), tuple(19108, 34608), tuple(12399, 38591), tuple(7329, 10740), tuple(84288, 50928), tuple(29461, 17629), tuple(63884, 88489), tuple(47479, 61085), tuple(75357, 57255), tuple(60107, 94046), tuple(32934, 66312), tuple(28615, 42600), tuple(55553, 85213), tuple(57838, 91426), tuple(9783, 11513), tuple(73677, 28821), tuple(75408, 75561), tuple(22995, 59224), tuple(74874, 54145), tuple(18513, 75901), tuple(46440, 69414), tuple(36072, 22263), tuple(60560, 73325), tuple(69967, 93358), tuple(75949, 98634), tuple(3688, 57991), tuple(43482, 94541), tuple(40922, 31011), tuple(57763, 74497), tuple(93576, 96392), tuple(83038, 80656), tuple(47757, 87045), tuple(14061, 53465), tuple(65619, 33775), tuple(11341, 6702), tuple(6249, 87358), tuple(15766, 85937), tuple(13135, 93945), tuple(24495, 95900), tuple(80359, 1739), tuple(15468, 73426), tuple(49240, 44999), tuple(82839, 90808), tuple(87438, 75613), tuple(348, 73144), tuple(99523, 85853), tuple(21557, 70210), tuple(64933, 1672), tuple(38154, 17477), tuple(97136, 67363), tuple(96491, 8038), tuple(97981, 3434), tuple(54372, 27038), tuple(88480, 86675), tuple(21028, 21083), tuple(43197, 4440), tuple(31702, 78290), tuple(66631, 24438), tuple(11482, 17922), tuple(90351, 39503), tuple(46186, 32439), tuple(73828, 6640), tuple(56916, 26029), tuple(62840, 1815), tuple(20281, 28488), tuple(18211, 30043), tuple(65211, 93012), tuple(43614, 58012), tuple(90322, 77343), tuple(64293, 94525), tuple(59489, 39760), tuple(93219, 78440), tuple(74613, 9732), tuple(38085, 19191), tuple(58029, 48186), tuple(88762, 1764), tuple(28627, 21993), tuple(49975, 41225), tuple(70486, 43480), tuple(82764, 96425), tuple(27218, 78327), tuple(17844, 73333), tuple(70463, 37629), tuple(10500, 33826), tuple(97343, 66575), tuple(82833, 51210), tuple(77353, 45073), tuple(27163, 39728), tuple(78076, 46691), tuple(80302, 39342), tuple(77142, 1319), tuple(87403, 80110), tuple(53805, 27786), tuple(50558, 74264), tuple(83146, 31358), tuple(11567, 4438), tuple(30041, 54287), tuple(91731, 18496), tuple(57591, 93894), tuple(72534, 59009), tuple(98064, 59148), tuple(69626, 66615), tuple(20951, 43949), tuple(61960, 68060), tuple(48892, 67918), tuple(61321, 56222), tuple(75424, 77260), tuple(4916, 81929), tuple(68892, 81531), tuple(28096, 28548), tuple(62016, 107), tuple(8593, 12030), tuple(66743, 36772), tuple(60174, 15106), tuple(52844, 1923), tuple(34768, 22065), tuple(88988, 62910), tuple(79214, 2998), tuple(25675, 31376), tuple(69959, 3614), tuple(43885, 31708), tuple(12206, 46548), tuple(69924, 19343), tuple(12984, 38980), tuple(58250, 69438), tuple(2580, 48684), tuple(38112, 37124), tuple(21842, 43150), tuple(59384, 21921), tuple(19908, 46678), tuple(73396, 79529), tuple(8274, 1557), tuple(36975, 65519), tuple(81069, 18712), tuple(13692, 9148), tuple(60617, 84762), tuple(75749, 66154), tuple(80375, 24553), tuple(4257, 47056), tuple(76880, 7687), tuple(40714, 43448), tuple(79112, 74791), tuple(33119, 72730), tuple(17670, 89183), tuple(51614, 3921), tuple(21247, 39857), tuple(86756, 67673), tuple(32792, 70035), tuple(5917, 7197), tuple(1762, 23130), tuple(6455, 63664), tuple(32806, 3729), tuple(60469, 20511), tuple(12522, 15149), tuple(98106, 79338), tuple(84754, 11162), tuple(52058, 17973), tuple(28789, 1521), tuple(32766, 36325), tuple(78914, 40453), tuple(70297, 71854), tuple(9313, 45190), tuple(54559, 66227), tuple(22342, 43860), tuple(44152, 84294), tuple(36913, 93173), tuple(88523, 36338), tuple(82234, 71140), tuple(8328, 22947), tuple(73250, 88125), tuple(74356, 16820), tuple(94472, 37349), tuple(23126, 87806), tuple(40315, 88729), tuple(19935, 19145), tuple(93312, 65719), tuple(8477, 33108), tuple(86660, 69525), tuple(75557, 66964), tuple(60437, 57494), tuple(94419, 42524), tuple(95372, 72274), tuple(49866, 85685), tuple(96808, 39404), tuple(62961, 72507), tuple(38634, 70815), tuple(91379, 42430), tuple(66359, 98699), tuple(24382, 4186), tuple(4003, 77760), tuple(87840, 75265), tuple(57641, 68871), tuple(9773, 15942), tuple(5664, 51289), tuple(47923, 31308), tuple(58632, 82468), tuple( diff --git a/tests/queries/0_stateless/01881_to_week_monotonic_fix.reference b/tests/queries/0_stateless/01881_to_week_monotonic_fix.reference new file mode 100644 index 00000000000..c8139ded0dc --- /dev/null +++ b/tests/queries/0_stateless/01881_to_week_monotonic_fix.reference @@ -0,0 +1,4 @@ +1 2020-11-11 +1 2021-01-01 +1 2020-11-11 +1 2021-01-01 diff --git a/tests/queries/0_stateless/01881_to_week_monotonic_fix.sql b/tests/queries/0_stateless/01881_to_week_monotonic_fix.sql new file mode 100644 index 00000000000..c73a7909faf --- /dev/null +++ b/tests/queries/0_stateless/01881_to_week_monotonic_fix.sql @@ -0,0 +1,11 @@ +drop table if exists test_tbl; + +create table test_tbl (vend_nm String, ship_dt Date) engine MergeTree partition by toWeek(ship_dt) order by vend_nm; + +insert into test_tbl values('1', '2020-11-11'), ('1', '2021-01-01'); + +select * From test_tbl where ship_dt >= toDate('2020-11-01') and ship_dt <= toDate('2021-05-05') order by ship_dt; + +select * From test_tbl where ship_dt >= toDate('2020-01-01') and ship_dt <= toDate('2021-05-05') order by ship_dt; + +drop table test_tbl; diff --git a/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.reference b/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.reference new file mode 100644 index 00000000000..30a4d52afe1 --- /dev/null +++ b/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.reference @@ -0,0 +1,2 @@ +100 +1 diff --git a/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.sql b/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.sql new file mode 100644 index 00000000000..d6cde1ef7a8 --- /dev/null +++ b/tests/queries/0_stateless/01882_check_max_parts_to_merge_at_once.sql @@ -0,0 +1,32 @@ +DROP TABLE IF EXISTS limited_merge_table; + +SET max_threads = 1; +SET max_block_size = 1; +SET min_insert_block_size_rows = 1; + +CREATE TABLE limited_merge_table +( + key UInt64 +) +ENGINE = MergeTree() +ORDER BY key +SETTINGS max_parts_to_merge_at_once = 3; + +SYSTEM STOP MERGES limited_merge_table; + +INSERT INTO limited_merge_table SELECT number FROM numbers(100); + +SYSTEM START MERGES limited_merge_table; + +OPTIMIZE TABLE limited_merge_table FINAL; + +SYSTEM FLUSH LOGS; + +SELECT COUNT() FROM limited_merge_table; + +-- final optimize FINAL will merge all parts, but all previous merges must merge <= 3 parts. +-- During concurrent run only one final merge can happen, thats why we have this `if`. +SELECT if(length(topK(2)(length(merged_from))) == 2, arrayMin(topK(2)(length(merged_from))) <= 3, 1) +FROM system.part_log WHERE table = 'limited_merge_table' and database = currentDatabase() and event_type = 'MergeParts'; + +DROP TABLE IF EXISTS limited_merge_table; diff --git a/tests/queries/0_stateless/arcadia_skip_list.txt b/tests/queries/0_stateless/arcadia_skip_list.txt index 98c3a04e2ce..c9024a6e0cc 100644 --- a/tests/queries/0_stateless/arcadia_skip_list.txt +++ b/tests/queries/0_stateless/arcadia_skip_list.txt @@ -236,4 +236,5 @@ 01801_s3_distributed 01833_test_collation_alvarotuso 01850_dist_INSERT_preserve_error +01870_modulo_partition_key 01880_remote_ipv6 diff --git a/tests/testflows/extended_precision_data_types/common.py b/tests/testflows/extended_precision_data_types/common.py new file mode 100644 index 00000000000..1c852bbf935 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/common.py @@ -0,0 +1,118 @@ +import uuid + +from contextlib import contextmanager + +from testflows.core.name import basename, parentname +from testflows._core.testtype import TestSubType +from testflows.asserts import values, error, snapshot + +from helpers.common import * + +@contextmanager +def allow_experimental_bigint(node): + """Enable experimental big int setting in Clickhouse. + """ + setting = ("allow_experimental_bigint_types", 1) + default_query_settings = None + + try: + with Given("I add allow_experimental_bigint to the default query settings"): + default_query_settings = getsattr(current().context, "default_query_settings", []) + default_query_settings.append(setting) + yield + finally: + with Finally("I remove allow_experimental_bigint from the default query settings"): + if default_query_settings: + try: + default_query_settings.pop(default_query_settings.index(setting)) + except ValueError: + pass + +@TestStep(Given) +def allow_experimental_map_type(self): + """Set allow_experimental_map_type = 1 + """ + setting = ("allow_experimental_map_type", 1) + default_query_settings = None + + try: + with By("adding allow_experimental_map_type to the default query settings"): + default_query_settings = getsattr(current().context, "default_query_settings", []) + default_query_settings.append(setting) + yield + finally: + with Finally("I remove allow_experimental_map_type from the default query settings"): + if default_query_settings: + try: + default_query_settings.pop(default_query_settings.index(setting)) + except ValueError: + pass + +def execute_query(sql, expected=None, format="TabSeparatedWithNames", compare_func=None): + """Execute SQL query and compare the output to the snapshot. + """ + name = basename(current().name) + + with When("I execute query", description=sql): + r = current().context.node.query(sql + " FORMAT " + format) + + if expected is not None: + with Then("I check output against expected"): + + if compare_func is None: + assert r.output.strip() == expected, error() + + else: + assert compare_func(r.output.strip(), expected), error() + + else: + with Then("I check output against snapshot"): + with values() as that: + assert that(snapshot("\n" + r.output.strip() + "\n", "tests", name=name, encoder=str)), error() + +@TestStep(Given) +def table(self, data_type, name="table0"): + """Create a table. + """ + node = current().context.node + + try: + with By("creating table"): + node.query(f"CREATE TABLE {name}(a {data_type}) ENGINE = Memory") + yield + + finally: + with Finally("drop the table"): + node.query(f"DROP TABLE IF EXISTS {name}") + +def getuid(): + """Create a unique variable name based on the test it is called from. + """ + if current().subtype == TestSubType.Example: + testname = f"{basename(parentname(current().name)).replace(' ', '_').replace(',','')}" + else: + testname = f"{basename(current().name).replace(' ', '_').replace(',','')}" + + for char in ['(', ')', '[', ']','\'']: + testname = testname.replace(f'{char}', '') + + return testname + "_" + str(uuid.uuid1()).replace('-', '_') + +def to_data_type(data_type, value): + """Return a conversion statement based on the data type provided + """ + if data_type in ['Decimal256(0)']: + return f'toDecimal256(\'{value}\',0)' + + else: + return f'to{data_type}(\'{value}\')' + + +data_types = [ + ('Int128', '-170141183460469231731687303715884105728', '170141183460469231731687303715884105727'), + ('Int256', '-57896044618658097711785492504343953926634992332820282019728792003956564819968', '57896044618658097711785492504343953926634992332820282019728792003956564819967'), + ('UInt128','0','340282366920938463463374607431768211455'), + ('UInt256', '0', '115792089237316195423570985008687907853269984665640564039457584007913129639935'), +] + +Decimal256_min_max = -1000000000000000000000000000000000000000000000000000000000000000000000000000,1000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml new file mode 100644 index 00000000000..567c8c05e32 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + :: + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/logs.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/logs.xml new file mode 100644 index 00000000000..bdf1bbc11c1 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/logs.xml @@ -0,0 +1,17 @@ + + 3 + + trace + /var/log/clickhouse-server/log.log + /var/log/clickhouse-server/log.err.log + 1000M + 10 + /var/log/clickhouse-server/stderr.log + /var/log/clickhouse-server/stdout.log + + + system + part_log
+ 500 +
+
diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/storage.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/storage.xml new file mode 100644 index 00000000000..618fd6b6d24 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.d/storage.xml @@ -0,0 +1,20 @@ + + + + + + 1024 + + + + + + + default + + + + + + + diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml new file mode 100644 index 00000000000..4ec12232539 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml @@ -0,0 +1,448 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml new file mode 100644 index 00000000000..86b2cd9e1e3 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/extended_precision_data_types/errors.py b/tests/testflows/extended_precision_data_types/errors.py new file mode 100644 index 00000000000..63b82f3368d --- /dev/null +++ b/tests/testflows/extended_precision_data_types/errors.py @@ -0,0 +1,11 @@ +def not_implemented_bigints(name): + return(48, f"Exception: {name} is not implemented for big integers") + +def bigints_not_implements(name): + return(48, f'Exception: {name} for big integers is not implemented') + +def illegal_type(): + return(43, 'Exception: Illegal type') + +def illegal_column(): + return(44, 'Exception: Illegal column') \ No newline at end of file diff --git a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml new file mode 100644 index 00000000000..fdd4a8057a9 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml @@ -0,0 +1,27 @@ +version: '2.3' + +services: + clickhouse: + image: yandex/clickhouse-integration-test + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/docker-compose.yml b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/docker-compose.yml new file mode 100644 index 00000000000..74d17d40e66 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/docker-compose.yml @@ -0,0 +1,30 @@ +version: '2.3' + +services: + + mysql1: + extends: + file: mysql-service.yml + service: mysql + hostname: mysql1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/mysql1/database:/var/lib/mysql" + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: hello-world + depends_on: + clickhouse1: + condition: service_healthy + mysql1: + condition: service_healthy diff --git a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/mysql-service.yml b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/mysql-service.yml new file mode 100644 index 00000000000..6924bccfad5 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/mysql-service.yml @@ -0,0 +1,19 @@ +version: '2.3' + +services: + mysql: + image: mysql:5.7.30 + restart: always + environment: + MYSQL_DATABASE: 'db' + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + expose: + - '3306' + healthcheck: + test: mysql -D db -u user --password=password -e "select 1;" + interval: 3s + timeout: 2s + retries: 40 + start_period: 2s diff --git a/tests/testflows/extended_precision_data_types/regression.py b/tests/testflows/extended_precision_data_types/regression.py new file mode 100755 index 00000000000..a0c3186c961 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/regression.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +import os +import sys + +from testflows.core import * + +append_path(sys.path, "..") + +from helpers.cluster import Cluster +from helpers.argparser import argparser +from extended_precision_data_types.requirements import * + +xfails = { +} + +xflags = { +} + +@TestModule +@ArgumentParser(argparser) +@XFails(xfails) +@XFlags(xflags) +@Name("extended precision data types") +@Specifications( + QA_SRS020_ClickHouse_Extended_Precision_Data_Types +) +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision("1.0"), +) +def regression(self, local, clickhouse_binary_path, stress=None, parallel=None): + """Extended precision data type regression. + """ + + top().terminating = False + + nodes = { + "clickhouse": + ("clickhouse1",) + } + with Cluster(local, clickhouse_binary_path, nodes=nodes, + docker_compose_project_dir=os.path.join(current_dir(), "extended-precision-data-type_env")) as cluster: + + self.context.cluster = cluster + self.context.stress = stress + + if parallel is not None: + self.context.parallel = parallel + + Feature(run=load("extended_precision_data_types.tests.feature", "feature")) + +if main(): + regression() diff --git a/tests/testflows/extended_precision_data_types/requirements/__init__.py b/tests/testflows/extended_precision_data_types/requirements/__init__.py new file mode 100644 index 00000000000..75e9d5b4bb8 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/requirements/__init__.py @@ -0,0 +1 @@ +from .requirements import * \ No newline at end of file diff --git a/tests/testflows/extended_precision_data_types/requirements/requirements.md b/tests/testflows/extended_precision_data_types/requirements/requirements.md new file mode 100644 index 00000000000..9bbd59a14d5 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/requirements/requirements.md @@ -0,0 +1,565 @@ +# QA-SRS020 ClickHouse Extended Precision Data Types +# Software Requirements Specification + +## Table of Contents + +* 1 [Revision History](#revision-history) +* 2 [Introduction](#introduction) +* 3 [Terminology](#terminology) + * 3.1 [Extended Precision Data Types](#extended-precision-data-types) +* 4 [Requirements](#requirements) + * 4.1 [RQ.SRS-020.ClickHouse.Extended.Precision](#rqsrs-020clickhouseextendedprecision) + * 4.2 [Conversion](#conversion) + * 4.2.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128](#rqsrs-020clickhouseextendedprecisionconversiontoint128) + * 4.2.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128](#rqsrs-020clickhouseextendedprecisionconversiontouint128) + * 4.2.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256](#rqsrs-020clickhouseextendedprecisionconversiontoint256) + * 4.2.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256](#rqsrs-020clickhouseextendedprecisionconversiontouint256) + * 4.2.5 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256](#rqsrs-020clickhouseextendedprecisionconversiontodecimal256) + * 4.2.6 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL](#rqsrs-020clickhouseextendedprecisionconversionfrommysql) + * 4.2.7 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL](#rqsrs-020clickhouseextendedprecisionconversiontomysql) + * 4.3 [Arithmetic](#arithmetic) + * 4.3.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported](#rqsrs-020clickhouseextendedprecisionarithmeticintsupported) + * 4.3.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported](#rqsrs-020clickhouseextendedprecisionarithmeticdecsupported) + * 4.3.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionarithmeticdecnotsupported) + * 4.4 [Arrays](#arrays) + * 4.4.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported](#rqsrs-020clickhouseextendedprecisionarraysintsupported) + * 4.4.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionarraysintnotsupported) + * 4.4.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported](#rqsrs-020clickhouseextendedprecisionarraysdecsupported) + * 4.4.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionarraysdecnotsupported) + * 4.5 [Comparison](#comparison) + * 4.5.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Comparison](#rqsrs-020clickhouseextendedprecisioncomparison) + * 4.6 [Logical Functions](#logical-functions) + * 4.6.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Logical](#rqsrs-020clickhouseextendedprecisionlogical) + * 4.7 [Mathematical Functions](#mathematical-functions) + * 4.7.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported](#rqsrs-020clickhouseextendedprecisionmathematicalsupported) + * 4.7.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported](#rqsrs-020clickhouseextendedprecisionmathematicalnotsupported) + * 4.8 [Rounding Functions](#rounding-functions) + * 4.8.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported](#rqsrs-020clickhouseextendedprecisionroundingintsupported) + * 4.8.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionroundingintnotsupported) + * 4.8.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported](#rqsrs-020clickhouseextendedprecisionroundingdecsupported) + * 4.8.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionroundingdecnotsupported) + * 4.9 [Bit Functions](#bit-functions) + * 4.9.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported](#rqsrs-020clickhouseextendedprecisionbitintsupported) + * 4.9.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionbitintnotsupported) + * 4.9.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionbitdecnotsupported) + * 4.10 [Null Functions](#null-functions) + * 4.10.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Null](#rqsrs-020clickhouseextendedprecisionnull) + * 4.11 [Tuple Functions](#tuple-functions) + * 4.11.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Tuple](#rqsrs-020clickhouseextendedprecisiontuple) + * 4.12 [Map Functions](#map-functions) + * 4.12.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported](#rqsrs-020clickhouseextendedprecisionmapsupported) + * 4.12.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported](#rqsrs-020clickhouseextendedprecisionmapnotsupported) + * 4.13 [Create](#create) + * 4.13.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table](#rqsrs-020clickhouseextendedprecisioncreatetable) +* 5 [References](#references) + +## Revision History + +This document is stored in an electronic form using [Git] source control management software +hosted in a [GitHub Repository]. +All the updates are tracked using the [Revision History]. + +## Introduction + +This software requirements specification covers requirements related to [ClickHouse] +using extended precision data types. + +## Terminology + +### Extended Precision Data Types + +Inclusive bounds: +* Int128 - [-170141183460469231731687303715884105728 : 170141183460469231731687303715884105727] +* UInt128 - [0 : 340282366920938463463374607431768211455] +* Int256 - [-57896044618658097711785492504343953926634992332820282019728792003956564819968 : 57896044618658097711785492504343953926634992332820282019728792003956564819967] +* UInt256 - [0 : 115792089237316195423570985008687907853269984665640564039457584007913129639935] + +Exclusive bounds: +* Decimal256 - (10^(76 - S): 10^(76 - S)), where S is the scale. + +## Requirements + +### RQ.SRS-020.ClickHouse.Extended.Precision +version: 1.0 + +[ClickHouse] SHALL support using [Extended Precision Data Types]. + +### Conversion + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Int128` using the `toInt128` function. + +For example, + +```sql +SELECT toInt128(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128 +version: 1.0 + +[ClickHouse] SHALL support converting values to `UInt128` format using `toUInt128` function. + +For example, + +```sql +SELECT toUInt128(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Int256` using `toInt256` function. + +For example, + +```sql +SELECT toInt256(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `UInt256` format using `toUInt256` function. + +For example, + +```sql +SELECT toUInt256(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Decimal256` format using `toDecimal256` function. + +For example, + +```sql +SELECT toDecimal256(1,2) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL +version: 1.0 + +[ClickHouse] SHALL support converting to [Extended Precision Data Types] from MySQL. + + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL +version: 1.0 + +[ClickHouse] MAY not support converting from [Extended Precision Data Types] to MySQL. + +### Arithmetic + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using [Arithmetic functions] with Int128, UInt128, Int256, and UInt256. + +Arithmetic functions: +* plus +* minus +* multiply +* divide +* intDiv +* intDivOrZero +* modulo +* moduloOrZero +* negate +* abs +* gcd +* lcm + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Arithmetic functions] with Decimal256: + +* plus +* minus +* multiply +* divide +* intDiv +* intDivOrZero +* negate +* abs + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Arithmetic functions] with Decimal256: + +* modulo +* moduloOrZero +* gcd +* lcm + +### Arrays + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Array functions] with Int128, UInt128, Int256, and UInt256. + +* empty +* notEmpty +* length +* arrayCount +* arrayPopBack +* arrayPopFront +* arraySort +* arrayReverseSort +* arrayUniq +* arrayJoin +* arrayDistinct +* arrayEnumerate +* arrayEnumerateDense +* arrayEnumerateUniq +* arrayReverse +* reverse +* arrayFlatten +* arrayCompact +* arrayExists +* arrayAll +* arrayMin +* arrayMax +* arraySum +* arrayAvg +* arrayReduce +* arrayReduceInRanges +* arrayZip +* arrayMap +* arrayFilter +* arrayFill +* arrayReverseFill +* arraySplit +* arrayFirst +* arrayFirstIndex +* arrayConcat +* hasAll +* hasAny +* hasSubstr +* arrayElement +* has +* indexOf +* countEqual +* arrayPushBack +* arrayPushFront +* arrayResize +* arraySlice + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Array functions] with Int128, UInt128, Int256, and UInt256: + +* arrayDifference +* arrayCumSum +* arrayCumSumNonNegative + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Array functions] with Decimal256: + +* empty +* notEmpty +* length +* arrayCount +* arrayPopBack +* arrayPopFront +* arraySort +* arrayReverseSort +* arrayUniq +* arrayJoin +* arrayDistinct +* arrayEnumerate +* arrayEnumerateDense +* arrayEnumerateUniq +* arrayReverse +* reverse +* arrayFlatten +* arrayCompact +* arrayExists +* arrayAll +* arrayReduce +* arrayReduceInRanges +* arrayZip +* arrayMap +* arrayFilter +* arrayFill +* arrayReverseFill +* arraySplit +* arrayFirst +* arrayFirstIndex +* arrayConcat +* hasAll +* hasAny +* hasSubstr +* arrayElement +* has +* indexOf +* countEqual +* arrayPushBack +* arrayPushFront +* arrayResize +* arraySlice + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Array functions] with Decimal256: + +* arrayMin +* arrayMax +* arraaySum +* arrayAvg +* arrayDifference +* arrayCumSum +* arrayCumSumNonNegative + +### Comparison + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Comparison +version: 1.0 + +[ClickHouse] SHALL support using [Comparison functions] with [Extended Precision Data Types]. + +Comparison functions: +* equals +* notEquals +* less +* greater +* lessOrEquals +* greaterOrEquals + +### Logical Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Logical +version: 1.0 + +[ClickHouse] MAY not support using [Logical functions] with [Extended Precision Data Types]. + +Logical functions: +* and +* or +* not +* xor + +### Mathematical Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Mathematical functions] with [Extended Precision Data Types]: + +* exp +* log, ln +* exp2 +* log2 +* exp10 +* log10 +* sqrt +* cbrt +* erf +* erfc +* lgamma +* tgamma +* sin +* cos +* tan +* asin +* acos +* atan +* cosh +* acosh +* sinh +* asinh +* tanh +* atanh +* log1p +* sign + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Mathematical functions] with [Extended Precision Data Types]: + +* pow, power +* intExp2 +* intExp10 +* atan2 +* hypot + +### Rounding Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256: + +* floor +* ceil +* trunc +* round +* roundBankers +* roundDuration +* roundAge + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256: + +* roundDown +* roundToExp2 + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Rounding functions] with Decimal256: + +* floor +* ceil +* trunc +* round +* roundBankers + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Rounding functions] with Decimal256: + +* roundDuration +* roundAge +* roundDown +* roundToExp2 + +### Bit Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256: + +* bitAnd +* bitOr +* bitXor +* bitNot +* bitShiftLeft +* bitShiftRight +* bitCount + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256: + +* bitRotateLeft +* bitRotateRight +* bitTest +* bitTestAll +* bitTestAny + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using [Bit functions] with Decimal256. + +Bit functions: +* bitAnd +* bitOr +* bitXor +* bitNot +* bitShiftLeft +* bitShiftRight +* bitCount +* bitRotateLeft +* bitRotateRight +* bitTest +* bitTestAll +* bitTestAny + +### Null Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Null +version: 1.0 + +[ClickHouse] SHALL support using [Null functions] with [Extended Precision Data Types]. + +Null functions: +* isNull +* isNotNull +* coalesce +* ifNull +* nullIf +* assumeNotNull +* toNullable + +### Tuple Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Tuple +version: 1.0 + +[ClickHouse] SHALL support using [Tuple functions] with [Extended Precision Data Types]. + +Tuple functions: +* tuple +* tupleElement +* untuple + +### Map Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Map functions] with [Extended Precision Data Types]: + +* map +* mapContains +* mapKeys +* mapValues + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Map functions] with [Extended Precision Data Types]: + +* mapAdd +* mapSubtract +* mapPopulateSeries + +### Create + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table +version: 1.0 + +[ClickHouse] SHALL support creating table with columns that use [Extended Precision Data Types]. + +## References + +* **ClickHouse:** https://clickhouse.tech +* **GitHub Repository**: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +* **Revision History**: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +* **Git:** https://git-scm.com/ + +[Extended Precision Data Types]: #extended-precision-data-types +[Arithmetic functions]: https://clickhouse.tech/docs/en/sql-reference/functions/arithmetic-functions/ +[Array functions]: https://clickhouse.tech/docs/en/sql-reference/functions/array-functions/ +[Comparison functions]: https://clickhouse.tech/docs/en/sql-reference/functions/comparison-functions/ +[Logical Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/logical-functions/ +[Mathematical Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/math-functions/ +[Rounding Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/rounding-functions/ +[Bit Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/bit-functions/ +[Null Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/functions-for-nulls/ +[Tuple Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/tuple-functions/ +[Map Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/tuple-map-functions/ +[SRS]: #srs +[ClickHouse]: https://clickhouse.tech +[GitHub Repository]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +[Revision History]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +[Git]: https://git-scm.com/ +[GitHub]: https://github.com diff --git a/tests/testflows/extended_precision_data_types/requirements/requirements.py b/tests/testflows/extended_precision_data_types/requirements/requirements.py new file mode 100644 index 00000000000..3fcf7798651 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/requirements/requirements.py @@ -0,0 +1,1423 @@ +# These requirements were auto generated +# from software requirements specification (SRS) +# document by TestFlows v1.6.210505.1133630. +# Do not edit by hand but re-generate instead +# using 'tfs requirements generate' command. +from testflows.core import Specification +from testflows.core import Requirement + +Heading = Specification.Heading + +RQ_SRS_020_ClickHouse_Extended_Precision = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using [Extended Precision Data Types].\n' + '\n' + ), + link=None, + level=2, + num='4.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt128 = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting values to `Int128` using the `toInt128` function.\n' + '\n' + 'For example,\n' + '\n' + '```sql\n' + 'SELECT toInt128(1)\n' + '```\n' + '\n' + ), + link=None, + level=3, + num='4.2.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt128 = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting values to `UInt128` format using `toUInt128` function.\n' + '\n' + 'For example,\n' + '\n' + '```sql\n' + 'SELECT toUInt128(1)\n' + '```\n' + '\n' + ), + link=None, + level=3, + num='4.2.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt256 = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting values to `Int256` using `toInt256` function.\n' + '\n' + 'For example,\n' + '\n' + '```sql\n' + 'SELECT toInt256(1)\n' + '```\n' + '\n' + ), + link=None, + level=3, + num='4.2.3') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt256 = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting values to `UInt256` format using `toUInt256` function.\n' + '\n' + 'For example,\n' + '\n' + '```sql\n' + 'SELECT toUInt256(1)\n' + '```\n' + '\n' + ), + link=None, + level=3, + num='4.2.4') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toDecimal256 = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting values to `Decimal256` format using `toDecimal256` function.\n' + '\n' + 'For example,\n' + '\n' + '```sql\n' + 'SELECT toDecimal256(1,2)\n' + '```\n' + '\n' + ), + link=None, + level=3, + num='4.2.5') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_FromMySQL = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support converting to [Extended Precision Data Types] from MySQL.\n' + '\n' + '\n' + ), + link=None, + level=3, + num='4.2.6') + +RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_ToMySQL = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support converting from [Extended Precision Data Types] to MySQL.\n' + '\n' + ), + link=None, + level=3, + num='4.2.7') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Int_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using [Arithmetic functions] with Int128, UInt128, Int256, and UInt256.\n' + '\n' + 'Arithmetic functions:\n' + '* plus\n' + '* minus\n' + '* multiply\n' + '* divide\n' + '* intDiv\n' + '* intDivOrZero\n' + '* modulo\n' + '* moduloOrZero\n' + '* negate\n' + '* abs\n' + '* gcd\n' + '* lcm\n' + '\n' + ), + link=None, + level=3, + num='4.3.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Arithmetic functions] with Decimal256:\n' + '\n' + '* plus\n' + '* minus\n' + '* multiply\n' + '* divide\n' + '* intDiv\n' + '* intDivOrZero\n' + '* negate\n' + '* abs\n' + '\n' + ), + link=None, + level=3, + num='4.3.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Arithmetic functions] with Decimal256:\n' + '\n' + '* modulo\n' + '* moduloOrZero\n' + '* gcd\n' + '* lcm\n' + '\n' + ), + link=None, + level=3, + num='4.3.3') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Array functions] with Int128, UInt128, Int256, and UInt256.\n' + '\n' + '* empty\n' + '* notEmpty\n' + '* length\n' + '* arrayCount\n' + '* arrayPopBack\n' + '* arrayPopFront\n' + '* arraySort\n' + '* arrayReverseSort\n' + '* arrayUniq\n' + '* arrayJoin\n' + '* arrayDistinct\n' + '* arrayEnumerate\n' + '* arrayEnumerateDense\n' + '* arrayEnumerateUniq\n' + '* arrayReverse\n' + '* reverse\n' + '* arrayFlatten\n' + '* arrayCompact\n' + '* arrayExists\n' + '* arrayAll\n' + '* arrayMin\n' + '* arrayMax\n' + '* arraySum\n' + '* arrayAvg\n' + '* arrayReduce\n' + '* arrayReduceInRanges\n' + '* arrayZip\n' + '* arrayMap\n' + '* arrayFilter\n' + '* arrayFill\n' + '* arrayReverseFill\n' + '* arraySplit\n' + '* arrayFirst\n' + '* arrayFirstIndex\n' + '* arrayConcat\n' + '* hasAll\n' + '* hasAny\n' + '* hasSubstr\n' + '* arrayElement\n' + '* has\n' + '* indexOf\n' + '* countEqual\n' + '* arrayPushBack\n' + '* arrayPushFront\n' + '* arrayResize\n' + '* arraySlice\n' + '\n' + ), + link=None, + level=3, + num='4.4.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Array functions] with Int128, UInt128, Int256, and UInt256:\n' + '\n' + '* arrayDifference\n' + '* arrayCumSum\n' + '* arrayCumSumNonNegative\n' + '\n' + ), + link=None, + level=3, + num='4.4.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Array functions] with Decimal256:\n' + '\n' + '* empty\n' + '* notEmpty\n' + '* length\n' + '* arrayCount\n' + '* arrayPopBack\n' + '* arrayPopFront\n' + '* arraySort\n' + '* arrayReverseSort\n' + '* arrayUniq\n' + '* arrayJoin\n' + '* arrayDistinct\n' + '* arrayEnumerate\n' + '* arrayEnumerateDense\n' + '* arrayEnumerateUniq\n' + '* arrayReverse\n' + '* reverse\n' + '* arrayFlatten\n' + '* arrayCompact\n' + '* arrayExists\n' + '* arrayAll\n' + '* arrayReduce\n' + '* arrayReduceInRanges\n' + '* arrayZip\n' + '* arrayMap\n' + '* arrayFilter\n' + '* arrayFill\n' + '* arrayReverseFill\n' + '* arraySplit\n' + '* arrayFirst\n' + '* arrayFirstIndex\n' + '* arrayConcat\n' + '* hasAll\n' + '* hasAny\n' + '* hasSubstr\n' + '* arrayElement\n' + '* has\n' + '* indexOf\n' + '* countEqual\n' + '* arrayPushBack\n' + '* arrayPushFront\n' + '* arrayResize\n' + '* arraySlice\n' + '\n' + ), + link=None, + level=3, + num='4.4.3') + +RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Array functions] with Decimal256:\n' + '\n' + '* arrayMin\n' + '* arrayMax\n' + '* arraaySum\n' + '* arrayAvg\n' + '* arrayDifference\n' + '* arrayCumSum\n' + '* arrayCumSumNonNegative\n' + '\n' + ), + link=None, + level=3, + num='4.4.4') + +RQ_SRS_020_ClickHouse_Extended_Precision_Comparison = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Comparison', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using [Comparison functions] with [Extended Precision Data Types].\n' + '\n' + 'Comparison functions:\n' + '* equals\n' + '* notEquals\n' + '* less\n' + '* greater\n' + '* lessOrEquals\n' + '* greaterOrEquals\n' + '\n' + ), + link=None, + level=3, + num='4.5.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Logical = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Logical', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using [Logical functions] with [Extended Precision Data Types].\n' + '\n' + 'Logical functions:\n' + '* and\n' + '* or\n' + '* not\n' + '* xor\n' + '\n' + ), + link=None, + level=3, + num='4.6.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Mathematical functions] with [Extended Precision Data Types]:\n' + '\n' + '* exp\n' + '* log, ln\n' + '* exp2\n' + '* log2\n' + '* exp10\n' + '* log10\n' + '* sqrt\n' + '* cbrt\n' + '* erf\n' + '* erfc\n' + '* lgamma\n' + '* tgamma\n' + '* sin\n' + '* cos\n' + '* tan\n' + '* asin\n' + '* acos\n' + '* atan\n' + '* cosh\n' + '* acosh\n' + '* sinh\n' + '* asinh\n' + '* tanh\n' + '* atanh\n' + '* log1p\n' + '* sign\n' + '\n' + ), + link=None, + level=3, + num='4.7.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Mathematical functions] with [Extended Precision Data Types]:\n' + '\n' + '* pow, power\n' + '* intExp2\n' + '* intExp10\n' + '* atan2\n' + '* hypot\n' + '\n' + ), + link=None, + level=3, + num='4.7.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256:\n' + '\n' + '* floor\n' + '* ceil\n' + '* trunc\n' + '* round\n' + '* roundBankers\n' + '* roundDuration\n' + '* roundAge\n' + '\n' + ), + link=None, + level=3, + num='4.8.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256:\n' + '\n' + '* roundDown\n' + '* roundToExp2\n' + '\n' + ), + link=None, + level=3, + num='4.8.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Rounding functions] with Decimal256:\n' + '\n' + '* floor\n' + '* ceil\n' + '* trunc\n' + '* round\n' + '* roundBankers\n' + '\n' + ), + link=None, + level=3, + num='4.8.3') + +RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Rounding functions] with Decimal256:\n' + '\n' + '* roundDuration\n' + '* roundAge\n' + '* roundDown\n' + '* roundToExp2\n' + '\n' + ), + link=None, + level=3, + num='4.8.4') + +RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256:\n' + '\n' + '* bitAnd\n' + '* bitOr\n' + '* bitXor\n' + '* bitNot\n' + '* bitShiftLeft\n' + '* bitShiftRight\n' + '* bitCount\n' + '\n' + ), + link=None, + level=3, + num='4.9.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256:\n' + '\n' + '* bitRotateLeft\n' + '* bitRotateRight\n' + '* bitTest\n' + '* bitTestAll\n' + '* bitTestAny\n' + '\n' + ), + link=None, + level=3, + num='4.9.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Dec_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using [Bit functions] with Decimal256.\n' + '\n' + 'Bit functions:\n' + '* bitAnd\n' + '* bitOr\n' + '* bitXor\n' + '* bitNot\n' + '* bitShiftLeft\n' + '* bitShiftRight\n' + '* bitCount\n' + '* bitRotateLeft\n' + '* bitRotateRight\n' + '* bitTest\n' + '* bitTestAll\n' + '* bitTestAny\n' + '\n' + ), + link=None, + level=3, + num='4.9.3') + +RQ_SRS_020_ClickHouse_Extended_Precision_Null = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Null', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using [Null functions] with [Extended Precision Data Types].\n' + '\n' + 'Null functions:\n' + '* isNull\n' + '* isNotNull\n' + '* coalesce\n' + '* ifNull\n' + '* nullIf\n' + '* assumeNotNull\n' + '* toNullable\n' + '\n' + ), + link=None, + level=3, + num='4.10.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Tuple = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Tuple', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using [Tuple functions] with [Extended Precision Data Types].\n' + '\n' + 'Tuple functions:\n' + '* tuple\n' + '* tupleElement\n' + '* untuple\n' + '\n' + ), + link=None, + level=3, + num='4.11.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Map_Supported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support using the following [Map functions] with [Extended Precision Data Types]:\n' + '\n' + '* map\n' + '* mapContains\n' + '* mapKeys\n' + '* mapValues\n' + '\n' + ), + link=None, + level=3, + num='4.12.1') + +RQ_SRS_020_ClickHouse_Extended_Precision_Map_NotSupported = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] MAY not support using the following [Map functions] with [Extended Precision Data Types]:\n' + '\n' + '* mapAdd\n' + '* mapSubtract\n' + '* mapPopulateSeries\n' + '\n' + ), + link=None, + level=3, + num='4.12.2') + +RQ_SRS_020_ClickHouse_Extended_Precision_Create_Table = Requirement( + name='RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table', + version='1.0', + priority=None, + group=None, + type=None, + uid=None, + description=( + '[ClickHouse] SHALL support creating table with columns that use [Extended Precision Data Types].\n' + '\n' + ), + link=None, + level=3, + num='4.13.1') + +QA_SRS020_ClickHouse_Extended_Precision_Data_Types = Specification( + name='QA-SRS020 ClickHouse Extended Precision Data Types', + description=None, + author=None, + date=None, + status=None, + approved_by=None, + approved_date=None, + approved_version=None, + version=None, + group=None, + type=None, + link=None, + uid=None, + parent=None, + children=None, + headings=( + Heading(name='Revision History', level=1, num='1'), + Heading(name='Introduction', level=1, num='2'), + Heading(name='Terminology', level=1, num='3'), + Heading(name='Extended Precision Data Types', level=2, num='3.1'), + Heading(name='Requirements', level=1, num='4'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision', level=2, num='4.1'), + Heading(name='Conversion', level=2, num='4.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128', level=3, num='4.2.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128', level=3, num='4.2.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256', level=3, num='4.2.3'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256', level=3, num='4.2.4'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256', level=3, num='4.2.5'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL', level=3, num='4.2.6'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL', level=3, num='4.2.7'), + Heading(name='Arithmetic', level=2, num='4.3'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported', level=3, num='4.3.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported', level=3, num='4.3.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported', level=3, num='4.3.3'), + Heading(name='Arrays', level=2, num='4.4'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported', level=3, num='4.4.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported', level=3, num='4.4.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported', level=3, num='4.4.3'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported', level=3, num='4.4.4'), + Heading(name='Comparison', level=2, num='4.5'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Comparison', level=3, num='4.5.1'), + Heading(name='Logical Functions', level=2, num='4.6'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Logical', level=3, num='4.6.1'), + Heading(name='Mathematical Functions', level=2, num='4.7'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported', level=3, num='4.7.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported', level=3, num='4.7.2'), + Heading(name='Rounding Functions', level=2, num='4.8'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported', level=3, num='4.8.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported', level=3, num='4.8.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported', level=3, num='4.8.3'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported', level=3, num='4.8.4'), + Heading(name='Bit Functions', level=2, num='4.9'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported', level=3, num='4.9.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported', level=3, num='4.9.2'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported', level=3, num='4.9.3'), + Heading(name='Null Functions', level=2, num='4.10'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Null', level=3, num='4.10.1'), + Heading(name='Tuple Functions', level=2, num='4.11'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Tuple', level=3, num='4.11.1'), + Heading(name='Map Functions', level=2, num='4.12'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported', level=3, num='4.12.1'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported', level=3, num='4.12.2'), + Heading(name='Create', level=2, num='4.13'), + Heading(name='RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table', level=3, num='4.13.1'), + Heading(name='References', level=1, num='5'), + ), + requirements=( + RQ_SRS_020_ClickHouse_Extended_Precision, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt128, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt128, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt256, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt256, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toDecimal256, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_FromMySQL, + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_ToMySQL, + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Int_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Comparison, + RQ_SRS_020_ClickHouse_Extended_Precision_Logical, + RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Dec_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Null, + RQ_SRS_020_ClickHouse_Extended_Precision_Tuple, + RQ_SRS_020_ClickHouse_Extended_Precision_Map_Supported, + RQ_SRS_020_ClickHouse_Extended_Precision_Map_NotSupported, + RQ_SRS_020_ClickHouse_Extended_Precision_Create_Table, + ), + content=''' +# QA-SRS020 ClickHouse Extended Precision Data Types +# Software Requirements Specification + +## Table of Contents + +* 1 [Revision History](#revision-history) +* 2 [Introduction](#introduction) +* 3 [Terminology](#terminology) + * 3.1 [Extended Precision Data Types](#extended-precision-data-types) +* 4 [Requirements](#requirements) + * 4.1 [RQ.SRS-020.ClickHouse.Extended.Precision](#rqsrs-020clickhouseextendedprecision) + * 4.2 [Conversion](#conversion) + * 4.2.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128](#rqsrs-020clickhouseextendedprecisionconversiontoint128) + * 4.2.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128](#rqsrs-020clickhouseextendedprecisionconversiontouint128) + * 4.2.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256](#rqsrs-020clickhouseextendedprecisionconversiontoint256) + * 4.2.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256](#rqsrs-020clickhouseextendedprecisionconversiontouint256) + * 4.2.5 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256](#rqsrs-020clickhouseextendedprecisionconversiontodecimal256) + * 4.2.6 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL](#rqsrs-020clickhouseextendedprecisionconversionfrommysql) + * 4.2.7 [RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL](#rqsrs-020clickhouseextendedprecisionconversiontomysql) + * 4.3 [Arithmetic](#arithmetic) + * 4.3.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported](#rqsrs-020clickhouseextendedprecisionarithmeticintsupported) + * 4.3.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported](#rqsrs-020clickhouseextendedprecisionarithmeticdecsupported) + * 4.3.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionarithmeticdecnotsupported) + * 4.4 [Arrays](#arrays) + * 4.4.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported](#rqsrs-020clickhouseextendedprecisionarraysintsupported) + * 4.4.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionarraysintnotsupported) + * 4.4.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported](#rqsrs-020clickhouseextendedprecisionarraysdecsupported) + * 4.4.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionarraysdecnotsupported) + * 4.5 [Comparison](#comparison) + * 4.5.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Comparison](#rqsrs-020clickhouseextendedprecisioncomparison) + * 4.6 [Logical Functions](#logical-functions) + * 4.6.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Logical](#rqsrs-020clickhouseextendedprecisionlogical) + * 4.7 [Mathematical Functions](#mathematical-functions) + * 4.7.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported](#rqsrs-020clickhouseextendedprecisionmathematicalsupported) + * 4.7.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported](#rqsrs-020clickhouseextendedprecisionmathematicalnotsupported) + * 4.8 [Rounding Functions](#rounding-functions) + * 4.8.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported](#rqsrs-020clickhouseextendedprecisionroundingintsupported) + * 4.8.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionroundingintnotsupported) + * 4.8.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported](#rqsrs-020clickhouseextendedprecisionroundingdecsupported) + * 4.8.4 [RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionroundingdecnotsupported) + * 4.9 [Bit Functions](#bit-functions) + * 4.9.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported](#rqsrs-020clickhouseextendedprecisionbitintsupported) + * 4.9.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported](#rqsrs-020clickhouseextendedprecisionbitintnotsupported) + * 4.9.3 [RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported](#rqsrs-020clickhouseextendedprecisionbitdecnotsupported) + * 4.10 [Null Functions](#null-functions) + * 4.10.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Null](#rqsrs-020clickhouseextendedprecisionnull) + * 4.11 [Tuple Functions](#tuple-functions) + * 4.11.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Tuple](#rqsrs-020clickhouseextendedprecisiontuple) + * 4.12 [Map Functions](#map-functions) + * 4.12.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported](#rqsrs-020clickhouseextendedprecisionmapsupported) + * 4.12.2 [RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported](#rqsrs-020clickhouseextendedprecisionmapnotsupported) + * 4.13 [Create](#create) + * 4.13.1 [RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table](#rqsrs-020clickhouseextendedprecisioncreatetable) +* 5 [References](#references) + +## Revision History + +This document is stored in an electronic form using [Git] source control management software +hosted in a [GitHub Repository]. +All the updates are tracked using the [Revision History]. + +## Introduction + +This software requirements specification covers requirements related to [ClickHouse] +using extended precision data types. + +## Terminology + +### Extended Precision Data Types + +Inclusive bounds: +* Int128 - [-170141183460469231731687303715884105728 : 170141183460469231731687303715884105727] +* UInt128 - [0 : 340282366920938463463374607431768211455] +* Int256 - [-57896044618658097711785492504343953926634992332820282019728792003956564819968 : 57896044618658097711785492504343953926634992332820282019728792003956564819967] +* UInt256 - [0 : 115792089237316195423570985008687907853269984665640564039457584007913129639935] + +Exclusive bounds: +* Decimal256 - (10^(76 - S): 10^(76 - S)), where S is the scale. + +## Requirements + +### RQ.SRS-020.ClickHouse.Extended.Precision +version: 1.0 + +[ClickHouse] SHALL support using [Extended Precision Data Types]. + +### Conversion + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt128 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Int128` using the `toInt128` function. + +For example, + +```sql +SELECT toInt128(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt128 +version: 1.0 + +[ClickHouse] SHALL support converting values to `UInt128` format using `toUInt128` function. + +For example, + +```sql +SELECT toUInt128(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toInt256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Int256` using `toInt256` function. + +For example, + +```sql +SELECT toInt256(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toUInt256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `UInt256` format using `toUInt256` function. + +For example, + +```sql +SELECT toUInt256(1) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.toDecimal256 +version: 1.0 + +[ClickHouse] SHALL support converting values to `Decimal256` format using `toDecimal256` function. + +For example, + +```sql +SELECT toDecimal256(1,2) +``` + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.FromMySQL +version: 1.0 + +[ClickHouse] SHALL support converting to [Extended Precision Data Types] from MySQL. + + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Conversion.ToMySQL +version: 1.0 + +[ClickHouse] MAY not support converting from [Extended Precision Data Types] to MySQL. + +### Arithmetic + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using [Arithmetic functions] with Int128, UInt128, Int256, and UInt256. + +Arithmetic functions: +* plus +* minus +* multiply +* divide +* intDiv +* intDivOrZero +* modulo +* moduloOrZero +* negate +* abs +* gcd +* lcm + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Arithmetic functions] with Decimal256: + +* plus +* minus +* multiply +* divide +* intDiv +* intDivOrZero +* negate +* abs + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arithmetic.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Arithmetic functions] with Decimal256: + +* modulo +* moduloOrZero +* gcd +* lcm + +### Arrays + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Array functions] with Int128, UInt128, Int256, and UInt256. + +* empty +* notEmpty +* length +* arrayCount +* arrayPopBack +* arrayPopFront +* arraySort +* arrayReverseSort +* arrayUniq +* arrayJoin +* arrayDistinct +* arrayEnumerate +* arrayEnumerateDense +* arrayEnumerateUniq +* arrayReverse +* reverse +* arrayFlatten +* arrayCompact +* arrayExists +* arrayAll +* arrayMin +* arrayMax +* arraySum +* arrayAvg +* arrayReduce +* arrayReduceInRanges +* arrayZip +* arrayMap +* arrayFilter +* arrayFill +* arrayReverseFill +* arraySplit +* arrayFirst +* arrayFirstIndex +* arrayConcat +* hasAll +* hasAny +* hasSubstr +* arrayElement +* has +* indexOf +* countEqual +* arrayPushBack +* arrayPushFront +* arrayResize +* arraySlice + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Array functions] with Int128, UInt128, Int256, and UInt256: + +* arrayDifference +* arrayCumSum +* arrayCumSumNonNegative + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Array functions] with Decimal256: + +* empty +* notEmpty +* length +* arrayCount +* arrayPopBack +* arrayPopFront +* arraySort +* arrayReverseSort +* arrayUniq +* arrayJoin +* arrayDistinct +* arrayEnumerate +* arrayEnumerateDense +* arrayEnumerateUniq +* arrayReverse +* reverse +* arrayFlatten +* arrayCompact +* arrayExists +* arrayAll +* arrayReduce +* arrayReduceInRanges +* arrayZip +* arrayMap +* arrayFilter +* arrayFill +* arrayReverseFill +* arraySplit +* arrayFirst +* arrayFirstIndex +* arrayConcat +* hasAll +* hasAny +* hasSubstr +* arrayElement +* has +* indexOf +* countEqual +* arrayPushBack +* arrayPushFront +* arrayResize +* arraySlice + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Arrays.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Array functions] with Decimal256: + +* arrayMin +* arrayMax +* arraaySum +* arrayAvg +* arrayDifference +* arrayCumSum +* arrayCumSumNonNegative + +### Comparison + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Comparison +version: 1.0 + +[ClickHouse] SHALL support using [Comparison functions] with [Extended Precision Data Types]. + +Comparison functions: +* equals +* notEquals +* less +* greater +* lessOrEquals +* greaterOrEquals + +### Logical Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Logical +version: 1.0 + +[ClickHouse] MAY not support using [Logical functions] with [Extended Precision Data Types]. + +Logical functions: +* and +* or +* not +* xor + +### Mathematical Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Mathematical functions] with [Extended Precision Data Types]: + +* exp +* log, ln +* exp2 +* log2 +* exp10 +* log10 +* sqrt +* cbrt +* erf +* erfc +* lgamma +* tgamma +* sin +* cos +* tan +* asin +* acos +* atan +* cosh +* acosh +* sinh +* asinh +* tanh +* atanh +* log1p +* sign + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Mathematical.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Mathematical functions] with [Extended Precision Data Types]: + +* pow, power +* intExp2 +* intExp10 +* atan2 +* hypot + +### Rounding Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256: + +* floor +* ceil +* trunc +* round +* roundBankers +* roundDuration +* roundAge + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Rounding functions] with Int128, UInt128, Int256, and UInt256: + +* roundDown +* roundToExp2 + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Rounding functions] with Decimal256: + +* floor +* ceil +* trunc +* round +* roundBankers + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Rounding.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Rounding functions] with Decimal256: + +* roundDuration +* roundAge +* roundDown +* roundToExp2 + +### Bit Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256: + +* bitAnd +* bitOr +* bitXor +* bitNot +* bitShiftLeft +* bitShiftRight +* bitCount + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Int.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Bit functions] with Int128, UInt128, Int256, and UInt256: + +* bitRotateLeft +* bitRotateRight +* bitTest +* bitTestAll +* bitTestAny + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Bit.Dec.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using [Bit functions] with Decimal256. + +Bit functions: +* bitAnd +* bitOr +* bitXor +* bitNot +* bitShiftLeft +* bitShiftRight +* bitCount +* bitRotateLeft +* bitRotateRight +* bitTest +* bitTestAll +* bitTestAny + +### Null Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Null +version: 1.0 + +[ClickHouse] SHALL support using [Null functions] with [Extended Precision Data Types]. + +Null functions: +* isNull +* isNotNull +* coalesce +* ifNull +* nullIf +* assumeNotNull +* toNullable + +### Tuple Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Tuple +version: 1.0 + +[ClickHouse] SHALL support using [Tuple functions] with [Extended Precision Data Types]. + +Tuple functions: +* tuple +* tupleElement +* untuple + +### Map Functions + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Map.Supported +version: 1.0 + +[ClickHouse] SHALL support using the following [Map functions] with [Extended Precision Data Types]: + +* map +* mapContains +* mapKeys +* mapValues + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Map.NotSupported +version: 1.0 + +[ClickHouse] MAY not support using the following [Map functions] with [Extended Precision Data Types]: + +* mapAdd +* mapSubtract +* mapPopulateSeries + +### Create + +#### RQ.SRS-020.ClickHouse.Extended.Precision.Create.Table +version: 1.0 + +[ClickHouse] SHALL support creating table with columns that use [Extended Precision Data Types]. + +## References + +* **ClickHouse:** https://clickhouse.tech +* **GitHub Repository**: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +* **Revision History**: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +* **Git:** https://git-scm.com/ + +[Extended Precision Data Types]: #extended-precision-data-types +[Arithmetic functions]: https://clickhouse.tech/docs/en/sql-reference/functions/arithmetic-functions/ +[Array functions]: https://clickhouse.tech/docs/en/sql-reference/functions/array-functions/ +[Comparison functions]: https://clickhouse.tech/docs/en/sql-reference/functions/comparison-functions/ +[Logical Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/logical-functions/ +[Mathematical Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/math-functions/ +[Rounding Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/rounding-functions/ +[Bit Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/bit-functions/ +[Null Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/functions-for-nulls/ +[Tuple Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/tuple-functions/ +[Map Functions]: https://clickhouse.tech/docs/en/sql-reference/functions/tuple-map-functions/ +[SRS]: #srs +[ClickHouse]: https://clickhouse.tech +[GitHub Repository]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +[Revision History]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/extended_precision_data_types/requirements/requirements.md +[Git]: https://git-scm.com/ +[GitHub]: https://github.com +''') diff --git a/tests/testflows/extended_precision_data_types/tests/__init__.py b/tests/testflows/extended_precision_data_types/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testflows/extended_precision_data_types/tests/arithmetic.py b/tests/testflows/extended_precision_data_types/tests/arithmetic.py new file mode 100644 index 00000000000..d939569d5a7 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/arithmetic.py @@ -0,0 +1,217 @@ +import os +import textwrap + +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('plus', '2'), + ('minus', '0'), + ('multiply', '1'), + ('divide', '1'), + ('intDiv', '1'), + ('intDivOrZero', '1'), + ('modulo', '0'), + ('moduloOrZero', '0'), + ('negate', '-1'), + ('abs', '1'), + ('gcd', '1'), + ('lcm', '1'), +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] +Examples_dec_list = [tuple(list(func)+[Name(f'{func[0]} - Decimal256')]) for func in funcs] + +@TestOutline +@Examples('arithmetic_func expected_result int_type min max', Examples_list) +def inline_check(self, arithmetic_func, expected_result, int_type, min, max, node=None): + """Check that arithmetic functions work using inline tests with Int128, UInt128, Int256, and UInt256. + """ + + if node is None: + node = self.context.node + + if arithmetic_func in ['negate','abs']: + + with When(f"I check {arithmetic_func} with {int_type}"): + output = node.query(f"SELECT {arithmetic_func}(to{int_type}(1))").output + assert output == expected_result, error() + + with When(f"I check {arithmetic_func} with {int_type} max and min value"): + execute_query(f""" + SELECT {arithmetic_func}(to{int_type}(\'{max}\')), {arithmetic_func}(to{int_type}(\'{min}\')) + """) + + else: + + with When(f"I check {arithmetic_func} with {int_type}"): + output = node.query(f"SELECT {arithmetic_func}(to{int_type}(1), to{int_type}(1))").output + assert output == expected_result, error() + + if arithmetic_func in ['gcd','lcm']: + + if int_type in ['UInt128','UInt256']: + exitcode=153 + else: + exitcode=151 + + with When(f"I check {arithmetic_func} with {int_type} max and min value"): + node.query(f"SELECT {arithmetic_func}(to{int_type}(\'{max}\'), to{int_type}(1)), {arithmetic_func}(to{int_type}(\'{min}\'), to{int_type}(1))", + exitcode = exitcode, message = 'Exception:') + + else: + + with When(f"I check {arithmetic_func} with {int_type} max and min value"): + execute_query(f""" + SELECT {arithmetic_func}(to{int_type}(\'{max}\'), to{int_type}(1)), {arithmetic_func}(to{int_type}(\'{min}\'), to{int_type}(1)) + """) + +@TestOutline +@Examples('arithmetic_func expected_result int_type min max', Examples_list) +def table_check(self, arithmetic_func, expected_result, int_type, min, max, node=None): + """Check that arithmetic functions work using tables with Int128, UInt128, Int256, and UInt256. + """ + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = int_type) + + if arithmetic_func in ['negate','abs']: + + for value in [1, min, max]: + + with When(f"I insert {arithmetic_func} with {int_type} {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(\'{value}\'))") + + with Then(f"I check the table output of {arithmetic_func} with {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + else: + + with When(f"I insert {arithmetic_func} with {int_type} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(1), to{int_type}(1))") + + with Then("I check that the output matches the expected value"): + output = node.query(f"SELECT * FROM {table_name}").output + assert output == expected_result, error() + + if arithmetic_func in ['gcd', 'lcm']: + + if int_type in ['UInt128', 'UInt256']: + + with When(f"I insert {arithmetic_func} with {int_type} {min} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(\'{min}\'), to{int_type}(1))", + exitcode = 153, message = 'Exception:') + + with And(f"I insert {arithmetic_func} with {int_type} {max} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(\'{max}\'), to{int_type}(1))") + + else: + + for value in [min, max]: + + with When(f"I insert {arithmetic_func} with {int_type} {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(\'{value}\'), to{int_type}(1))", + exitcode = 151, message = 'Exception:') + + else: + + for value in [min, max]: + + with When(f"I insert {arithmetic_func} with {int_type} {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(to{int_type}(\'{value}\'), to{int_type}(1))") + + with Then(f"I check the table output of {arithmetic_func} with {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline +@Examples('arithmetic_func expected_result', Examples_dec_list) +def inline_check_dec(self, arithmetic_func, expected_result, node=None): + """Check that arithmetic functions work using inline with Decimal256. + """ + + if node is None: + node = self.context.node + + if arithmetic_func is 'negate' or arithmetic_func is 'abs': + + with When(f"I check {arithmetic_func} with toDecimal256"): + output = node.query(f"SELECT {arithmetic_func}(toDecimal256(1,0))").output + assert output == expected_result, error() + + elif arithmetic_func in ['modulo', 'moduloOrZero', 'gcd', 'lcm']: + + with When(f"I check {arithmetic_func} with toDecimal256"): + node.query(f"SELECT {arithmetic_func}(toDecimal256(1,0), toDecimal256(1,0))", + exitcode=43, message = 'Exception:') + + else: + + with When(f"I check {arithmetic_func} with toDecimal256"): + output = node.query(f"SELECT {arithmetic_func}(toDecimal256(1,0), toDecimal256(1,0))").output + assert output == expected_result, error() + +@TestOutline +@Examples('arithmetic_func expected_result', Examples_dec_list) +def table_check_dec(self, arithmetic_func, expected_result, node=None): + """Check that arithmetic functions work using tables with Decimal256. + """ + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + if arithmetic_func in ['negate','abs']: + + with When(f"I insert {arithmetic_func} with toDecimal256 into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(toDecimal256(1,0))") + + with Then(f"I check the table for output of {arithmetic_func} with Decimal256"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + elif arithmetic_func in ['modulo', 'moduloOrZero', 'gcd', 'lcm']: + + with When(f"I check {arithmetic_func} with toDecimal256"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(toDecimal256(1,0), toDecimal256(1,0))", + exitcode=43, message = 'Exception:') + + else: + with When(f"I insert {arithmetic_func} with toDecimal256 into the table"): + node.query(f"INSERT INTO {table_name} SELECT {arithmetic_func}(toDecimal256(1,0), toDecimal256(1,0))") + + with Then("I check that the output matches the expected value"): + output = node.query(f"SELECT * FROM {table_name}").output + assert output == expected_result, error() + +@TestFeature +@Name("arithmetic") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Int_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Arithmetic_Dec_NotSupported("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that arithmetic functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run = inline_check) + Scenario(run = table_check) + Scenario(run = inline_check_dec) + Scenario(run = table_check_dec) diff --git a/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py b/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py new file mode 100644 index 00000000000..6efa122ffdf --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py @@ -0,0 +1,484 @@ +import uuid + +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +def get_table_name(): + return "table" + "_" + str(uuid.uuid1()).replace('-', '_') + +@TestOutline(Suite) +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Int_NotSupported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Arrays_Dec_NotSupported("1.0"), +) +def array_func(self, data_type, node=None): + """Check array functions with extended precision data types. + """ + + if node is None: + node = self.context.node + + for func in ['arrayPopBack(', + 'arrayPopFront(', + 'arraySort(', + 'arrayReverseSort(', + 'arrayDistinct(', + 'arrayEnumerate(', + 'arrayEnumerateDense(', + 'arrayEnumerateUniq(', + 'arrayReverse(', + 'reverse(', + 'arrayFlatten(', + 'arrayCompact(', + 'arrayReduceInRanges(\'sum\', [(1, 5)],', + 'arrayMap(x -> (x + 2),', + 'arrayFill(x -> x=3,', + 'arrayReverseFill(x -> x=3,', + f'arrayConcat([{to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}],', + 'arrayFilter(x -> x == 1, ']: + + with Scenario(f"Inline - {data_type} - {func}"): + execute_query(f""" + SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)})) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array({data_type})') + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['arraySplit((x, y) -> x=y, [0, 0, 0],']: + + with Scenario(f"Inline - {data_type} - {func}"): + execute_query(f""" + SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)})) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array(Array({data_type}))') + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in [f'arrayZip([{to_data_type(data_type,1)}],']: + + with Scenario(f"Inline - {data_type} - {func}"): + execute_query(f""" + SELECT {func}array({to_data_type(data_type,3)})) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array(Tuple({data_type}, {data_type}))') + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,1)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['empty(', + 'notEmpty(', + 'length(', + 'arrayCount(x -> x == 1, ', + 'arrayUniq(', + 'arrayJoin(', + 'arrayExists(x -> x==1,', + 'arrayAll(x -> x==1,', + 'arrayMin(', + 'arrayMax(', + 'arraySum(', + 'arrayAvg(', + 'arrayReduce(\'max\', ', + 'arrayFirst(x -> x==3,', + 'arrayFirstIndex(x -> x==3,', + f'hasAll([{to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}], ', + f'hasAny([{to_data_type(data_type,2)}, {to_data_type(data_type,1)}], ', + f'hasSubstr([{to_data_type(data_type,2)}, {to_data_type(data_type,1)}], ']: + + if func in ['arrayMin(','arrayMax(','arraySum(', 'arrayAvg('] and data_type in ['Decimal256(0)']: + + with Scenario(f"Inline - {data_type} - {func}"): + node.query(f"SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))", + exitcode = 44, message = 'Exception:') + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))", + exitcode = 44, message = 'Exception:') + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + else: + + with Scenario(f"Inline - {data_type} - {func}"): + + execute_query(f""" + SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)})) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['arrayDifference(', + 'arrayCumSum(', + 'arrayCumSumNonNegative(']: + + if data_type in ['Decimal256(0)']: + exitcode = 44 + else: + exitcode = 43 + + with Scenario(f"Inline - {data_type} - {func}"): + node.query(f"SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))", + exitcode = exitcode, message = 'Exception:') + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}))", + exitcode = exitcode, message = 'Exception:') + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['arrayElement']: + + with Scenario(f"Inline - {data_type} - {func}"): + + execute_query(f""" + SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), 1) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), 1)") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['arrayPushBack', + 'arrayPushFront']: + + with Scenario(f"Inline - {data_type} - {func}"): + + execute_query(f""" + SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), {to_data_type(data_type,1)}) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array({data_type})') + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), {to_data_type(data_type,1)})") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['arrayResize', + 'arraySlice']: + + with Scenario(f"Inline - {data_type} - {func}"): + + execute_query(f""" + SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), 1) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array({data_type})') + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), 1)") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + for func in ['has', + 'indexOf', + 'countEqual']: + + with Scenario(f"Inline - {data_type} - {func}"): + execute_query(f""" + SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), NULL) + """) + + with Scenario(f"Table - {data_type} - {func}"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(array({to_data_type(data_type,3)}, {to_data_type(data_type,2)}, {to_data_type(data_type,1)}), NULL)") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline(Suite) +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Tuple("1.0"), +) +def tuple_func(self, data_type, node=None): + """Check tuple functions with extended precision data types. + """ + + if node is None: + node = self.context.node + + with Scenario(f"Creating a tuple with {data_type}"): + node.query(f"SELECT tuple({to_data_type(data_type,1)}, {to_data_type(data_type,1)}, {to_data_type(data_type,1)})") + + with Scenario(f"Creating a tuple with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Tuple({data_type}, {data_type}, {data_type})') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT tuple({to_data_type(data_type,1)}, {to_data_type(data_type,1)}, {to_data_type(data_type,1)})") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"tupleElement with {data_type}"): + node.query(f"SELECT tupleElement(({to_data_type(data_type,1)}, {to_data_type(data_type,1)}), 1)") + + with Scenario(f"tupleElement with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT tupleElement(({to_data_type(data_type,1)}, {to_data_type(data_type,1)}), 1)") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"untuple with {data_type}"): + node.query(f"SELECT untuple(({to_data_type(data_type,1)},))") + + with Scenario(f"untuple with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT untuple(({to_data_type(data_type,1)},))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"tupleHammingDistance with {data_type}"): + node.query(f"SELECT tupleHammingDistance(({to_data_type(data_type,1)}, {to_data_type(data_type,1)}), ({to_data_type(data_type,2)}, {to_data_type(data_type,2)}))") + + with Scenario(f"tupleHammingDistance with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT tupleHammingDistance(({to_data_type(data_type,1)}, {to_data_type(data_type,1)}), ({to_data_type(data_type,2)}, {to_data_type(data_type,2)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline(Suite) +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Map_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Map_NotSupported("1.0"), +) +def map_func(self, data_type, node=None): + """Check Map functions with extended precision data types. + """ + + if node is None: + node = self.context.node + + with Scenario(f"Creating a map with {data_type}"): + node.query(f"SELECT map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)})") + + with Scenario(f"Creating a map with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Map(String, {data_type})') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)})") + + execute_query(f""" + SELECT * FROM {table_name} + """) + + with Scenario(f"mapAdd with {data_type}"): + node.query(f"SELECT mapAdd(([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]), ([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]))", + exitcode = 44, message='Exception:') + + with Scenario(f"mapAdd with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Tuple(Array({data_type}), Array({data_type}))') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapAdd(([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]), ([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]))", + exitcode = 44, message='Exception:') + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"mapSubtract with {data_type}"): + node.query(f"SELECT mapSubtract(([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]), ([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]))", + exitcode = 44, message='Exception:') + + with Scenario(f"mapSubtract with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Tuple(Array({data_type}), Array({data_type}))') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapSubtract(([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]), ([{to_data_type(data_type,1)}, {to_data_type(data_type,2)}], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}]))", + exitcode = 44, message='Exception:') + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"mapPopulateSeries with {data_type}"): + node.query(f"SELECT mapPopulateSeries([1,2,3], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}, {to_data_type(data_type,3)}], 5)", + exitcode = 44, message='Exception:') + + with Scenario(f"mapPopulateSeries with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Tuple(Array({data_type}), Array({data_type}))') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapPopulateSeries([1,2,3], [{to_data_type(data_type,1)}, {to_data_type(data_type,2)}, {to_data_type(data_type,3)}], 5)", + exitcode = 44, message='Exception:') + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"mapContains with {data_type}"): + node.query(f"SELECT mapContains( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}), 'key1')") + + with Scenario(f"mapContains with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = data_type) + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapContains( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}), 'key1')") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"mapKeys with {data_type}"): + node.query(f"SELECT mapKeys( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}))") + + with Scenario(f"mapKeys with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = 'Array(String)') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapKeys( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + with Scenario(f"mapValues with {data_type}"): + node.query(f"SELECT mapValues( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}))") + + with Scenario(f"mapValues with {data_type} on a table"): + table_name = get_table_name() + + table(name = table_name, data_type = f'Array({data_type})') + + with When("I insert the output into a table"): + node.query(f"INSERT INTO {table_name} SELECT mapValues( map('key1', {to_data_type(data_type,1)}, 'key2', {to_data_type(data_type,2)}))") + + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestFeature +@Name("array, tuple, map") +@Examples("data_type",[ + ('Int128',), + ('Int256',), + ('UInt128',), + ('UInt256',), + ('Decimal256(0)',), +]) +def feature(self, node="clickhouse1", stress=None, parallel=None): + """Check that array, tuple, and map functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + + with allow_experimental_bigint(self.context.node): + for example in self.examples: + data_type, = example + + with Feature(data_type): + + Suite(test=array_func)(data_type=data_type) + Suite(test=tuple_func)(data_type=data_type) + + with Given("I allow experimental map type"): + allow_experimental_map_type() + + Suite(test=map_func)(data_type=data_type) diff --git a/tests/testflows/extended_precision_data_types/tests/bit.py b/tests/testflows/extended_precision_data_types/tests/bit.py new file mode 100644 index 00000000000..24f63532c74 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/bit.py @@ -0,0 +1,179 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * +from extended_precision_data_types.errors import * + +funcs = [ + ('bitAnd', True, None), + ('bitOr', True, None), + ('bitXor', True, None), + ('bitShiftLeft', True, None), + ('bitShiftRight', True, None), + ('bitRotateLeft', False, not_implemented_bigints('Bit rotate')), + ('bitRotateRight', False, not_implemented_bigints('Bit rotate')), + ('bitTest', False, not_implemented_bigints('bitTest')), + ('bitTestAll', False, illegal_column()), + ('bitTestAny', False, illegal_column()), + ('bitNot', True, None), + ('bitCount', True, None) +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] +Examples_dec_list = [tuple(list(func)+[Name(f'{func[0]} - Decimal256')]) for func in funcs] + +@TestOutline(Scenario) +@Examples('func supported error int_type min max', Examples_list) +def bit_int_inline(self, func, supported, error, int_type, min, max, node=None): + """ Check bit functions with Int128, UInt128, Int256, and UInt256 using inline tests. + """ + + if error is not None: + exitcode,message = error + + if node is None: + node = self.context.node + + if func in ["bitNot", "bitCount"]: + + with When(f"Check {func} with {int_type}"): + execute_query(f""" + SELECT {func}(to{int_type}(1)), {func}(to{int_type}(\'{max}\')), {func}(to{int_type}(\'{min}\')) + """) + + elif supported: + + with When(f"I check {func} with {int_type}"): + execute_query(f""" + SELECT {func}(to{int_type}(1), 1), {func}(to{int_type}(\'{max}\'), 1), {func}(to{int_type}(\'{min}\'), 1) + """) + + else: + + with When(f"I check {func} with {int_type}"): + node.query(f"SELECT {func}(to{int_type}(1), 1), {func}(to{int_type}(\'{max}\'), 1), {func}(to{int_type}(\'{min}\'), 1)", + exitcode=exitcode, message = message) + +@TestOutline(Scenario) +@Examples('func supported error int_type min max', Examples_list) +def bit_int_table(self, func, supported, error, int_type, min, max, node=None): + """ Check bit functions with Int128, UInt128, Int256, and UInt256 using table tests. + """ + + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + + if error is not None: + exitcode,message = error + + with Given(f"I have a table"): + table(name = table_name, data_type = int_type) + + if func in ["bitNot", "bitCount"]: + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'))") + + with Then(f"I check the table with values of {func} and {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + elif supported: + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'), 1)") + + with Then(f"I check the table with values of {func} and {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + else: + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'), 1)", + exitcode=exitcode, message=message) + +@TestOutline(Scenario) +@Examples('func supported error', Examples_dec_list) +def bit_dec_inline(self, func, supported, error, node=None): + """ Check bit functions with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + exitcode, message = illegal_type() + + if node is None: + node = self.context.node + + if func in ["bitNot", "bitCount"]: + + with When(f"Check {func} with Decimal256"): + node.query(f"SELECT {func}(toDecimal256(1,0)), {func}(toDecimal256(\'{max}\',0)), {func}(toDecimal256(\'{min}\',0))", + exitcode=exitcode, message = message) + + else: + + with When(f"I check {func} with Decimal256"): + node.query(f"SELECT {func}(toDecimal256(1,0), 1), {func}(toDecimal256(\'{max}\',0), 1), {func}(toDecimal256(\'{min}\',0), 1)", + exitcode=exitcode, message = message) + +@TestOutline(Scenario) +@Examples('func supported error', Examples_dec_list) +def bit_dec_table(self, func, supported, error, node=None): + """ Check bit functions with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + table_name = f"table_{getuid()}" + exitcode, message = illegal_type() + + if node is None: + node = self.context.node + + with Given(f"I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + if func in ["bitNot", "bitCount"]: + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0))", + exitcode=exitcode, message = message) + + else: + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0), 1)", + exitcode=exitcode, message=message) + +@TestFeature +@Name("bit") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Int_NotSupported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Bit_Dec_NotSupported("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that bit functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=bit_int_inline) + Scenario(run=bit_int_table) + Scenario(run=bit_dec_inline) + Scenario(run=bit_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/comparison.py b/tests/testflows/extended_precision_data_types/tests/comparison.py new file mode 100644 index 00000000000..d3e5dcc0dbd --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/comparison.py @@ -0,0 +1,110 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('equals',), + ('notEquals',), + ('less',), + ('greater',), + ('lessOrEquals',), + ('greaterOrEquals',) +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] + +@TestOutline(Scenario) +@Examples('func int_type min max', Examples_list) +def comp_int_inline(self, func, int_type, min, max, node=None): + """Check comparison functions with Int128, UInt128, Int256, and UInt256 using inline tests. + """ + + if node is None: + node = self.context.node + + with When(f"I check {func} with {int_type}"): + execute_query(f""" + SELECT {func}(to{int_type}(1), to{int_type}(1)), {func}(to{int_type}(\'{max}\'), to{int_type}(\'{min}\')) + """) + +@TestOutline(Scenario) +@Examples('func int_type min max', Examples_list) +def comp_int_table(self, func, int_type, min, max, node=None): + """Check comparison functions with Int128, UInt128, Int256, and UInt256 using table tests. + """ + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = int_type) + + for value in [1, max, min]: + + with When(f"I insert into a table the output {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'), to{int_type}(1))") + + with Then(f"I check the table for the output of {func} with {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline(Scenario) +@Examples('func', funcs) +def comp_dec_inline(self, func, node=None): + """Check comparison functions with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + with When(f"I check {func} with Decimal256"): + execute_query(f""" + SELECT {func}(toDecimal256(1,0), toDecimal256(1,0)), {func}(toDecimal256(\'{max}\',0), toDecimal256(\'{min}\',0)) + """) + +@TestOutline(Scenario) +@Examples('func', funcs) +def comp_dec_table(self, func, node=None): + """Check comparison functions with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + for value in [1, max, min]: + + with When(f"I insert into a table the output {func} with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0), toDecimal256(1,0))") + + with Then(f"I check the table for the output of {func} with Decimal256"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestFeature +@Name("comparison") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Comparison("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that comparison functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=comp_int_inline) + Scenario(run=comp_int_table) + Scenario(run=comp_dec_inline) + Scenario(run=comp_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/conversion.py b/tests/testflows/extended_precision_data_types/tests/conversion.py new file mode 100644 index 00000000000..e46031ad29f --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/conversion.py @@ -0,0 +1,275 @@ +import os +import textwrap + +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +@contextmanager +def dictionary(name, node, mysql_node): + """Create a table in MySQL and use it a source for a dictionary. + """ + try: + with Given("table in MySQL"): + sql = f""" + CREATE TABLE {name}( + id INT NOT NULL AUTO_INCREMENT, + int128 BIGINT, + uint128 BIGINT, + int256 BIGINT, + uint256 BIGINT, + dec256 DECIMAL, + PRIMARY KEY ( id ) + ); + """ + with When("I drop the table if exists"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + with And("I create a table"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + with And("dictionary that uses MySQL table as the external source"): + with When("I drop the dictionary if exists"): + node.query(f"DROP DICTIONARY IF EXISTS dict_{name}") + with And("I create the dictionary"): + sql = f""" + CREATE DICTIONARY dict_{name} + ( + id UInt8, + int128 Int128, + uint128 UInt128, + int256 Int256, + uint256 UInt256, + dec256 Decimal256(0) + ) + PRIMARY KEY id + SOURCE(MYSQL( + USER 'user' + PASSWORD 'password' + DB 'db' + TABLE '{name}' + REPLICA(PRIORITY 1 HOST '{mysql_node.name}' PORT 3306) + )) + LAYOUT(HASHED()) + LIFETIME(0) + """ + node.query(textwrap.dedent(sql)) + + yield f"dict_{name}" + + finally: + with Finally("I drop the dictionary", flags=TE): + node.query(f"DROP DICTIONARY IF EXISTS dict_{name}") + + with And("I drop a table in MySQL", flags=TE): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + +@contextmanager +def table(name, node, mysql_node): + """Create a table in MySQL and use it a source for a table in ClickHouse. + """ + try: + with Given("table in MySQL"): + sql = f""" + CREATE TABLE {name}( + id INT NOT NULL AUTO_INCREMENT, + int128 BIGINT, + uint128 BIGINT, + int256 BIGINT, + uint256 BIGINT, + dec256 DECIMAL, + PRIMARY KEY ( id ) + ); + """ + with When("I drop the table if exists"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + + with And("I create a table"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + with And("table that uses MySQL table as the external source"): + + with When("I drop the table if exists"): + node.query(f"DROP TABLE IF EXISTS {name}") + with And("I create the table"): + sql = f""" + CREATE TABLE {name} + ( + id UInt8, + int128 Int128, + uint128 UInt128, + int256 Int256, + uint256 UInt256, + dec256 Decimal256(0) + ) + ENGINE = MySQL('{mysql_node.name}:3306', 'default', '{name}', 'default', 'password') + """ + node.query(textwrap.dedent(sql)) + + yield f"table_{name}" + + finally: + with Finally("I drop the table", flags=TE): + node.query(f"DROP TABLE IF EXISTS {name}") + + with And("I drop a table in MySQL", flags=TE): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + +@contextmanager +def table_func(name, node, mysql_node): + """Create a table in MySQL and use it a source for a table using mysql table function. + """ + try: + with Given("table in MySQL"): + sql = f""" + CREATE TABLE {name}( + id INT NOT NULL AUTO_INCREMENT, + int128 BIGINT, + uint128 BIGINT, + int256 BIGINT, + uint256 BIGINT, + dec256 DECIMAL, + PRIMARY KEY ( id ) + ); + """ + with When("I drop the table if exists"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + with And("I create a table"): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + yield f"mysql('{mysql_node.name}:3306', 'db', '{name}', 'user', 'password')" + + finally: + + with Finally("I drop the table", flags=TE): + node.query(f"DROP TABLE IF EXISTS {name}") + + with And("I drop a table in MySQL", flags=TE): + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user -e \"DROP TABLE IF EXISTS {name};\"", exitcode=0) + +@TestOutline(Scenario) +@Examples('int_type min max',[ + ('Int128', '-170141183460469231731687303715884105728', '170141183460469231731687303715884105727', Requirements(RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt128("1.0")), Name('Int128')), + ('Int256', '-57896044618658097711785492504343953926634992332820282019728792003956564819968', '57896044618658097711785492504343953926634992332820282019728792003956564819967', Requirements(RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toInt256("1.0")), Name('Int256')), + ('UInt128','0','340282366920938463463374607431768211455', Requirements(RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt128("1.0")), Name('UInt128')), + ('UInt256', '0', '115792089237316195423570985008687907853269984665640564039457584007913129639935', Requirements(RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toUInt256("1.0")), Name('UInt256')), +]) +def int_conversion(self, int_type, min, max, node=None): + """Check that ClickHouse converts values to Int128. + """ + + if node is None: + node = self.context.node + + with When(f"I convert {min}, {max}, 1 to {int_type}"): + output = node.query(f"SELECT to{int_type}(\'{min}\'), to{int_type}(\'{max}\'), to{int_type}(1) format TabSeparatedRaw").output + assert output == f'{min}\t{max}\t1', error() + +@TestScenario +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_toDecimal256("1.0"), +) +def to_decimal256(self, node=None): + """Check that ClickHouse converts values to Int128. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + for value in [1,min,max]: + output = node.query(f"SELECT toDecimal256(\'{value}\',0)").output + assert output == str(value), error() + +@TestScenario +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_ToMySQL("1.0"), +) +def MySQL_table(self, node=None): + """Check that ClickHouse converts MySQL values from MySQL table into ClickHouse table. + """ + table_name = f'table_{getuid()}' + + node = self.context.node + mysql_node = self.context.mysql_node + + with table(table_name, node, mysql_node): + + with When("I insert parameters values in MySQL"): + sql = f""" + INSERT INTO {table_name}(int128, uint128, int256, uint256, dec256) VALUES (1,1,1,1,1); + """ + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + with Then("I select from the table on top of the mysql table"): + node.query(f"SELECT * FROM {table_name}", + exitcode=50, message='Exception:') + +@TestScenario +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_FromMySQL("1.0"), +) +def MySQL_func(self, node=None): + """Check that ClickHouse converts MySQL values into a ClickHouse table using the MySQL table function. + """ + table_name = f'table_{getuid()}' + + node = self.context.node + mysql_node = self.context.mysql_node + + with table_func(table_name, node, mysql_node) as table_function: + + with When("I insert parameters values in MySQL"): + sql = f""" + INSERT INTO {table_name}(int128, uint128, int256, uint256, dec256) VALUES (1,1,1,1,1); + """ + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + with And("I make sure the table doesn't exist"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + with And("I create the table"): + node.query(f"CREATE TABLE {table_name} (id UInt8, int128 Int128, uint128 UInt128, int256 Int256, uint256 UInt256, dec256 Decimal256(0)) Engine = Memory") + + with And("I insert into the clickhouse table from the mysql table"): + node.query(f"INSERT INTO {table_name} SELECT * FROM {table_function}") + + with Then("I select from the clickhouse table"): + output = node.query(f"SELECT * FROM {table_name}").output + assert output == '1\t1\t1\t1\t1\t1', error() + +@TestScenario +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Conversion_ToMySQL("1.0"), +) +def MySQL_dict(self, node=None): + """Check that ClickHouse converts MySQL values from MySQL table into ClickHouse dictionary. + """ + + node = self.context.node + mysql_node = self.context.mysql_node + + table_name = f'table_{getuid()}' + + with dictionary(table_name, node, mysql_node): + + with When("I insert parameters values in MySQL"): + sql = f""" + INSERT INTO {table_name}(int128, uint128, int256, uint256, dec256) VALUES (1,1,1,1,1); + """ + mysql_node.command(f"MYSQL_PWD=password mysql -D db -u user <<'EOF'{textwrap.dedent(sql)}\nEOF", exitcode=0) + + with Then("I select from the table on top of the mysql table"): + node.query(f"SELECT * FROM dict_{table_name}", + exitcode=50, message='Exception:') + +@TestFeature +@Name("conversion") +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check the conversion of extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + for scenario in loads(current_module(), Scenario): + with allow_experimental_bigint(self.context.node): + Scenario(run=scenario) diff --git a/tests/testflows/extended_precision_data_types/tests/feature.py b/tests/testflows/extended_precision_data_types/tests/feature.py new file mode 100644 index 00000000000..83293b61f35 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/feature.py @@ -0,0 +1,19 @@ +from testflows.core import * +from testflows.core.name import basename, parentname +from testflows._core.testtype import TestSubType + +@TestFeature +@Name("tests") +def feature(self): + """Check functions with Int128, Int256, UInt256, and Decimal256. + """ + Feature(run=load("extended_precision_data_types.tests.conversion", "feature")) + Feature(run=load("extended_precision_data_types.tests.arithmetic", "feature")) + Feature(run=load("extended_precision_data_types.tests.array_tuple_map", "feature")) + Feature(run=load("extended_precision_data_types.tests.comparison", "feature")) + Feature(run=load("extended_precision_data_types.tests.logical", "feature")) + Feature(run=load("extended_precision_data_types.tests.mathematical", "feature")) + Feature(run=load("extended_precision_data_types.tests.rounding", "feature")) + Feature(run=load("extended_precision_data_types.tests.bit", "feature")) + Feature(run=load("extended_precision_data_types.tests.null", "feature")) + Feature(run=load("extended_precision_data_types.tests.table", "feature")) diff --git a/tests/testflows/extended_precision_data_types/tests/logical.py b/tests/testflows/extended_precision_data_types/tests/logical.py new file mode 100644 index 00000000000..36dbc97da97 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/logical.py @@ -0,0 +1,99 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('and',), + ('or',), + ('not',), + ('xor',), +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] + +@TestOutline(Scenario) +@Examples('func int_type min max', Examples_list) +def log_int_inline(self, func, int_type, min, max, node=None): + """Check logical functions with Int128, Int256, and UInt256 using inline tests. + """ + table_name = f'table_{getuid()}' + + if node is None: + node = self.context.node + + with When(f"Check {func} with {int_type}"): + node.query(f"SELECT {func}(to{int_type}(1), to{int_type}(1)), {func}(to{int_type}(\'{max}\'), to{int_type}(1)), {func}(to{int_type}(\'{min}\'), to{int_type}(1))", + exitcode=43, message = 'Exception: Illegal type ') + +@TestOutline(Scenario) +@Examples('func int_type min max', Examples_list) +def log_int_table(self, func, int_type, min, max, node=None): + """Check logical functions with Int128, Int256, and UInt256 using table tests. + """ + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = int_type) + + for value in [1, min, max]: + + with When(f"Check {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'), to{int_type}(\'{value}\'))", + exitcode=43, message = 'Exception: Illegal type') + +@TestOutline(Scenario) +@Examples('func', funcs) +def log_dec_inline(self, func, node=None): + """Check logical functions with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + with When(f"Check {func} with Decimal256"): + node.query(f"SELECT {func}(toDecimal256(1,0), toDecimal256(1,0)), {func}(toDecimal256(\'{max}\',0), toDecimal256(1)), {func}(toDecimal256(\'{min}\',0), toDecimal256(1))", + exitcode=43, message = 'Exception: Illegal type ') + +@TestOutline(Scenario) +@Examples('func', funcs) +def log_dec_table(self, func, node=None): + """Check logical functions with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + for value in [1, min, max]: + + with When(f"Check {func} with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0), toDecimal256(\'{value}\',0))", + exitcode=43, message = 'Exception: Illegal type ') + +@TestFeature +@Name("logical") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Logical("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that comparison functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=log_int_inline) + Scenario(run=log_int_table) + Scenario(run=log_dec_inline) + Scenario(run=log_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/mathematical.py b/tests/testflows/extended_precision_data_types/tests/mathematical.py new file mode 100644 index 00000000000..6062a00a7c9 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/mathematical.py @@ -0,0 +1,187 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('exp(', 3, 0), + ('log(', 0, 0), + ('ln(', 0, 0), + ('exp2(', 2, 0), + ('log2(', 0, 0), + ('exp10(', 10, 0), + ('log10(', 0, 0), + ('sqrt(', 1, 0), + ('cbrt(', 1, 0), + ('erf(', 1, 0), + ('erfc(', 0, 0), + ('lgamma(', 0, 0), + ('tgamma(', 1, 0), + ('sin(', 1, 0), + ('cos(', 1, 0), + ('tan(', 2, 0), + ('asin(', 2, 0), + ('acos(', 0, 0), + ('atan(', 1, 0), + ('intExp2(', 2, 48), + ('intExp10(', 10, 48), + ('cosh(', 2, 0), + ('acosh(', 0, 0), + ('sinh(', 1, 0), + ('asinh(', 1, 0), + ('tanh(', 1, 0), + ('atanh(', 'inf', 0), + ('log1p(', 1, 0), + ('sign(', 1, 0), + ('pow(1,', 1, 43), + ('power(1,', 1, 43), + ('atan2(1,', 1, 43), + ('hypot(1,', 1, 43), +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] +Examples_dec_list = [tuple(list(func)+[Name(f'{func[0]} - Decimal256')]) for func in funcs] + +@TestOutline(Scenario) +@Examples('func expected_result exitcode int_type min max', Examples_list) +def math_int_inline(self, func, expected_result, exitcode, int_type, min, max, node=None): + """Check mathematical functions with Int128, UInt128, Int256, and UInt256 using inline tests. + """ + if node is None: + node = self.context.node + + if func in ['intExp2(', 'intExp10(', 'pow(1,', 'power(1,', 'atan2(1,', 'hypot(1,']: + + with When(f"I check {func} with {int_type} using 1, max, and min"): + node.query(f"SELECT {func} to{int_type}(1)), {func} to{int_type}(\'{max}\')), {func} to{int_type}(\'{min}\'))", + exitcode=exitcode, message = 'Exception:') + + else: + + with When(f"I check {func} with {int_type} using 1"): + output = node.query(f"SELECT {func} to{int_type}(1))").output + if output == 'inf': + pass + else: + assert round(float(output)) == expected_result, error() + + with And(f"I check {func} with {int_type} using max and min"): + execute_query(f""" + SELECT {func} to{int_type}(\'{max}\')), {func} to{int_type}(\'{min}\')) + """) + +@TestOutline(Scenario) +@Examples('func expected_result exitcode int_type min max', Examples_list) +def math_int_table(self, func, expected_result, exitcode, int_type, min, max, node=None): + """Check mathematical functions with Int128, UInt128, Int256, and UInt256 using table tests. + """ + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = f'Nullable({int_type})') + + if func in ['intExp2(', 'intExp10(', 'pow(1,', 'power(1,', 'atan2(1,', 'hypot(1,']: + + for value in [1, max, min]: + + with When(f"I insert the output of {func} with {int_type} using {value} into a table"): + node.query(f"INSERT INTO {table_name} SELECT {func} to{int_type}(\'{value}\'))", + exitcode=exitcode, message = 'Exception:') + + else: + + for value in [1, max, min]: + + with And(f"I insert the output of {func} with {int_type} using {value} into a table"): + node.query(f"INSERT INTO {table_name} SELECT to{int_type}OrZero( toString({func} to{int_type}(\'{value}\'))))") + + with Then(f"I check the outputs of {func} with {int_type}"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline(Scenario) +@Examples('func expected_result exitcode', Examples_dec_list) +def math_dec_inline(self, func, expected_result, exitcode, node=None): + """Check mathematical functions with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + if func in ['intExp2(', 'intExp10(', 'pow(1,', 'power(1,', 'atan2(1,', 'hypot(1,']: + + with When(f"I check {func} with Decimal256 using 1, max, and min"): + node.query(f"SELECT {func} toDecimal256(1,0)), {func} toDecimal256(\'{max}\',0)), {func} toDecimal256(\'{min}\',0))", + exitcode=43, message = 'Exception: Illegal type ') + + else: + + with When(f"I check {func} with Decimal256 using 1"): + output = node.query(f"SELECT {func} toDecimal256(1,0))").output + if output == 'inf': + pass + else: + assert round(float(output)) == expected_result, error() + + with And(f"I check {func} with Decimal256 using max and min"): + execute_query(f""" + SELECT {func} toDecimal256(\'{max}\',0)), {func} toDecimal256(\'{min}\',0)) + """) + +@TestOutline(Scenario) +@Examples('func expected_result exitcode', Examples_dec_list) +def math_dec_table(self, func, expected_result, exitcode, node=None): + """Check mathematical functions with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + table_name = f'table_{getuid()}' + + with Given(f"I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + if func in ['intExp2(', 'intExp10(', 'pow(1,', 'power(1,', 'atan2(1,', 'hypot(1,']: + + for value in [1, max, min]: + + with When(f"I insert the output of {func} with Decimal256 using {value} into a table"): + node.query(f"INSERT INTO {table_name} SELECT {func} toDecimal256(\'{value}\',0))", + exitcode=43, message = 'Exception: Illegal type ') + + else: + + for value in [1, max, min]: + + with When(f"I insert the output of {func} with Decimal256 using {value} into a table"): + node.query(f"INSERT INTO {table_name} SELECT toDecimal256OrZero( toString({func} toDecimal256(\'{value}\',0))),0)") + + with Then(f"I check the outputs of {func} with Decimal256"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestFeature +@Name("mathematical") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Mathematical_NotSupported("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that mathematical functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=math_int_inline) + Scenario(run=math_int_table) + Scenario(run=math_dec_inline) + Scenario(run=math_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/null.py b/tests/testflows/extended_precision_data_types/tests/null.py new file mode 100644 index 00000000000..9ddaa94a245 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/null.py @@ -0,0 +1,119 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('isNull(', 0), + ('isNotNull(', 1), + ('coalesce(', 1), + ('assumeNotNull(', 1), + ('toNullable(', 1), + ('ifNull(1,', 1), + ('nullIf(1,', '\\N'), +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] + +@TestOutline(Scenario) +@Examples('func expected_result int_type min max', Examples_list) +def null_int_inline(self, func, expected_result, int_type, min, max, node=None): + """Check null function with Int128, UInt128, Int256, and UInt256 using inline tests. + """ + + if node is None: + node = self.context.node + + with When(f"I check {func} with {int_type}"): + output = node.query(f"SELECT {func} to{int_type}(1))").output + assert output == str(expected_result), error() + + with And(f"I check {func} with {int_type} using min and max"): + execute_query(f""" + SELECT {func} to{int_type}(\'{min}\')), {func} to{int_type}(\'{max}\')) + """) + +@TestOutline(Scenario) +@Examples('func expected_result int_type min max', Examples_list) +def null_int_table(self, func, expected_result, int_type, min, max, node=None): + """Check null function with Int128, UInt128, Int256, and UInt256 using table tests. + """ + + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + + with Given("I have a table"): + table(name = table_name, data_type = f'Nullable({int_type})') + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func} to{int_type}(\'{value}\'))") + + with Then(f"I check {func} with {int_type} on the table"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestOutline(Scenario) +@Examples('func expected_result', funcs) +def null_dec_inline(self, func, expected_result, node=None): + """Check null function with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + with When(f"I check {func} with Decimal256"): + output = node.query(f"SELECT {func} toDecimal256(1,0))").output + assert output == str(expected_result), error() + + with And(f"I check {func} with Decimal256 using min and max"): + execute_query(f""" + SELECT {func} toDecimal256(\'{min}\',0)), {func} toDecimal256(\'{max}\',0)) + """) + +@TestOutline(Scenario) +@Examples('func expected_result', funcs) +def null_dec_table(self, func, expected_result, node=None): + """Check null function with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + + with Given("I have a table"): + table(name = table_name, data_type = 'Nullable(Decimal256(0))') + + for value in [1, min, max]: + + with When(f"I insert the output of {func} with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT {func} toDecimal256(\'{value}\',0))") + + with Then(f"I check {func} with Decimal256 on the table"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + +@TestFeature +@Name("null") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Null("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that null functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=null_int_inline) + Scenario(run=null_int_table) + Scenario(run=null_dec_inline) + Scenario(run=null_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/rounding.py b/tests/testflows/extended_precision_data_types/tests/rounding.py new file mode 100644 index 00000000000..f01d6898b32 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/rounding.py @@ -0,0 +1,191 @@ +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +funcs = [ + ('ceil', 1, True), + ('floor', 1, True), + ('trunc', 1, True), + ('round', 1, True), + ('roundBankers', 1, True), + ('roundToExp2', 1, False), + ('roundDuration', 1, True), + ('roundAge', 17, True), + ('roundDown', 1, False) +] + +Examples_list = [tuple(list(func)+list(data_type)+[Name(f'{func[0]} - {data_type[0]}')]) for func in funcs for data_type in data_types] +Examples_dec_list = [tuple(list(func)+[Name(f'{func[0]} - Decimal256')]) for func in funcs] + +@TestOutline(Scenario) +@Examples('func expected_result supported int_type min max', Examples_list) +def round_int_inline(self, func, expected_result, supported, int_type, min, max, node=None): + """Check rounding functions with Int128, UInt128, Int256, and UInt256 using inline tests. + """ + + if node is None: + node = self.context.node + + if func is 'roundDown': + + with When(f"I check roundDown with {int_type}"): + node.query(f"SELECT roundDown(to{int_type}(1), [0,2]), roundDown(to{int_type}(\'{max}\'), [0,2]), roundDown(to{int_type}(\'{min}\'), [0,2])", + exitcode=44, message=f'Exception: Illegal column {int_type} of first argument of function roundDown') + + elif supported: + + with When(f"I check {func} with {int_type}"): + output = node.query(f"SELECT {func}(to{int_type}(1))").output + assert output == str(expected_result), error() + + with And(f'I check {func} with {int_type} using min and max values'): + execute_query(f""" + SELECT {func}(to{int_type}(\'{min}\')), {func}(to{int_type}(\'{max}\')) + """) + + else: + + with When(f"I check {func} with {int_type}"): + node.query(f"SELECT {func}(to{int_type}(1)), {func}(to{int_type}(\'{max}\')), {func}(to{int_type}(\'{min}\'))", + exitcode=48, message=f'Exception: {func}() for big integers is not implemented:') + +@TestOutline(Scenario) +@Examples('func expected_result supported int_type min max', Examples_list) +def round_int_table(self, func, expected_result, supported, int_type, min, max, node=None): + """Check rounding functions with Int128, UInt128, Int256, and UInt256 using table tests. + """ + + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + + with Given("I have a table"): + table(name = table_name, data_type = int_type) + + if func is 'roundDown': + + for value in [1,max,min]: + + with When(f"I check roundDown with {int_type} and {value}"): + node.query(f"INSERT INTO {table_name} SELECT roundDown(to{int_type}(\'{value}\'), [0,2])", + exitcode=44, message=f'Exception: Illegal column {int_type} of first argument of function roundDown') + + elif supported: + + for value in [1,max,min]: + + with When(f"I insert the output of {func} with {int_type} and {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(\'{value}\'))") + + with Then(f"I select the output of {func} with {int_type} from the table"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + else: + + for value in [1,max,min]: + + with When(f"I insert the output of {func} with {int_type} and {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(to{int_type}(1))", + exitcode=48, message=f'Exception: {func}() for big integers is not implemented:') + +@TestOutline(Scenario) +@Examples('func expected_result supported', Examples_dec_list) +def round_dec_inline(self, func, expected_result, supported, node=None): + """Check rounding functions with Decimal256 using inline tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + if node is None: + node = self.context.node + + if func is 'roundDown': + + with When(f"I check roundDown with Decimal256"): + node.query(f"""SELECT roundDown(toDecimal256(1,0), [toDecimal256(0,0),toDecimal256(2,0)]), + roundDown(toDecimal256(\'{max}\',0), [toDecimal256(0,0),toDecimal256(2,0)]), + roundDown(toDecimal256(\'{min}\',0), [toDecimal256(0,0),toDecimal256(2,0)])""", + exitcode=44, message=f'Exception: Illegal column Decimal256 of first argument of function roundDown') + + elif func not in ['roundDuration', 'roundAge', 'roundToExp2']: + + with When(f"I check {func} with Decimal256"): + output = node.query(f"SELECT {func}(toDecimal256(1,0))").output + assert output == str(expected_result), error() + + with And(f'I check {func} with Decimal256 using min and max values'): + execute_query(f""" + SELECT {func}(toDecimal256(\'{min}\',0)), {func}(toDecimal256(\'{max}\',0)) + """) + + else: + + with When(f"I check {func} with Decimal256"): + node.query(f"SELECT {func}(toDecimal256(1,0)), {func}(toDecimal256(\'{max}\',0)), {func}(toDecimal256(\'{min}\',0))", + exitcode=43, message=f'Exception: Illegal type Decimal(76, 0)') + +@TestOutline(Scenario) +@Examples('func expected_result supported', Examples_dec_list) +def round_dec_table(self, func, expected_result, supported, node=None): + """Check rounding functions with Decimal256 using table tests. + """ + min = Decimal256_min_max[0] + max = Decimal256_min_max[1] + + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + + with Given("I have a table"): + table(name = table_name, data_type = 'Decimal256(0)') + + if func is 'roundDown': + + for value in [1, max, min]: + + with When(f"I check roundDown with Decimal256 and {value}"): + node.query(f"INSERT INTO {table_name} SELECT roundDown(toDecimal256(\'{value}\',0), [toDecimal256(0,0),toDecimal256(2,0)])", + exitcode=44, message=f'Exception: Illegal column Decimal256 of first argument of function roundDown') + + elif func not in ['roundDuration', 'roundAge', 'roundToExp2']: + + for value in [1, max, min]: + + with When(f"I insert the output of {func} with Decimal256 and {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0))") + + with Then(f"I select the output of {func} with Decimal256 from the table"): + execute_query(f""" + SELECT * FROM {table_name} ORDER BY a ASC + """) + + else: + + for value in [1, max, min]: + + with When(f"I insert the output of {func} with Decimal256 and {value} into the table"): + node.query(f"INSERT INTO {table_name} SELECT {func}(toDecimal256(\'{value}\',0))", + exitcode=43, message=f'Exception: Illegal type Decimal(76, 0)') + +@TestFeature +@Name("rounding") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Int_NotSupported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_Supported("1.0"), + RQ_SRS_020_ClickHouse_Extended_Precision_Rounding_Dec_NotSupported("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that rounding functions work with extended precision data types. + """ + self.context.node = self.context.cluster.node(node) + self.context.mysql_node = self.context.cluster.node(mysql_node) + + with allow_experimental_bigint(self.context.node): + Scenario(run=round_int_inline) + Scenario(run=round_int_table) + Scenario(run=round_dec_inline) + Scenario(run=round_dec_table) diff --git a/tests/testflows/extended_precision_data_types/tests/table.py b/tests/testflows/extended_precision_data_types/tests/table.py new file mode 100644 index 00000000000..1548d6b20c2 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/tests/table.py @@ -0,0 +1,35 @@ +from testflows.core import * +from testflows.asserts import error +from contextlib import contextmanager + +from extended_precision_data_types.requirements import * +from extended_precision_data_types.common import * + +@TestFeature +@Name("table") +@Requirements( + RQ_SRS_020_ClickHouse_Extended_Precision_Create_Table("1.0"), +) +def feature(self, node="clickhouse1", mysql_node="mysql1", stress=None, parallel=None): + """Check that clickhouse is able to create a table with extended precision data types. + """ + node = self.context.cluster.node(node) + + table_name = f"table_{getuid()}" + + with allow_experimental_bigint(node): + + try: + with When("I create a table with Int128, UInt128, Int256, UInt256, Decimal256"): + node.query(f"CREATE TABLE {table_name}(a Int128, b UInt128, c Int256, d UInt256, e Decimal256(0)) ENGINE = Memory") + + with And("I insert values into the table"): + node.query(f"INSERT INTO {table_name} VALUES (toInt128(1), toUInt128(1), toInt256(1), toUInt256(1), toDecimal256(1,0))") + + with Then("I select from the table"): + output = node.query(f"SELECT * FROM {table_name}").output + assert output == '1\t1\t1\t1\t1', error() + + finally: + with Finally("I drop the table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index 2547463a91d..c7a264a9c27 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -30,6 +30,7 @@ def regression(self, local, clickhouse_binary_path, stress=None, parallel=None): run_scenario(pool, tasks, Feature(test=load("window_functions.regression", "regression")), args) run_scenario(pool, tasks, Feature(test=load("datetime64_extended_range.regression", "regression")), args) #run_scenario(pool, tasks, Feature(test=load("kerberos.regression", "regression")), args) + run_scenario(pool, tasks, Feature(test=load("extended_precision_data_types.regression", "regression")), args) finally: join(tasks) diff --git a/tests/testflows/runner b/tests/testflows/runner index 213ff6e50d8..772a4d01a84 100755 --- a/tests/testflows/runner +++ b/tests/testflows/runner @@ -126,5 +126,7 @@ if __name__ == "__main__": move_from = os.path.join(args.clickhouse_root, 'tests/testflows') status = os.path.join(move_from, 'check_status.tsv') results = os.path.join(move_from, 'test_results.tsv') + clickhouse_logs = os.path.join(move_from, 'clickhouse_logs.tar.gz') subprocess.call("mv {} {}".format(status, result_path), shell=True) subprocess.call("mv {} {}".format(results, result_path), shell=True) + subprocess.call("mv {} {}".format(clickhouse_logs, result_path), shell=True) diff --git a/utils/github/backport.py b/utils/github/backport.py index 589124324b1..a28a1510694 100644 --- a/utils/github/backport.py +++ b/utils/github/backport.py @@ -28,8 +28,7 @@ class Backport: def getBranchesWithRelease(self): branches = set() for pull_request in self._gh.find_pull_requests("release"): - if not pull_request['merged'] and not pull_request['closed']: - branches.add(pull_request['headRefName']) + branches.add(pull_request['headRefName']) return branches def execute(self, repo, upstream, until_commit, run_cherrypick): diff --git a/utils/github/query.py b/utils/github/query.py index 628d3a12dfd..39b1d0ce003 100644 --- a/utils/github/query.py +++ b/utils/github/query.py @@ -48,7 +48,7 @@ class Query: url ''' - def __init__(self, token, owner, name, team, max_page_size=100, min_page_size=5): + def __init__(self, token, owner, name, team, max_page_size=100, min_page_size=10): self._PULL_REQUEST = Query._PULL_REQUEST.format(min_page_size=min_page_size) self._token = token @@ -165,7 +165,7 @@ class Query: ''' _QUERY = ''' repository(owner: "{owner}" name: "{name}") {{ - pullRequests(first: {min_page_size} labels: "{label_name}") {{ + pullRequests(first: {min_page_size} labels: "{label_name}" states: OPEN) {{ nodes {{ {pull_request_data} }}