Merge branch 'evo' into beta

This commit is contained in:
XMRig 2019-10-10 13:34:18 +07:00
commit 08e9e834b9
98 changed files with 10309 additions and 7628 deletions

View file

@ -1,3 +1,8 @@
# v4.3.0-beta
- [#1227](https://github.com/xmrig/xmrig/pull/1227) Added new algorithm `rx/arq`, RandomX variant for upcoming ArQmA fork.
- [#808](https://github.com/xmrig/xmrig/issues/808#issuecomment-539297156) Added experimental support for persistent memory for CPU mining threads.
- [#1221](https://github.com/xmrig/xmrig/issues/1221) Improved RandomX dataset memory usage and initialization speed for NUMA machines.
# v4.2.1-beta
- [#1150](https://github.com/xmrig/xmrig/issues/1150) Fixed build on FreeBSD.
- [#1175](https://github.com/xmrig/xmrig/issues/1175) Fixed support for systems where total count of NUMA nodes not equal usable nodes count.

View file

@ -55,6 +55,7 @@ set(HEADERS
)
set(HEADERS_CRYPTO
src/backend/common/interfaces/IMemoryPool.h
src/crypto/cn/asm/CryptonightR_template.h
src/crypto/cn/c_blake256.h
src/crypto/cn/c_groestl.h
@ -73,6 +74,7 @@ set(HEADERS_CRYPTO
src/crypto/common/Algorithm.h
src/crypto/common/Coin.h
src/crypto/common/keccak.h
src/crypto/common/MemoryPool.h
src/crypto/common/Nonce.h
src/crypto/common/portable/mm_malloc.h
src/crypto/common/VirtualMemory.h
@ -111,10 +113,22 @@ set(SOURCES_CRYPTO
src/crypto/common/Algorithm.cpp
src/crypto/common/Coin.cpp
src/crypto/common/keccak.cpp
src/crypto/common/MemoryPool.cpp
src/crypto/common/Nonce.cpp
src/crypto/common/VirtualMemory.cpp
)
if (WITH_HWLOC)
list(APPEND HEADERS_CRYPTO
src/crypto/common/NUMAMemoryPool.h
)
list(APPEND SOURCES_CRYPTO
src/crypto/common/NUMAMemoryPool.cpp
src/crypto/common/VirtualMemory_hwloc.cpp
)
endif()
if (WIN32)
set(SOURCES_OS
"${SOURCES_OS}"

View file

@ -30,54 +30,69 @@ XMRig is a high performance RandomX and CryptoNight CPU miner, with official sup
## Usage
The preferred way to configure the miner is the [JSON config file](src/config.json) as it is more flexible and human friendly. The command line interface does not cover all features, such as mining profiles for different algorithms. Important options can be changed during runtime without miner restart by editing the config file or executing API calls.
### Options
### Command line options
```
-a, --algo=ALGO specify the algorithm to use
cn/r, cn/2, cn/1, cn/0, cn/double, cn/half, cn/fast,
cn/rwz, cn/zls, cn/xao, cn/rto, cn/gpu,
cn-lite/1,
cn-heavy/xhv, cn-heavy/tube, cn-heavy/0,
cn-pico,
rx/wow, rx/loki
Network:
-o, --url=URL URL of mining server
-O, --userpass=U:P username:password pair for mining server
-a, --algo=ALGO mining algorithm https://xmrig.com/docs/algorithms
--coin=COIN specify coin instead of algorithm
-u, --user=USERNAME username for mining server
-p, --pass=PASSWORD password for mining server
--rig-id=ID rig identifier for pool-side statistics (needs pool support)
-t, --threads=N number of miner threads
-v, --av=N algorithm variation, 0 auto select
-O, --userpass=U:P username:password pair for mining server
-k, --keepalive send keepalived packet for prevent timeout (needs pool support)
--nicehash enable nicehash.com support
--rig-id=ID rig identifier for pool-side statistics (needs pool support)
--tls enable SSL/TLS support (needs pool support)
--tls-fingerprint=F pool TLS certificate fingerprint, if set enable strict certificate pinning
--tls-fingerprint=HEX pool TLS certificate fingerprint for strict certificate pinning
--daemon use daemon RPC instead of pool for solo mining
--daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)
-r, --retries=N number of times to retry before switch to backup server (default: 5)
-R, --retry-pause=N time to pause between retries (default: 5)
--user-agent set custom user-agent string for pool
--donate-level=N donate level, default 5%% (5 minutes in 100 minutes)
--donate-over-proxy=N control donate over xmrig-proxy feature
CPU backend:
--no-cpu disable CPU mining backend
-t, --threads=N number of CPU threads
-v, --av=N algorithm variation, 0 auto select
--cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1
--cpu-priority set process priority (0 idle, 2 normal to 5 highest)
--cpu-max-threads-hint=N maximum CPU threads count (in percentage) hint for autoconfig
--cpu-memory-pool=N number of 2 MB pages for persistent memory pool, -1 (auto), 0 (disable)
--no-huge-pages disable huge pages support
--no-color disable colored output
--donate-level=N donate level, default 5% (5 minutes in 100 minutes)
--user-agent set custom user-agent string for pool
-B, --background run the miner in the background
-c, --config=FILE load a JSON-format configuration file
-l, --log-file=FILE log all output to a file
--asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer.
--print-time=N print hashrate report every N seconds
--asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer
--randomx-init=N threads count to initialize RandomX dataset
--randomx-no-numa disable NUMA support for RandomX
API:
--api-worker-id=ID custom worker-id for API
--api-id=ID custom instance ID for API
--http-enabled enable HTTP API
--http-host=HOST bind host for HTTP API (default: 127.0.0.1)
--http-port=N bind port for HTTP API
--http-access-token=T access token for HTTP API
--http-no-restricted enable full remote access to HTTP API (only if access token set)
--randomx-init=N threads count to initialize RandomX dataset
--randomx-no-numa disable NUMA support for RandomX
--export-topology export hwloc topology to a XML file and exit
--dry-run test configuration and exit
-h, --help display this help and exit
OpenCL backend:
--opencl enable OpenCL mining backend
--opencl-devices=N list of OpenCL devices to use
--opencl-platform=N OpenCL platform index or name
--opencl-loader=N path to OpenCL-ICD-Loader (OpenCL.dll or libOpenCL.so)
--opencl-no-cache disable OpenCL cache
--print-platforms print available OpenCL platforms and exit
Logging:
-l, --log-file=FILE log all output to a file
--print-time=N print hashrate report every N seconds
--no-color disable colored output
Misc:
-c, --config=FILE load a JSON-format configuration file
-B, --background run the miner in the background
-V, --version output version information and exit
-h, --help display this help and exit
--dry-run test configuration and exit
--export-topology export hwloc topology to a XML file and exit
```
## Donations

View file

@ -4,9 +4,12 @@ if (WITH_RANDOMX)
list(APPEND HEADERS_CRYPTO
src/crypto/rx/Rx.h
src/crypto/rx/RxAlgo.h
src/crypto/rx/RxBasicStorage.h
src/crypto/rx/RxCache.h
src/crypto/rx/RxConfig.h
src/crypto/rx/RxDataset.h
src/crypto/rx/RxQueue.h
src/crypto/rx/RxSeed.h
src/crypto/rx/RxVm.h
)
@ -32,9 +35,11 @@ if (WITH_RANDOMX)
src/crypto/randomx/vm_interpreted.cpp
src/crypto/rx/Rx.cpp
src/crypto/rx/RxAlgo.cpp
src/crypto/rx/RxBasicStorage.cpp
src/crypto/rx/RxCache.cpp
src/crypto/rx/RxConfig.cpp
src/crypto/rx/RxDataset.cpp
src/crypto/rx/RxQueue.cpp
src/crypto/rx/RxVm.cpp
)
@ -63,6 +68,21 @@ if (WITH_RANDOMX)
if (CMAKE_CXX_COMPILER_ID MATCHES Clang)
set_source_files_properties(src/crypto/randomx/jit_compiler_x86.cpp PROPERTIES COMPILE_FLAGS -Wno-unused-const-variable)
endif()
if (WITH_HWLOC)
list(APPEND HEADERS_CRYPTO
src/crypto/rx/RxNUMAStorage.h
)
list(APPEND SOURCES_CRYPTO
src/crypto/rx/RxConfig_hwloc.cpp
src/crypto/rx/RxNUMAStorage.cpp
)
else()
list(APPEND SOURCES_CRYPTO
src/crypto/rx/RxConfig_basic.cpp
)
endif()
else()
remove_definitions(/DXMRIG_ALGO_RANDOMX)
endif()

View file

@ -3,7 +3,7 @@
Algorithm can be defined in 3 ways:
1. By pool, using algorithm negotiation, in this case no need specify algorithm on miner side.
2. Per pool `coin` option, currently only usable value for this option is `monero`.
2. Per pool `coin` option, currently only usable values for this option is `monero` and `arqma`.
3. Per pool `algo` option.
Option `coin` useful for pools without algorithm negotiation support or daemon to allow automatically switch algorithm in next hard fork.
@ -12,11 +12,12 @@ Option `coin` useful for pools without algorithm negotiation support or daemon t
| Name | Memory | Version | Notes |
|------|--------|---------|-------|
| `rx/arq` | 256 KB | 4.3.0+ | RandomARQ (RandomX variant for ArQmA). |
| `rx/0` | 2 MB | 3.2.0+ | RandomX (Monero). |
| `argon2/chukwa` | 512 KB | 3.1.0+ | Argon2id (Chukwa). |
| `argon2/wrkz` | 256 KB | 3.1.0+ | Argon2id (WRKZ) |
| `rx/wow` | 1 MB | 3.0.0+ | RandomWOW. |
| `rx/loki` | 2 MB | 3.0.0+ | RandomXL. |
| `rx/wow` | 1 MB | 3.0.0+ | RandomWOW (RandomX variant for Wownero). |
| `rx/loki` | 2 MB | 3.0.0+ | RandomXL (RandomX variant for Loki). |
| `cn/fast` | 2 MB | 3.0.0+ | CryptoNight variant 1 with half iterations. |
| `cn/rwz` | 2 MB | 2.14.0+ | CryptoNight variant 2 with 3/4 iterations and reversed shuffle operation. |
| `cn/zls` | 2 MB | 2.14.0+ | CryptoNight variant 2 with 3/4 iterations. |

View file

@ -97,3 +97,6 @@ Allow override automatically detected Argon2 implementation, this option added m
#### `max-threads-hint` (since v4.2.0)
Maximum CPU threads count (in percentage) hint for autoconfig. [CPU_MAX_USAGE.md](CPU_MAX_USAGE.md)
#### `memory-pool` (since v4.3.0)
Use continuous, persistent memory block for mining threads, useful for preserve huge pages allocation while algorithm swithing. Default value `false` (feature disabled) or `true` or specific count of 2 MB huge pages.

View file

@ -59,6 +59,7 @@ function rx()
'randomx_constants_monero.h',
'randomx_constants_wow.h',
'randomx_constants_loki.h',
'randomx_constants_arqma.h',
'aes.cl',
'blake2b.cl',
'randomx_vm.cl',

View file

@ -36,7 +36,6 @@
#include "core/config/Config.h"
#include "core/Controller.h"
#include "core/Miner.h"
#include "crypto/common/VirtualMemory.h"
#include "net/Network.h"
#include "Summary.h"
#include "version.h"
@ -80,8 +79,6 @@ int xmrig::App::exec()
m_console = new Console(this);
}
VirtualMemory::init(m_controller->config()->cpu().isHugePages());
Summary::print(m_controller);
if (m_controller->config()->isDryRun()) {

View file

@ -38,6 +38,11 @@ const char *ocl_tag();
#endif
#ifdef XMRIG_ALGO_RANDOMX
const char *rx_tag();
#endif
} // namespace xmrig

View file

@ -48,12 +48,21 @@ public:
inline bool isExist(const Algorithm &algo) const { return isDisabled(algo) || m_aliases.count(algo) > 0 || has(algo.shortName()); }
inline const T &get(const Algorithm &algo, bool strict = false) const { return get(profileName(algo, strict)); }
inline void disable(const Algorithm &algo) { m_disabled.insert(algo); }
inline void setAlias(const Algorithm &algo, const char *profile) { m_aliases[algo] = profile; }
inline void move(const char *profile, T &&threads)
inline size_t move(const char *profile, T &&threads)
{
if (has(profile)) {
return 0;
}
const size_t count = threads.count();
if (!threads.isEmpty()) {
m_profiles.insert({ profile, std::move(threads) });
}
return count;
}
const T &get(const String &profileName) const;

View file

@ -34,8 +34,7 @@ xmrig::Worker::Worker(size_t id, int64_t affinity, int priority) :
m_affinity(affinity),
m_id(id),
m_hashCount(0),
m_timestamp(0),
m_count(0)
m_timestamp(0)
{
m_node = VirtualMemory::bindToNUMANode(affinity);

View file

@ -54,8 +54,8 @@ protected:
const size_t m_id;
std::atomic<uint64_t> m_hashCount;
std::atomic<uint64_t> m_timestamp;
uint32_t m_node = 0;
uint64_t m_count;
uint32_t m_node = 0;
uint64_t m_count = 0;
};

View file

@ -3,6 +3,7 @@ set(HEADERS_BACKEND_COMMON
src/backend/common/Tags.h
src/backend/common/interfaces/IBackend.h
src/backend/common/interfaces/IRxListener.h
src/backend/common/interfaces/IRxStorage.h
src/backend/common/interfaces/IThread.h
src/backend/common/interfaces/IWorker.h
src/backend/common/misc/PciTopology.h

View file

@ -0,0 +1,53 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_IMEMORYPOOL_H
#define XMRIG_IMEMORYPOOL_H
#include <cstddef>
#include <cstdint>
namespace xmrig {
class IMemoryPool
{
public:
virtual ~IMemoryPool() = default;
virtual bool isHugePages(uint32_t node) const = 0;
virtual uint8_t *get(size_t size, uint32_t node) = 0;
virtual void release(uint32_t node) = 0;
};
} /* namespace xmrig */
#endif /* XMRIG_IMEMORYPOOL_H */

View file

@ -0,0 +1,53 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2016-2018 XMRig <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_IRXSTORAGE_H
#define XMRIG_IRXSTORAGE_H
#include <cstdint>
#include <utility>
namespace xmrig {
class Job;
class RxDataset;
class RxSeed;
class IRxStorage
{
public:
virtual ~IRxStorage() = default;
virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0;
virtual std::pair<uint32_t, uint32_t> hugePages() const = 0;
virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages) = 0;
};
} /* namespace xmrig */
#endif // XMRIG_IRXSTORAGE_H

View file

@ -111,13 +111,13 @@ public:
return;
}
LOG_INFO("%s" GREEN_BOLD(" READY") " threads %s%zu/%zu (%zu)" CLEAR " huge pages %s%zu/%zu %1.0f%%" CLEAR " memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"),
LOG_INFO("%s" GREEN_BOLD(" READY") " threads %s%zu/%zu (%zu)" CLEAR " huge pages %s%1.0f%% %zu/%zu" CLEAR " memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"),
tag,
m_errors == 0 ? CYAN_BOLD_S : YELLOW_BOLD_S,
m_started, m_threads, m_ways,
(m_hugePages == m_pages ? GREEN_BOLD_S : (m_hugePages == 0 ? RED_BOLD_S : YELLOW_BOLD_S)),
m_hugePages, m_pages,
m_hugePages == 0 ? 0.0 : static_cast<double>(m_hugePages) / m_pages * 100.0,
m_hugePages, m_pages,
memory() / 1024,
Chrono::steadyMSecs() - m_ts
);

View file

@ -23,48 +23,27 @@
*/
#include "backend/cpu/Cpu.h"
#include "backend/cpu/CpuConfig.h"
#include "backend/cpu/CpuConfig_gen.h"
#include "backend/cpu/Cpu.h"
#include "base/io/json/Json.h"
#include "rapidjson/document.h"
namespace xmrig {
static const char *kCn = "cn";
static const char *kEnabled = "enabled";
static const char *kHugePages = "huge-pages";
static const char *kHwAes = "hw-aes";
static const char *kMaxThreadsHint = "max-threads-hint";
static const char *kMemoryPool = "memory-pool";
static const char *kPriority = "priority";
#ifdef XMRIG_FEATURE_ASM
static const char *kAsm = "asm";
#endif
#ifdef XMRIG_ALGO_CN_GPU
static const char *kCnGPU = "cn/gpu";
#endif
#ifdef XMRIG_ALGO_CN_LITE
static const char *kCnLite = "cn-lite";
#endif
#ifdef XMRIG_ALGO_CN_HEAVY
static const char *kCnHeavy = "cn-heavy";
#endif
#ifdef XMRIG_ALGO_CN_PICO
static const char *kCnPico = "cn-pico";
#endif
#ifdef XMRIG_ALGO_RANDOMX
static const char *kRx = "rx";
static const char *kRxWOW = "rx/wow";
#endif
#ifdef XMRIG_ALGO_ARGON2
static const char *kArgon2 = "argon2";
static const char *kArgon2Impl = "argon2-impl";
#endif
@ -90,6 +69,7 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const
obj.AddMember(StringRef(kHugePages), m_hugePages, allocator);
obj.AddMember(StringRef(kHwAes), m_aes == AES_AUTO ? Value(kNullType) : Value(m_aes == AES_HW), allocator);
obj.AddMember(StringRef(kPriority), priority() != -1 ? Value(priority()) : Value(kNullType), allocator);
obj.AddMember(StringRef(kMemoryPool), m_memoryPool < 1 ? Value(m_memoryPool < 0) : Value(m_memoryPool), allocator);
if (m_threads.isEmpty()) {
obj.AddMember(StringRef(kMaxThreadsHint), m_limit, allocator);
@ -109,6 +89,12 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const
}
size_t xmrig::CpuConfig::memPoolSize() const
{
return m_memoryPool < 0 ? Cpu::info()->threads() : m_memoryPool;
}
std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm) const
{
std::vector<CpuLaunchData> out;
@ -128,7 +114,7 @@ std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, cons
}
void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version)
void xmrig::CpuConfig::read(const rapidjson::Value &value)
{
if (value.IsObject()) {
m_enabled = Json::getBool(value, kEnabled, m_enabled);
@ -137,6 +123,7 @@ void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version)
setAesMode(Json::getValue(value, kHwAes));
setPriority(Json::getInt(value, kPriority, -1));
setMemoryPool(Json::getValue(value, kMemoryPool));
# ifdef XMRIG_FEATURE_ASM
m_assembly = Json::getValue(value, kAsm);
@ -146,16 +133,14 @@ void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version)
m_argon2Impl = Json::getString(value, kArgon2Impl);
# endif
if (!m_threads.read(value)) {
generate();
}
m_threads.read(value);
if (version == 0) {
generateArgon2();
}
generate();
}
else if (value.IsBool() && value.IsFalse()) {
m_enabled = false;
else if (value.IsBool()) {
m_enabled = value.GetBool();
generate();
}
else {
generate();
@ -165,52 +150,40 @@ void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version)
void xmrig::CpuConfig::generate()
{
m_shouldSave = true;
ICpuInfo *cpu = Cpu::info();
if (!isEnabled() || m_threads.has("*")) {
return;
}
m_threads.disable(Algorithm::CN_0);
m_threads.move(kCn, cpu->threads(Algorithm::CN_0, m_limit));
size_t count = 0;
# ifdef XMRIG_ALGO_CN_GPU
m_threads.move(kCnGPU, cpu->threads(Algorithm::CN_GPU, m_limit));
# endif
count += xmrig::generate<Algorithm::CN>(m_threads, m_limit);
count += xmrig::generate<Algorithm::CN_LITE>(m_threads, m_limit);
count += xmrig::generate<Algorithm::CN_HEAVY>(m_threads, m_limit);
count += xmrig::generate<Algorithm::CN_PICO>(m_threads, m_limit);
count += xmrig::generate<Algorithm::RANDOM_X>(m_threads, m_limit);
count += xmrig::generate<Algorithm::ARGON2>(m_threads, m_limit);
# ifdef XMRIG_ALGO_CN_LITE
m_threads.disable(Algorithm::CN_LITE_0);
m_threads.move(kCnLite, cpu->threads(Algorithm::CN_LITE_1, m_limit));
# endif
# ifdef XMRIG_ALGO_CN_HEAVY
m_threads.move(kCnHeavy, cpu->threads(Algorithm::CN_HEAVY_0, m_limit));
# endif
# ifdef XMRIG_ALGO_CN_PICO
m_threads.move(kCnPico, cpu->threads(Algorithm::CN_PICO_0, m_limit));
# endif
# ifdef XMRIG_ALGO_RANDOMX
m_threads.move(kRx, cpu->threads(Algorithm::RX_0, m_limit));
m_threads.move(kRxWOW, cpu->threads(Algorithm::RX_WOW, m_limit));
# endif
generateArgon2();
m_shouldSave = count > 0;
}
void xmrig::CpuConfig::generateArgon2()
void xmrig::CpuConfig::setAesMode(const rapidjson::Value &value)
{
# ifdef XMRIG_ALGO_ARGON2
m_threads.move(kArgon2, Cpu::info()->threads(Algorithm::AR2_CHUKWA, m_limit));
# endif
}
void xmrig::CpuConfig::setAesMode(const rapidjson::Value &aesMode)
{
if (aesMode.IsBool()) {
m_aes = aesMode.GetBool() ? AES_HW : AES_SOFT;
if (value.IsBool()) {
m_aes = value.GetBool() ? AES_HW : AES_SOFT;
}
else {
m_aes = AES_AUTO;
}
}
void xmrig::CpuConfig::setMemoryPool(const rapidjson::Value &value)
{
if (value.IsBool()) {
m_memoryPool = value.GetBool() ? -1 : 0;
}
else if (value.IsInt()) {
m_memoryPool = value.GetInt();
}
}

View file

@ -48,8 +48,9 @@ public:
bool isHwAES() const;
rapidjson::Value toJSON(rapidjson::Document &doc) const;
size_t memPoolSize() const;
std::vector<CpuLaunchData> get(const Miner *miner, const Algorithm &algorithm) const;
void read(const rapidjson::Value &value, uint32_t version);
void read(const rapidjson::Value &value);
inline bool isEnabled() const { return m_enabled; }
inline bool isHugePages() const { return m_hugePages; }
@ -61,8 +62,8 @@ public:
private:
void generate();
void generateArgon2();
void setAesMode(const rapidjson::Value &aesMode);
void setAesMode(const rapidjson::Value &value);
void setMemoryPool(const rapidjson::Value &value);
inline void setPriority(int priority) { m_priority = (priority >= -1 && priority <= 5) ? priority : -1; }
@ -71,6 +72,7 @@ private:
bool m_enabled = true;
bool m_hugePages = true;
bool m_shouldSave = false;
int m_memoryPool = 0;
int m_priority = -1;
String m_argon2Impl;
Threads<CpuThreads> m_threads;

View file

@ -0,0 +1,149 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_CPUCONFIG_GEN_H
#define XMRIG_CPUCONFIG_GEN_H
#include "backend/common/Threads.h"
#include "backend/cpu/Cpu.h"
#include "backend/cpu/CpuThreads.h"
namespace xmrig {
static inline size_t generate(const char *key, Threads<CpuThreads> &threads, const Algorithm &algorithm, uint32_t limit)
{
if (threads.isExist(algorithm) || threads.has(key)) {
return 0;
}
return threads.move(key, Cpu::info()->threads(algorithm, limit));
}
template<Algorithm::Family FAMILY>
static inline size_t generate(Threads<CpuThreads> &, uint32_t) { return 0; }
template<>
size_t inline generate<Algorithm::CN>(Threads<CpuThreads> &threads, uint32_t limit)
{
size_t count = 0;
count += generate("cn", threads, Algorithm::CN_0, limit);
if (!threads.isExist(Algorithm::CN_0)) {
threads.disable(Algorithm::CN_0);
++count;
}
# ifdef XMRIG_ALGO_CN_GPU
count += generate("cn/gpu", threads, Algorithm::CN_GPU, limit);
# endif
return count;
}
#ifdef XMRIG_ALGO_CN_LITE
template<>
size_t inline generate<Algorithm::CN_LITE>(Threads<CpuThreads> &threads, uint32_t limit)
{
size_t count = 0;
count += generate("cn-lite", threads, Algorithm::CN_LITE_1, limit);
if (!threads.isExist(Algorithm::CN_LITE_0)) {
threads.disable(Algorithm::CN_LITE_0);
++count;
}
return count;
}
#endif
#ifdef XMRIG_ALGO_CN_HEAVY
template<>
size_t inline generate<Algorithm::CN_HEAVY>(Threads<CpuThreads> &threads, uint32_t limit)
{
return generate("cn-heavy", threads, Algorithm::CN_HEAVY_0, limit);
}
#endif
#ifdef XMRIG_ALGO_CN_PICO
template<>
size_t inline generate<Algorithm::CN_PICO>(Threads<CpuThreads> &threads, uint32_t limit)
{
return generate("cn-pico", threads, Algorithm::CN_PICO_0, limit);
}
#endif
#ifdef XMRIG_ALGO_RANDOMX
template<>
size_t inline generate<Algorithm::RANDOM_X>(Threads<CpuThreads> &threads, uint32_t limit)
{
size_t count = 0;
auto wow = Cpu::info()->threads(Algorithm::RX_WOW, limit);
if (!threads.isExist(Algorithm::RX_ARQ)) {
auto arq = Cpu::info()->threads(Algorithm::RX_ARQ, limit);
if (arq == wow) {
threads.setAlias(Algorithm::RX_ARQ, "rx/wow");
++count;
}
else {
count += threads.move("rx/arq", std::move(arq));
}
}
if (!threads.isExist(Algorithm::RX_WOW)) {
count += threads.move("rx/wow", std::move(wow));
}
count += generate("rx", threads, Algorithm::RX_0, limit);
return count;
}
#endif
#ifdef XMRIG_ALGO_ARGON2
template<>
size_t inline generate<Algorithm::ARGON2>(Threads<CpuThreads> &threads, uint32_t limit)
{
return generate("argon2", threads, Algorithm::AR2_CHUKWA, limit);
}
#endif
} /* namespace xmrig */
#endif /* XMRIG_CPUCONFIG_GEN_H */

View file

@ -120,6 +120,16 @@ xmrig::CpuThreads::CpuThreads(size_t count, uint32_t intensity)
}
bool xmrig::CpuThreads::isEqual(const CpuThreads &other) const
{
if (isEmpty() && other.isEmpty()) {
return true;
}
return count() == other.count() && std::equal(m_data.begin(), m_data.end(), other.m_data.begin());
}
rapidjson::Value xmrig::CpuThreads::toJSON(rapidjson::Document &doc) const
{
using namespace rapidjson;

View file

@ -38,7 +38,7 @@ namespace xmrig {
class CpuThreads
{
public:
inline CpuThreads() {}
inline CpuThreads() = default;
inline CpuThreads(size_t count) : m_data(count) {}
CpuThreads(const rapidjson::Value &value);
@ -51,6 +51,10 @@ public:
inline void add(int64_t affinity, uint32_t intensity) { add(CpuThread(affinity, intensity)); }
inline void reserve(size_t capacity) { m_data.reserve(capacity); }
inline bool operator!=(const CpuThreads &other) const { return !isEqual(other); }
inline bool operator==(const CpuThreads &other) const { return isEqual(other); }
bool isEqual(const CpuThreads &other) const;
rapidjson::Value toJSON(rapidjson::Document &doc) const;
private:

View file

@ -62,19 +62,19 @@ xmrig::CpuWorker<N>::CpuWorker(size_t id, const CpuLaunchData &data) :
m_miner(data.miner),
m_ctx()
{
m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages);
m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, true, m_node);
}
template<size_t N>
xmrig::CpuWorker<N>::~CpuWorker()
{
CnCtx::release(m_ctx, N);
delete m_memory;
# ifdef XMRIG_ALGO_RANDOMX
delete m_vm;
# endif
CnCtx::release(m_ctx, N);
delete m_memory;
}

View file

@ -2,6 +2,7 @@ set(HEADERS_BACKEND_CPU
src/backend/cpu/Cpu.h
src/backend/cpu/CpuBackend.h
src/backend/cpu/CpuConfig.h
src/backend/cpu/CpuConfig_gen.h
src/backend/cpu/CpuLaunchData.cpp
src/backend/cpu/CpuThread.h
src/backend/cpu/CpuThreads.h

View file

@ -46,7 +46,6 @@
namespace xmrig {
std::vector<uint32_t> HwlocCpuInfo::m_nodeIndexes;
uint32_t HwlocCpuInfo::m_features = 0;
@ -185,11 +184,11 @@ xmrig::HwlocCpuInfo::HwlocCpuInfo()
m_features |= SET_THISTHREAD_MEMBIND;
}
m_nodeIndexes.reserve(m_nodes);
m_nodeset.reserve(m_nodes);
hwloc_obj_t node = nullptr;
while ((node = hwloc_get_next_obj_by_type(m_topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) {
m_nodeIndexes.emplace_back(node->os_index);
m_nodeset.emplace_back(node->os_index);
}
}
}
@ -201,6 +200,20 @@ xmrig::HwlocCpuInfo::~HwlocCpuInfo()
}
bool xmrig::HwlocCpuInfo::membind(hwloc_const_bitmap_t nodeset)
{
if (!hwloc_topology_get_support(m_topology)->membind->set_thisthread_membind) {
return false;
}
# if HWLOC_API_VERSION >= 0x20000
return hwloc_set_membind(m_topology, nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET) >= 0;
# else
return hwloc_set_membind_nodeset(m_topology, nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD) >= 0;
# endif
}
xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const
{
if (L2() == 0 && L3() == 0) {

View file

@ -30,8 +30,9 @@
#include "base/tools/Object.h"
using hwloc_obj_t = struct hwloc_obj *;
using hwloc_topology_t = struct hwloc_topology *;
using hwloc_const_bitmap_t = const struct hwloc_bitmap_s *;
using hwloc_obj_t = struct hwloc_obj *;
using hwloc_topology_t = struct hwloc_topology *;
namespace xmrig {
@ -52,7 +53,11 @@ public:
~HwlocCpuInfo() override;
static inline bool has(Feature feature) { return m_features & feature; }
static inline const std::vector<uint32_t> &nodeIndexes() { return m_nodeIndexes; }
inline const std::vector<uint32_t> &nodeset() const { return m_nodeset; }
inline hwloc_topology_t topology() const { return m_topology; }
bool membind(hwloc_const_bitmap_t nodeset);
protected:
CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const override;
@ -67,7 +72,7 @@ protected:
private:
void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const;
static std::vector<uint32_t> m_nodeIndexes;
static uint32_t m_features;
char m_backend[20] = { 0 };
@ -76,6 +81,7 @@ private:
size_t m_cores = 0;
size_t m_nodes = 0;
size_t m_packages = 0;
std::vector<uint32_t> m_nodeset;
};

View file

@ -24,21 +24,18 @@
#include "backend/opencl/OclConfig.h"
#include "backend/opencl/OclConfig_gen.h"
#include "backend/opencl/wrappers/OclLib.h"
#include "base/io/json/Json.h"
#include "base/io/log/Log.h"
#include "rapidjson/document.h"
#include <algorithm>
namespace xmrig {
static const char *kAMD = "AMD";
static const char *kCache = "cache";
static const char *kCn = "cn";
static const char *kCn2 = "cn/2";
static const char *kDevicesHint = "devices-hint";
static const char *kEnabled = "enabled";
static const char *kINTEL = "INTEL";
@ -47,69 +44,9 @@ static const char *kNVIDIA = "NVIDIA";
static const char *kPlatform = "platform";
#ifdef XMRIG_ALGO_CN_GPU
static const char *kCnGPU = "cn/gpu";
#endif
#ifdef XMRIG_ALGO_CN_LITE
static const char *kCnLite = "cn-lite";
#endif
#ifdef XMRIG_ALGO_CN_HEAVY
static const char *kCnHeavy = "cn-heavy";
#endif
#ifdef XMRIG_ALGO_CN_PICO
static const char *kCnPico = "cn-pico";
#endif
#ifdef XMRIG_ALGO_RANDOMX
static const char *kRx = "rx";
static const char *kRxWOW = "rx/wow";
#endif
#ifdef XMRIG_ALGO_ARGON2
//static const char *kArgon2 = "argon2";
#endif
extern template class Threads<OclThreads>;
static size_t generate(const char *key, Threads<OclThreads> &threads, const Algorithm &algorithm, const std::vector<OclDevice> &devices)
{
if (threads.has(key) || threads.isExist(algorithm)) {
return 0;
}
OclThreads profile;
for (const OclDevice &device : devices) {
device.generate(algorithm, profile);
}
const size_t count = profile.count();
threads.move(key, std::move(profile));
return count;
}
static inline std::vector<OclDevice> filterDevices(const std::vector<OclDevice> &devices, const std::vector<uint32_t> &hints)
{
std::vector<OclDevice> out;
out.reserve(std::min(devices.size(), hints.size()));
for (const auto &device : devices) {
auto it = std::find(hints.begin(), hints.end(), device.index());
if (it != hints.end()) {
out.emplace_back(device);
}
}
return out;
}
}
@ -251,39 +188,11 @@ void xmrig::OclConfig::generate()
size_t count = 0;
count += xmrig::generate(kCn, m_threads, Algorithm::CN_0, devices);
count += xmrig::generate(kCn2, m_threads, Algorithm::CN_2, devices);
if (!m_threads.isExist(Algorithm::CN_0)) {
m_threads.disable(Algorithm::CN_0);
count++;
}
# ifdef XMRIG_ALGO_CN_GPU
count += xmrig::generate(kCnGPU, m_threads, Algorithm::CN_GPU, devices);
# endif
# ifdef XMRIG_ALGO_CN_LITE
count += xmrig::generate(kCnLite, m_threads, Algorithm::CN_LITE_1, devices);
if (!m_threads.isExist(Algorithm::CN_LITE_0)) {
m_threads.disable(Algorithm::CN_LITE_0);
count++;
}
# endif
# ifdef XMRIG_ALGO_CN_HEAVY
count += xmrig::generate(kCnHeavy, m_threads, Algorithm::CN_HEAVY_0, devices);
# endif
# ifdef XMRIG_ALGO_CN_PICO
count += xmrig::generate(kCnPico, m_threads, Algorithm::CN_PICO_0, devices);
# endif
# ifdef XMRIG_ALGO_RANDOMX
count += xmrig::generate(kRx, m_threads, Algorithm::RX_0, devices);
count += xmrig::generate(kRxWOW, m_threads, Algorithm::RX_WOW, devices);
# endif
count += xmrig::generate<Algorithm::CN>(m_threads, devices);
count += xmrig::generate<Algorithm::CN_LITE>(m_threads, devices);
count += xmrig::generate<Algorithm::CN_HEAVY>(m_threads, devices);
count += xmrig::generate<Algorithm::CN_PICO>(m_threads, devices);
count += xmrig::generate<Algorithm::RANDOM_X>(m_threads, devices);
m_shouldSave = count > 0;
}

View file

@ -0,0 +1,152 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_OCLCONFIG_GEN_H
#define XMRIG_OCLCONFIG_GEN_H
#include "backend/common/Threads.h"
#include "backend/opencl/OclThreads.h"
#include <algorithm>
namespace xmrig {
static inline size_t generate(const char *key, Threads<OclThreads> &threads, const Algorithm &algorithm, const std::vector<OclDevice> &devices)
{
if (threads.isExist(algorithm) || threads.has(key)) {
return 0;
}
return threads.move(key, OclThreads(devices, algorithm));
}
template<Algorithm::Family FAMILY>
static inline size_t generate(Threads<OclThreads> &, const std::vector<OclDevice> &) { return 0; }
template<>
size_t inline generate<Algorithm::CN>(Threads<OclThreads> &threads, const std::vector<OclDevice> &devices)
{
size_t count = 0;
count += generate("cn", threads, Algorithm::CN_0, devices);
count += generate("cn/2", threads, Algorithm::CN_2, devices);
if (!threads.isExist(Algorithm::CN_0)) {
threads.disable(Algorithm::CN_0);
count++;
}
# ifdef XMRIG_ALGO_CN_GPU
count += generate("cn/gpu", threads, Algorithm::CN_GPU, devices);
# endif
return count;
}
#ifdef XMRIG_ALGO_CN_LITE
template<>
size_t inline generate<Algorithm::CN_LITE>(Threads<OclThreads> &threads, const std::vector<OclDevice> &devices)
{
size_t count = generate("cn-lite", threads, Algorithm::CN_LITE_1, devices);
if (!threads.isExist(Algorithm::CN_LITE_0)) {
threads.disable(Algorithm::CN_LITE_0);
++count;
}
return count;
}
#endif
#ifdef XMRIG_ALGO_CN_HEAVY
template<>
size_t inline generate<Algorithm::CN_HEAVY>(Threads<OclThreads> &threads, const std::vector<OclDevice> &devices)
{
return generate("cn-heavy", threads, Algorithm::CN_HEAVY_0, devices);
}
#endif
#ifdef XMRIG_ALGO_CN_PICO
template<>
size_t inline generate<Algorithm::CN_PICO>(Threads<OclThreads> &threads, const std::vector<OclDevice> &devices)
{
return generate("cn-pico", threads, Algorithm::CN_PICO_0, devices);
}
#endif
#ifdef XMRIG_ALGO_RANDOMX
template<>
size_t inline generate<Algorithm::RANDOM_X>(Threads<OclThreads> &threads, const std::vector<OclDevice> &devices)
{
size_t count = 0;
auto rx = OclThreads(devices, Algorithm::RX_0);
auto wow = OclThreads(devices, Algorithm::RX_WOW);
auto arq = OclThreads(devices, Algorithm::RX_ARQ);
if (!threads.isExist(Algorithm::RX_WOW) && wow != rx) {
count += threads.move("rx/wow", std::move(wow));
}
if (!threads.isExist(Algorithm::RX_ARQ) && arq != rx) {
count += threads.move("rx/arq", std::move(arq));
}
count += threads.move("rx", std::move(rx));
return count;
}
#endif
static inline std::vector<OclDevice> filterDevices(const std::vector<OclDevice> &devices, const std::vector<uint32_t> &hints)
{
std::vector<OclDevice> out;
out.reserve(std::min(devices.size(), hints.size()));
for (const auto &device : devices) {
auto it = std::find(hints.begin(), hints.end(), device.index());
if (it != hints.end()) {
out.emplace_back(device);
}
}
return out;
}
} /* namespace xmrig */
#endif /* XMRIG_OCLCONFIG_GEN_H */

View file

@ -44,6 +44,24 @@ xmrig::OclThreads::OclThreads(const rapidjson::Value &value)
}
xmrig::OclThreads::OclThreads(const std::vector<OclDevice> &devices, const Algorithm &algorithm)
{
for (const OclDevice &device : devices) {
device.generate(algorithm, *this);
}
}
bool xmrig::OclThreads::isEqual(const OclThreads &other) const
{
if (isEmpty() && other.isEmpty()) {
return true;
}
return count() == other.count() && std::equal(m_data.begin(), m_data.end(), other.m_data.begin());
}
rapidjson::Value xmrig::OclThreads::toJSON(rapidjson::Document &doc) const
{
using namespace rapidjson;

View file

@ -30,6 +30,7 @@
#include "backend/opencl/OclThread.h"
#include "backend/opencl/wrappers/OclDevice.h"
namespace xmrig {
@ -40,6 +41,7 @@ class OclThreads
public:
OclThreads() = default;
OclThreads(const rapidjson::Value &value);
OclThreads(const std::vector<OclDevice> &devices, const Algorithm &algorithm);
inline bool isEmpty() const { return m_data.empty(); }
inline const std::vector<OclThread> &data() const { return m_data; }
@ -47,6 +49,10 @@ public:
inline void add(OclThread &&thread) { m_data.push_back(thread); }
inline void reserve(size_t capacity) { m_data.reserve(capacity); }
inline bool operator!=(const OclThreads &other) const { return !isEqual(other); }
inline bool operator==(const OclThreads &other) const { return isEqual(other); }
bool isEqual(const OclThreads &other) const;
rapidjson::Value toJSON(rapidjson::Document &doc) const;
private:

View file

@ -19,8 +19,9 @@
#define ALGO_RX_0 18
#define ALGO_RX_WOW 19
#define ALGO_RX_LOKI 20
#define ALGO_AR2_CHUKWA 21
#define ALGO_AR2_WRKZ 22
#define ALGO_RX_ARQMA 21
#define ALGO_AR2_CHUKWA 22
#define ALGO_AR2_WRKZ 23
#define FAMILY_UNKNOWN 0
#define FAMILY_CN 1

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,358 +2,379 @@
namespace xmrig {
static char cryptonight_r_defines_cl[7709] = {
0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,
static char cryptonight_r_defines_cl[8353] = {
0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,
0x20,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x4e,0x29,0x20,0x28,0x2a,0x28,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,
0x75,0x69,0x6e,0x74,0x34,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x73,0x63,0x72,0x61,0x74,0x63,0x68,
0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x29,0x20,0x2b,0x20,0x28,0x69,0x64,0x78,0x31,0x20,0x5e,0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x29,0x29,0x0a,
0x23,0x65,0x6c,0x73,0x65,0x0a,0x23,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x30,0x29,0x0a,0x23,
0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x29,0x20,0x2b,0x20,0x28,0x69,0x64,0x78,0x31,0x20,0x5e,0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x29,0x29,0x0d,
0x23,0x65,0x6c,0x73,0x65,0x0d,0x23,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x30,0x29,0x0d,0x23,
0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x4e,0x29,0x20,0x28,0x2a,0x28,0x5f,0x5f,
0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x34,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,
0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x20,0x2b,0x20,0x28,0x69,0x64,0x78,0x20,0x5e,0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x29,
0x29,0x0a,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x31,0x29,0x0a,0x23,0x64,0x65,
0x29,0x0d,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x31,0x29,0x0d,0x23,0x64,0x65,
0x66,0x69,0x6e,0x65,0x20,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x4e,0x29,0x20,0x28,0x2a,0x28,0x5f,0x5f,0x67,0x6c,
0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x34,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,
0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x28,0x69,0x64,0x78,0x20,0x5e,
0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x2c,0x20,0x54,0x68,0x72,0x65,0x61,0x64,0x73,0x29,0x29,0x29,0x0a,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,0x54,
0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x32,0x29,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x53,0x43,0x52,0x41,0x54,0x43,
0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x2c,0x20,0x54,0x68,0x72,0x65,0x61,0x64,0x73,0x29,0x29,0x29,0x0d,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,0x54,
0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x32,0x29,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x53,0x43,0x52,0x41,0x54,0x43,
0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x4e,0x29,0x20,0x28,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x34,0x2a,
0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x20,
0x2b,0x20,0x28,0x28,0x28,0x69,0x64,0x78,0x20,0x5e,0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x20,0x25,0x20,0x28,0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,
0x4b,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x69,0x64,0x78,0x20,0x5e,0x20,0x28,0x4e,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x20,0x2f,0x20,0x28,
0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x20,0x2a,0x20,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x20,0x2a,0x20,0x28,0x4d,
0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x29,0x29,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,
0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x52,0x4f,0x54,0x5f,0x42,0x49,0x54,0x53,0x20,0x33,0x32,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x4d,0x45,0x4d,0x5f,
0x43,0x48,0x55,0x4e,0x4b,0x20,0x28,0x31,0x20,0x3c,0x3c,0x20,0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x5f,0x45,0x58,0x50,0x4f,0x4e,0x45,0x4e,0x54,0x29,0x0a,
0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x57,0x4f,0x4c,0x46,0x5f,0x41,0x45,0x53,0x5f,0x43,0x4c,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x57,0x4f,0x4c,0x46,
0x5f,0x41,0x45,0x53,0x5f,0x43,0x4c,0x0a,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x63,0x6c,0x5f,0x61,0x6d,0x64,0x5f,0x6d,0x65,0x64,0x69,0x61,0x5f,0x6f,0x70,0x73,0x32,
0x0a,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x61,0x6d,0x64,
0x5f,0x6d,0x65,0x64,0x69,0x61,0x5f,0x6f,0x70,0x73,0x32,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x78,0x6d,0x72,
0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x20,0x3c,0x3c,0x20,0x34,0x29,0x29,0x29,0x29,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,
0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x52,0x4f,0x54,0x5f,0x42,0x49,0x54,0x53,0x20,0x33,0x32,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x4d,0x45,0x4d,0x5f,
0x43,0x48,0x55,0x4e,0x4b,0x20,0x28,0x31,0x20,0x3c,0x3c,0x20,0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x5f,0x45,0x58,0x50,0x4f,0x4e,0x45,0x4e,0x54,0x29,0x0d,
0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x57,0x4f,0x4c,0x46,0x5f,0x41,0x45,0x53,0x5f,0x43,0x4c,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x57,0x4f,0x4c,0x46,
0x5f,0x41,0x45,0x53,0x5f,0x43,0x4c,0x0d,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x63,0x6c,0x5f,0x61,0x6d,0x64,0x5f,0x6d,0x65,0x64,0x69,0x61,0x5f,0x6f,0x70,0x73,0x32,
0x0d,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x61,0x6d,0x64,
0x5f,0x6d,0x65,0x64,0x69,0x61,0x5f,0x6f,0x70,0x73,0x32,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x78,0x6d,0x72,
0x69,0x67,0x5f,0x61,0x6d,0x64,0x5f,0x62,0x66,0x65,0x28,0x73,0x72,0x63,0x30,0x2c,0x20,0x73,0x72,0x63,0x31,0x2c,0x20,0x73,0x72,0x63,0x32,0x29,0x20,0x61,0x6d,0x64,
0x5f,0x62,0x66,0x65,0x28,0x73,0x72,0x63,0x30,0x2c,0x20,0x73,0x72,0x63,0x31,0x2c,0x20,0x73,0x72,0x63,0x32,0x29,0x0a,0x23,0x65,0x6c,0x73,0x65,0x0a,0x69,0x6e,0x6c,
0x5f,0x62,0x66,0x65,0x28,0x73,0x72,0x63,0x30,0x2c,0x20,0x73,0x72,0x63,0x31,0x2c,0x20,0x73,0x72,0x63,0x32,0x29,0x0d,0x23,0x65,0x6c,0x73,0x65,0x0d,0x69,0x6e,0x6c,
0x69,0x6e,0x65,0x20,0x69,0x6e,0x74,0x20,0x78,0x6d,0x72,0x69,0x67,0x5f,0x61,0x6d,0x64,0x5f,0x62,0x66,0x65,0x28,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,
0x20,0x73,0x72,0x63,0x30,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,
0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x0a,0x7b,0x0a,0x69,0x66,0x28,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x77,0x69,0x64,0x74,0x68,0x29,0x3c,0x33,0x32,
0x75,0x29,0x20,0x7b,0x0a,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x73,0x72,0x63,0x30,0x3c,0x3c,0x28,0x33,0x32,0x75,0x2d,0x6f,0x66,0x66,0x73,0x65,0x74,0x2d,0x77,
0x69,0x64,0x74,0x68,0x29,0x29,0x3e,0x3e,0x28,0x33,0x32,0x75,0x2d,0x77,0x69,0x64,0x74,0x68,0x29,0x3b,0x0a,0x7d,0x0a,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x72,
0x63,0x30,0x3e,0x3e,0x6f,0x66,0x66,0x73,0x65,0x74,0x3b,0x0a,0x7d,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x41,0x45,0x53,0x30,0x5f,0x43,0x5b,0x32,0x35,0x36,0x5d,0x20,0x3d,0x0a,
0x7b,0x0a,0x30,0x78,0x41,0x35,0x36,0x33,0x36,0x33,0x43,0x36,0x55,0x2c,0x30,0x78,0x38,0x34,0x37,0x43,0x37,0x43,0x46,0x38,0x55,0x2c,0x30,0x78,0x39,0x39,0x37,0x37,
0x37,0x37,0x45,0x45,0x55,0x2c,0x30,0x78,0x38,0x44,0x37,0x42,0x37,0x42,0x46,0x36,0x55,0x2c,0x0a,0x30,0x78,0x30,0x44,0x46,0x32,0x46,0x32,0x46,0x46,0x55,0x2c,0x30,
0x78,0x42,0x44,0x36,0x42,0x36,0x42,0x44,0x36,0x55,0x2c,0x30,0x78,0x42,0x31,0x36,0x46,0x36,0x46,0x44,0x45,0x55,0x2c,0x30,0x78,0x35,0x34,0x43,0x35,0x43,0x35,0x39,
0x31,0x55,0x2c,0x0a,0x30,0x78,0x35,0x30,0x33,0x30,0x33,0x30,0x36,0x30,0x55,0x2c,0x30,0x78,0x30,0x33,0x30,0x31,0x30,0x31,0x30,0x32,0x55,0x2c,0x30,0x78,0x41,0x39,
0x36,0x37,0x36,0x37,0x43,0x45,0x55,0x2c,0x30,0x78,0x37,0x44,0x32,0x42,0x32,0x42,0x35,0x36,0x55,0x2c,0x0a,0x30,0x78,0x31,0x39,0x46,0x45,0x46,0x45,0x45,0x37,0x55,
0x2c,0x30,0x78,0x36,0x32,0x44,0x37,0x44,0x37,0x42,0x35,0x55,0x2c,0x30,0x78,0x45,0x36,0x41,0x42,0x41,0x42,0x34,0x44,0x55,0x2c,0x30,0x78,0x39,0x41,0x37,0x36,0x37,
0x36,0x45,0x43,0x55,0x2c,0x0a,0x30,0x78,0x34,0x35,0x43,0x41,0x43,0x41,0x38,0x46,0x55,0x2c,0x30,0x78,0x39,0x44,0x38,0x32,0x38,0x32,0x31,0x46,0x55,0x2c,0x30,0x78,
0x34,0x30,0x43,0x39,0x43,0x39,0x38,0x39,0x55,0x2c,0x30,0x78,0x38,0x37,0x37,0x44,0x37,0x44,0x46,0x41,0x55,0x2c,0x0a,0x30,0x78,0x31,0x35,0x46,0x41,0x46,0x41,0x45,
0x46,0x55,0x2c,0x30,0x78,0x45,0x42,0x35,0x39,0x35,0x39,0x42,0x32,0x55,0x2c,0x30,0x78,0x43,0x39,0x34,0x37,0x34,0x37,0x38,0x45,0x55,0x2c,0x30,0x78,0x30,0x42,0x46,
0x30,0x46,0x30,0x46,0x42,0x55,0x2c,0x0a,0x30,0x78,0x45,0x43,0x41,0x44,0x41,0x44,0x34,0x31,0x55,0x2c,0x30,0x78,0x36,0x37,0x44,0x34,0x44,0x34,0x42,0x33,0x55,0x2c,
0x30,0x78,0x46,0x44,0x41,0x32,0x41,0x32,0x35,0x46,0x55,0x2c,0x30,0x78,0x45,0x41,0x41,0x46,0x41,0x46,0x34,0x35,0x55,0x2c,0x0a,0x30,0x78,0x42,0x46,0x39,0x43,0x39,
0x43,0x32,0x33,0x55,0x2c,0x30,0x78,0x46,0x37,0x41,0x34,0x41,0x34,0x35,0x33,0x55,0x2c,0x30,0x78,0x39,0x36,0x37,0x32,0x37,0x32,0x45,0x34,0x55,0x2c,0x30,0x78,0x35,
0x42,0x43,0x30,0x43,0x30,0x39,0x42,0x55,0x2c,0x0a,0x30,0x78,0x43,0x32,0x42,0x37,0x42,0x37,0x37,0x35,0x55,0x2c,0x30,0x78,0x31,0x43,0x46,0x44,0x46,0x44,0x45,0x31,
0x55,0x2c,0x30,0x78,0x41,0x45,0x39,0x33,0x39,0x33,0x33,0x44,0x55,0x2c,0x30,0x78,0x36,0x41,0x32,0x36,0x32,0x36,0x34,0x43,0x55,0x2c,0x0a,0x30,0x78,0x35,0x41,0x33,
0x36,0x33,0x36,0x36,0x43,0x55,0x2c,0x30,0x78,0x34,0x31,0x33,0x46,0x33,0x46,0x37,0x45,0x55,0x2c,0x30,0x78,0x30,0x32,0x46,0x37,0x46,0x37,0x46,0x35,0x55,0x2c,0x30,
0x78,0x34,0x46,0x43,0x43,0x43,0x43,0x38,0x33,0x55,0x2c,0x0a,0x30,0x78,0x35,0x43,0x33,0x34,0x33,0x34,0x36,0x38,0x55,0x2c,0x30,0x78,0x46,0x34,0x41,0x35,0x41,0x35,
0x35,0x31,0x55,0x2c,0x30,0x78,0x33,0x34,0x45,0x35,0x45,0x35,0x44,0x31,0x55,0x2c,0x30,0x78,0x30,0x38,0x46,0x31,0x46,0x31,0x46,0x39,0x55,0x2c,0x0a,0x30,0x78,0x39,
0x33,0x37,0x31,0x37,0x31,0x45,0x32,0x55,0x2c,0x30,0x78,0x37,0x33,0x44,0x38,0x44,0x38,0x41,0x42,0x55,0x2c,0x30,0x78,0x35,0x33,0x33,0x31,0x33,0x31,0x36,0x32,0x55,
0x2c,0x30,0x78,0x33,0x46,0x31,0x35,0x31,0x35,0x32,0x41,0x55,0x2c,0x0a,0x30,0x78,0x30,0x43,0x30,0x34,0x30,0x34,0x30,0x38,0x55,0x2c,0x30,0x78,0x35,0x32,0x43,0x37,
0x43,0x37,0x39,0x35,0x55,0x2c,0x30,0x78,0x36,0x35,0x32,0x33,0x32,0x33,0x34,0x36,0x55,0x2c,0x30,0x78,0x35,0x45,0x43,0x33,0x43,0x33,0x39,0x44,0x55,0x2c,0x0a,0x30,
0x78,0x32,0x38,0x31,0x38,0x31,0x38,0x33,0x30,0x55,0x2c,0x30,0x78,0x41,0x31,0x39,0x36,0x39,0x36,0x33,0x37,0x55,0x2c,0x30,0x78,0x30,0x46,0x30,0x35,0x30,0x35,0x30,
0x41,0x55,0x2c,0x30,0x78,0x42,0x35,0x39,0x41,0x39,0x41,0x32,0x46,0x55,0x2c,0x0a,0x30,0x78,0x30,0x39,0x30,0x37,0x30,0x37,0x30,0x45,0x55,0x2c,0x30,0x78,0x33,0x36,
0x31,0x32,0x31,0x32,0x32,0x34,0x55,0x2c,0x30,0x78,0x39,0x42,0x38,0x30,0x38,0x30,0x31,0x42,0x55,0x2c,0x30,0x78,0x33,0x44,0x45,0x32,0x45,0x32,0x44,0x46,0x55,0x2c,
0x0a,0x30,0x78,0x32,0x36,0x45,0x42,0x45,0x42,0x43,0x44,0x55,0x2c,0x30,0x78,0x36,0x39,0x32,0x37,0x32,0x37,0x34,0x45,0x55,0x2c,0x30,0x78,0x43,0x44,0x42,0x32,0x42,
0x32,0x37,0x46,0x55,0x2c,0x30,0x78,0x39,0x46,0x37,0x35,0x37,0x35,0x45,0x41,0x55,0x2c,0x0a,0x30,0x78,0x31,0x42,0x30,0x39,0x30,0x39,0x31,0x32,0x55,0x2c,0x30,0x78,
0x39,0x45,0x38,0x33,0x38,0x33,0x31,0x44,0x55,0x2c,0x30,0x78,0x37,0x34,0x32,0x43,0x32,0x43,0x35,0x38,0x55,0x2c,0x30,0x78,0x32,0x45,0x31,0x41,0x31,0x41,0x33,0x34,
0x55,0x2c,0x0a,0x30,0x78,0x32,0x44,0x31,0x42,0x31,0x42,0x33,0x36,0x55,0x2c,0x30,0x78,0x42,0x32,0x36,0x45,0x36,0x45,0x44,0x43,0x55,0x2c,0x30,0x78,0x45,0x45,0x35,
0x41,0x35,0x41,0x42,0x34,0x55,0x2c,0x30,0x78,0x46,0x42,0x41,0x30,0x41,0x30,0x35,0x42,0x55,0x2c,0x0a,0x30,0x78,0x46,0x36,0x35,0x32,0x35,0x32,0x41,0x34,0x55,0x2c,
0x30,0x78,0x34,0x44,0x33,0x42,0x33,0x42,0x37,0x36,0x55,0x2c,0x30,0x78,0x36,0x31,0x44,0x36,0x44,0x36,0x42,0x37,0x55,0x2c,0x30,0x78,0x43,0x45,0x42,0x33,0x42,0x33,
0x37,0x44,0x55,0x2c,0x0a,0x30,0x78,0x37,0x42,0x32,0x39,0x32,0x39,0x35,0x32,0x55,0x2c,0x30,0x78,0x33,0x45,0x45,0x33,0x45,0x33,0x44,0x44,0x55,0x2c,0x30,0x78,0x37,
0x31,0x32,0x46,0x32,0x46,0x35,0x45,0x55,0x2c,0x30,0x78,0x39,0x37,0x38,0x34,0x38,0x34,0x31,0x33,0x55,0x2c,0x0a,0x30,0x78,0x46,0x35,0x35,0x33,0x35,0x33,0x41,0x36,
0x55,0x2c,0x30,0x78,0x36,0x38,0x44,0x31,0x44,0x31,0x42,0x39,0x55,0x2c,0x30,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x55,0x2c,0x30,0x78,0x32,0x43,0x45,0x44,
0x45,0x44,0x43,0x31,0x55,0x2c,0x0a,0x30,0x78,0x36,0x30,0x32,0x30,0x32,0x30,0x34,0x30,0x55,0x2c,0x30,0x78,0x31,0x46,0x46,0x43,0x46,0x43,0x45,0x33,0x55,0x2c,0x30,
0x78,0x43,0x38,0x42,0x31,0x42,0x31,0x37,0x39,0x55,0x2c,0x30,0x78,0x45,0x44,0x35,0x42,0x35,0x42,0x42,0x36,0x55,0x2c,0x0a,0x30,0x78,0x42,0x45,0x36,0x41,0x36,0x41,
0x44,0x34,0x55,0x2c,0x30,0x78,0x34,0x36,0x43,0x42,0x43,0x42,0x38,0x44,0x55,0x2c,0x30,0x78,0x44,0x39,0x42,0x45,0x42,0x45,0x36,0x37,0x55,0x2c,0x30,0x78,0x34,0x42,
0x33,0x39,0x33,0x39,0x37,0x32,0x55,0x2c,0x0a,0x30,0x78,0x44,0x45,0x34,0x41,0x34,0x41,0x39,0x34,0x55,0x2c,0x30,0x78,0x44,0x34,0x34,0x43,0x34,0x43,0x39,0x38,0x55,
0x2c,0x30,0x78,0x45,0x38,0x35,0x38,0x35,0x38,0x42,0x30,0x55,0x2c,0x30,0x78,0x34,0x41,0x43,0x46,0x43,0x46,0x38,0x35,0x55,0x2c,0x0a,0x30,0x78,0x36,0x42,0x44,0x30,
0x44,0x30,0x42,0x42,0x55,0x2c,0x30,0x78,0x32,0x41,0x45,0x46,0x45,0x46,0x43,0x35,0x55,0x2c,0x30,0x78,0x45,0x35,0x41,0x41,0x41,0x41,0x34,0x46,0x55,0x2c,0x30,0x78,
0x31,0x36,0x46,0x42,0x46,0x42,0x45,0x44,0x55,0x2c,0x0a,0x30,0x78,0x43,0x35,0x34,0x33,0x34,0x33,0x38,0x36,0x55,0x2c,0x30,0x78,0x44,0x37,0x34,0x44,0x34,0x44,0x39,
0x41,0x55,0x2c,0x30,0x78,0x35,0x35,0x33,0x33,0x33,0x33,0x36,0x36,0x55,0x2c,0x30,0x78,0x39,0x34,0x38,0x35,0x38,0x35,0x31,0x31,0x55,0x2c,0x0a,0x30,0x78,0x43,0x46,
0x34,0x35,0x34,0x35,0x38,0x41,0x55,0x2c,0x30,0x78,0x31,0x30,0x46,0x39,0x46,0x39,0x45,0x39,0x55,0x2c,0x30,0x78,0x30,0x36,0x30,0x32,0x30,0x32,0x30,0x34,0x55,0x2c,
0x30,0x78,0x38,0x31,0x37,0x46,0x37,0x46,0x46,0x45,0x55,0x2c,0x0a,0x30,0x78,0x46,0x30,0x35,0x30,0x35,0x30,0x41,0x30,0x55,0x2c,0x30,0x78,0x34,0x34,0x33,0x43,0x33,
0x43,0x37,0x38,0x55,0x2c,0x30,0x78,0x42,0x41,0x39,0x46,0x39,0x46,0x32,0x35,0x55,0x2c,0x30,0x78,0x45,0x33,0x41,0x38,0x41,0x38,0x34,0x42,0x55,0x2c,0x0a,0x30,0x78,
0x46,0x33,0x35,0x31,0x35,0x31,0x41,0x32,0x55,0x2c,0x30,0x78,0x46,0x45,0x41,0x33,0x41,0x33,0x35,0x44,0x55,0x2c,0x30,0x78,0x43,0x30,0x34,0x30,0x34,0x30,0x38,0x30,
0x55,0x2c,0x30,0x78,0x38,0x41,0x38,0x46,0x38,0x46,0x30,0x35,0x55,0x2c,0x0a,0x30,0x78,0x41,0x44,0x39,0x32,0x39,0x32,0x33,0x46,0x55,0x2c,0x30,0x78,0x42,0x43,0x39,
0x44,0x39,0x44,0x32,0x31,0x55,0x2c,0x30,0x78,0x34,0x38,0x33,0x38,0x33,0x38,0x37,0x30,0x55,0x2c,0x30,0x78,0x30,0x34,0x46,0x35,0x46,0x35,0x46,0x31,0x55,0x2c,0x0a,
0x30,0x78,0x44,0x46,0x42,0x43,0x42,0x43,0x36,0x33,0x55,0x2c,0x30,0x78,0x43,0x31,0x42,0x36,0x42,0x36,0x37,0x37,0x55,0x2c,0x30,0x78,0x37,0x35,0x44,0x41,0x44,0x41,
0x41,0x46,0x55,0x2c,0x30,0x78,0x36,0x33,0x32,0x31,0x32,0x31,0x34,0x32,0x55,0x2c,0x0a,0x30,0x78,0x33,0x30,0x31,0x30,0x31,0x30,0x32,0x30,0x55,0x2c,0x30,0x78,0x31,
0x41,0x46,0x46,0x46,0x46,0x45,0x35,0x55,0x2c,0x30,0x78,0x30,0x45,0x46,0x33,0x46,0x33,0x46,0x44,0x55,0x2c,0x30,0x78,0x36,0x44,0x44,0x32,0x44,0x32,0x42,0x46,0x55,
0x2c,0x0a,0x30,0x78,0x34,0x43,0x43,0x44,0x43,0x44,0x38,0x31,0x55,0x2c,0x30,0x78,0x31,0x34,0x30,0x43,0x30,0x43,0x31,0x38,0x55,0x2c,0x30,0x78,0x33,0x35,0x31,0x33,
0x31,0x33,0x32,0x36,0x55,0x2c,0x30,0x78,0x32,0x46,0x45,0x43,0x45,0x43,0x43,0x33,0x55,0x2c,0x0a,0x30,0x78,0x45,0x31,0x35,0x46,0x35,0x46,0x42,0x45,0x55,0x2c,0x30,
0x78,0x41,0x32,0x39,0x37,0x39,0x37,0x33,0x35,0x55,0x2c,0x30,0x78,0x43,0x43,0x34,0x34,0x34,0x34,0x38,0x38,0x55,0x2c,0x30,0x78,0x33,0x39,0x31,0x37,0x31,0x37,0x32,
0x45,0x55,0x2c,0x0a,0x30,0x78,0x35,0x37,0x43,0x34,0x43,0x34,0x39,0x33,0x55,0x2c,0x30,0x78,0x46,0x32,0x41,0x37,0x41,0x37,0x35,0x35,0x55,0x2c,0x30,0x78,0x38,0x32,
0x37,0x45,0x37,0x45,0x46,0x43,0x55,0x2c,0x30,0x78,0x34,0x37,0x33,0x44,0x33,0x44,0x37,0x41,0x55,0x2c,0x0a,0x30,0x78,0x41,0x43,0x36,0x34,0x36,0x34,0x43,0x38,0x55,
0x2c,0x30,0x78,0x45,0x37,0x35,0x44,0x35,0x44,0x42,0x41,0x55,0x2c,0x30,0x78,0x32,0x42,0x31,0x39,0x31,0x39,0x33,0x32,0x55,0x2c,0x30,0x78,0x39,0x35,0x37,0x33,0x37,
0x33,0x45,0x36,0x55,0x2c,0x0a,0x30,0x78,0x41,0x30,0x36,0x30,0x36,0x30,0x43,0x30,0x55,0x2c,0x30,0x78,0x39,0x38,0x38,0x31,0x38,0x31,0x31,0x39,0x55,0x2c,0x30,0x78,
0x44,0x31,0x34,0x46,0x34,0x46,0x39,0x45,0x55,0x2c,0x30,0x78,0x37,0x46,0x44,0x43,0x44,0x43,0x41,0x33,0x55,0x2c,0x0a,0x30,0x78,0x36,0x36,0x32,0x32,0x32,0x32,0x34,
0x34,0x55,0x2c,0x30,0x78,0x37,0x45,0x32,0x41,0x32,0x41,0x35,0x34,0x55,0x2c,0x30,0x78,0x41,0x42,0x39,0x30,0x39,0x30,0x33,0x42,0x55,0x2c,0x30,0x78,0x38,0x33,0x38,
0x38,0x38,0x38,0x30,0x42,0x55,0x2c,0x0a,0x30,0x78,0x43,0x41,0x34,0x36,0x34,0x36,0x38,0x43,0x55,0x2c,0x30,0x78,0x32,0x39,0x45,0x45,0x45,0x45,0x43,0x37,0x55,0x2c,
0x30,0x78,0x44,0x33,0x42,0x38,0x42,0x38,0x36,0x42,0x55,0x2c,0x30,0x78,0x33,0x43,0x31,0x34,0x31,0x34,0x32,0x38,0x55,0x2c,0x0a,0x30,0x78,0x37,0x39,0x44,0x45,0x44,
0x45,0x41,0x37,0x55,0x2c,0x30,0x78,0x45,0x32,0x35,0x45,0x35,0x45,0x42,0x43,0x55,0x2c,0x30,0x78,0x31,0x44,0x30,0x42,0x30,0x42,0x31,0x36,0x55,0x2c,0x30,0x78,0x37,
0x36,0x44,0x42,0x44,0x42,0x41,0x44,0x55,0x2c,0x0a,0x30,0x78,0x33,0x42,0x45,0x30,0x45,0x30,0x44,0x42,0x55,0x2c,0x30,0x78,0x35,0x36,0x33,0x32,0x33,0x32,0x36,0x34,
0x55,0x2c,0x30,0x78,0x34,0x45,0x33,0x41,0x33,0x41,0x37,0x34,0x55,0x2c,0x30,0x78,0x31,0x45,0x30,0x41,0x30,0x41,0x31,0x34,0x55,0x2c,0x0a,0x30,0x78,0x44,0x42,0x34,
0x39,0x34,0x39,0x39,0x32,0x55,0x2c,0x30,0x78,0x30,0x41,0x30,0x36,0x30,0x36,0x30,0x43,0x55,0x2c,0x30,0x78,0x36,0x43,0x32,0x34,0x32,0x34,0x34,0x38,0x55,0x2c,0x30,
0x78,0x45,0x34,0x35,0x43,0x35,0x43,0x42,0x38,0x55,0x2c,0x0a,0x30,0x78,0x35,0x44,0x43,0x32,0x43,0x32,0x39,0x46,0x55,0x2c,0x30,0x78,0x36,0x45,0x44,0x33,0x44,0x33,
0x42,0x44,0x55,0x2c,0x30,0x78,0x45,0x46,0x41,0x43,0x41,0x43,0x34,0x33,0x55,0x2c,0x30,0x78,0x41,0x36,0x36,0x32,0x36,0x32,0x43,0x34,0x55,0x2c,0x0a,0x30,0x78,0x41,
0x38,0x39,0x31,0x39,0x31,0x33,0x39,0x55,0x2c,0x30,0x78,0x41,0x34,0x39,0x35,0x39,0x35,0x33,0x31,0x55,0x2c,0x30,0x78,0x33,0x37,0x45,0x34,0x45,0x34,0x44,0x33,0x55,
0x2c,0x30,0x78,0x38,0x42,0x37,0x39,0x37,0x39,0x46,0x32,0x55,0x2c,0x0a,0x30,0x78,0x33,0x32,0x45,0x37,0x45,0x37,0x44,0x35,0x55,0x2c,0x30,0x78,0x34,0x33,0x43,0x38,
0x43,0x38,0x38,0x42,0x55,0x2c,0x30,0x78,0x35,0x39,0x33,0x37,0x33,0x37,0x36,0x45,0x55,0x2c,0x30,0x78,0x42,0x37,0x36,0x44,0x36,0x44,0x44,0x41,0x55,0x2c,0x0a,0x30,
0x78,0x38,0x43,0x38,0x44,0x38,0x44,0x30,0x31,0x55,0x2c,0x30,0x78,0x36,0x34,0x44,0x35,0x44,0x35,0x42,0x31,0x55,0x2c,0x30,0x78,0x44,0x32,0x34,0x45,0x34,0x45,0x39,
0x43,0x55,0x2c,0x30,0x78,0x45,0x30,0x41,0x39,0x41,0x39,0x34,0x39,0x55,0x2c,0x0a,0x30,0x78,0x42,0x34,0x36,0x43,0x36,0x43,0x44,0x38,0x55,0x2c,0x30,0x78,0x46,0x41,
0x35,0x36,0x35,0x36,0x41,0x43,0x55,0x2c,0x30,0x78,0x30,0x37,0x46,0x34,0x46,0x34,0x46,0x33,0x55,0x2c,0x30,0x78,0x32,0x35,0x45,0x41,0x45,0x41,0x43,0x46,0x55,0x2c,
0x0a,0x30,0x78,0x41,0x46,0x36,0x35,0x36,0x35,0x43,0x41,0x55,0x2c,0x30,0x78,0x38,0x45,0x37,0x41,0x37,0x41,0x46,0x34,0x55,0x2c,0x30,0x78,0x45,0x39,0x41,0x45,0x41,
0x45,0x34,0x37,0x55,0x2c,0x30,0x78,0x31,0x38,0x30,0x38,0x30,0x38,0x31,0x30,0x55,0x2c,0x0a,0x30,0x78,0x44,0x35,0x42,0x41,0x42,0x41,0x36,0x46,0x55,0x2c,0x30,0x78,
0x38,0x38,0x37,0x38,0x37,0x38,0x46,0x30,0x55,0x2c,0x30,0x78,0x36,0x46,0x32,0x35,0x32,0x35,0x34,0x41,0x55,0x2c,0x30,0x78,0x37,0x32,0x32,0x45,0x32,0x45,0x35,0x43,
0x55,0x2c,0x0a,0x30,0x78,0x32,0x34,0x31,0x43,0x31,0x43,0x33,0x38,0x55,0x2c,0x30,0x78,0x46,0x31,0x41,0x36,0x41,0x36,0x35,0x37,0x55,0x2c,0x30,0x78,0x43,0x37,0x42,
0x34,0x42,0x34,0x37,0x33,0x55,0x2c,0x30,0x78,0x35,0x31,0x43,0x36,0x43,0x36,0x39,0x37,0x55,0x2c,0x0a,0x30,0x78,0x32,0x33,0x45,0x38,0x45,0x38,0x43,0x42,0x55,0x2c,
0x30,0x78,0x37,0x43,0x44,0x44,0x44,0x44,0x41,0x31,0x55,0x2c,0x30,0x78,0x39,0x43,0x37,0x34,0x37,0x34,0x45,0x38,0x55,0x2c,0x30,0x78,0x32,0x31,0x31,0x46,0x31,0x46,
0x33,0x45,0x55,0x2c,0x0a,0x30,0x78,0x44,0x44,0x34,0x42,0x34,0x42,0x39,0x36,0x55,0x2c,0x30,0x78,0x44,0x43,0x42,0x44,0x42,0x44,0x36,0x31,0x55,0x2c,0x30,0x78,0x38,
0x36,0x38,0x42,0x38,0x42,0x30,0x44,0x55,0x2c,0x30,0x78,0x38,0x35,0x38,0x41,0x38,0x41,0x30,0x46,0x55,0x2c,0x0a,0x30,0x78,0x39,0x30,0x37,0x30,0x37,0x30,0x45,0x30,
0x55,0x2c,0x30,0x78,0x34,0x32,0x33,0x45,0x33,0x45,0x37,0x43,0x55,0x2c,0x30,0x78,0x43,0x34,0x42,0x35,0x42,0x35,0x37,0x31,0x55,0x2c,0x30,0x78,0x41,0x41,0x36,0x36,
0x36,0x36,0x43,0x43,0x55,0x2c,0x0a,0x30,0x78,0x44,0x38,0x34,0x38,0x34,0x38,0x39,0x30,0x55,0x2c,0x30,0x78,0x30,0x35,0x30,0x33,0x30,0x33,0x30,0x36,0x55,0x2c,0x30,
0x78,0x30,0x31,0x46,0x36,0x46,0x36,0x46,0x37,0x55,0x2c,0x30,0x78,0x31,0x32,0x30,0x45,0x30,0x45,0x31,0x43,0x55,0x2c,0x0a,0x30,0x78,0x41,0x33,0x36,0x31,0x36,0x31,
0x43,0x32,0x55,0x2c,0x30,0x78,0x35,0x46,0x33,0x35,0x33,0x35,0x36,0x41,0x55,0x2c,0x30,0x78,0x46,0x39,0x35,0x37,0x35,0x37,0x41,0x45,0x55,0x2c,0x30,0x78,0x44,0x30,
0x42,0x39,0x42,0x39,0x36,0x39,0x55,0x2c,0x0a,0x30,0x78,0x39,0x31,0x38,0x36,0x38,0x36,0x31,0x37,0x55,0x2c,0x30,0x78,0x35,0x38,0x43,0x31,0x43,0x31,0x39,0x39,0x55,
0x2c,0x30,0x78,0x32,0x37,0x31,0x44,0x31,0x44,0x33,0x41,0x55,0x2c,0x30,0x78,0x42,0x39,0x39,0x45,0x39,0x45,0x32,0x37,0x55,0x2c,0x0a,0x30,0x78,0x33,0x38,0x45,0x31,
0x45,0x31,0x44,0x39,0x55,0x2c,0x30,0x78,0x31,0x33,0x46,0x38,0x46,0x38,0x45,0x42,0x55,0x2c,0x30,0x78,0x42,0x33,0x39,0x38,0x39,0x38,0x32,0x42,0x55,0x2c,0x30,0x78,
0x33,0x33,0x31,0x31,0x31,0x31,0x32,0x32,0x55,0x2c,0x0a,0x30,0x78,0x42,0x42,0x36,0x39,0x36,0x39,0x44,0x32,0x55,0x2c,0x30,0x78,0x37,0x30,0x44,0x39,0x44,0x39,0x41,
0x39,0x55,0x2c,0x30,0x78,0x38,0x39,0x38,0x45,0x38,0x45,0x30,0x37,0x55,0x2c,0x30,0x78,0x41,0x37,0x39,0x34,0x39,0x34,0x33,0x33,0x55,0x2c,0x0a,0x30,0x78,0x42,0x36,
0x39,0x42,0x39,0x42,0x32,0x44,0x55,0x2c,0x30,0x78,0x32,0x32,0x31,0x45,0x31,0x45,0x33,0x43,0x55,0x2c,0x30,0x78,0x39,0x32,0x38,0x37,0x38,0x37,0x31,0x35,0x55,0x2c,
0x30,0x78,0x32,0x30,0x45,0x39,0x45,0x39,0x43,0x39,0x55,0x2c,0x0a,0x30,0x78,0x34,0x39,0x43,0x45,0x43,0x45,0x38,0x37,0x55,0x2c,0x30,0x78,0x46,0x46,0x35,0x35,0x35,
0x35,0x41,0x41,0x55,0x2c,0x30,0x78,0x37,0x38,0x32,0x38,0x32,0x38,0x35,0x30,0x55,0x2c,0x30,0x78,0x37,0x41,0x44,0x46,0x44,0x46,0x41,0x35,0x55,0x2c,0x0a,0x30,0x78,
0x38,0x46,0x38,0x43,0x38,0x43,0x30,0x33,0x55,0x2c,0x30,0x78,0x46,0x38,0x41,0x31,0x41,0x31,0x35,0x39,0x55,0x2c,0x30,0x78,0x38,0x30,0x38,0x39,0x38,0x39,0x30,0x39,
0x55,0x2c,0x30,0x78,0x31,0x37,0x30,0x44,0x30,0x44,0x31,0x41,0x55,0x2c,0x0a,0x30,0x78,0x44,0x41,0x42,0x46,0x42,0x46,0x36,0x35,0x55,0x2c,0x30,0x78,0x33,0x31,0x45,
0x36,0x45,0x36,0x44,0x37,0x55,0x2c,0x30,0x78,0x43,0x36,0x34,0x32,0x34,0x32,0x38,0x34,0x55,0x2c,0x30,0x78,0x42,0x38,0x36,0x38,0x36,0x38,0x44,0x30,0x55,0x2c,0x0a,
0x30,0x78,0x43,0x33,0x34,0x31,0x34,0x31,0x38,0x32,0x55,0x2c,0x30,0x78,0x42,0x30,0x39,0x39,0x39,0x39,0x32,0x39,0x55,0x2c,0x30,0x78,0x37,0x37,0x32,0x44,0x32,0x44,
0x35,0x41,0x55,0x2c,0x30,0x78,0x31,0x31,0x30,0x46,0x30,0x46,0x31,0x45,0x55,0x2c,0x0a,0x30,0x78,0x43,0x42,0x42,0x30,0x42,0x30,0x37,0x42,0x55,0x2c,0x30,0x78,0x46,
0x43,0x35,0x34,0x35,0x34,0x41,0x38,0x55,0x2c,0x30,0x78,0x44,0x36,0x42,0x42,0x42,0x42,0x36,0x44,0x55,0x2c,0x30,0x78,0x33,0x41,0x31,0x36,0x31,0x36,0x32,0x43,0x55,
0x0a,0x7d,0x3b,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x42,0x59,0x54,0x45,0x28,0x78,0x2c,0x20,0x79,0x29,0x20,0x28,0x78,0x6d,0x72,0x69,0x67,0x5f,0x61,0x6d,
0x64,0x5f,0x62,0x66,0x65,0x28,0x28,0x78,0x29,0x2c,0x20,0x28,0x79,0x29,0x20,0x3c,0x3c,0x20,0x33,0x55,0x2c,0x20,0x38,0x55,0x29,0x29,0x0a,0x23,0x69,0x66,0x20,0x28,
0x41,0x4c,0x47,0x4f,0x20,0x3d,0x3d,0x20,0x41,0x4c,0x47,0x4f,0x5f,0x43,0x4e,0x5f,0x48,0x45,0x41,0x56,0x59,0x5f,0x54,0x55,0x42,0x45,0x29,0x0a,0x69,0x6e,0x6c,0x69,
0x6e,0x65,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,0x64,0x5f,0x62,0x69,0x74,0x74,0x75,0x62,0x65,0x32,0x28,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x30,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,
0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x31,0x2c,0x75,0x69,0x6e,0x74,0x34,0x20,0x78,0x2c,0x75,0x69,0x6e,0x74,0x34,0x20,0x6b,0x29,0x0a,0x7b,
0x0a,0x78,0x3d,0x7e,0x78,0x3b,0x0a,0x6b,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x30,0x29,
0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x31,0x29,0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,
0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,0x33,0x29,
0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x78,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x6b,0x2e,0x73,0x30,0x3b,0x0a,0x6b,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x41,0x45,
0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,
0x31,0x29,0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,
0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x78,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x6b,
0x2e,0x73,0x31,0x3b,0x0a,0x6b,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x30,0x29,0x5d,0x5e,
0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,0x31,0x29,0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,
0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x33,0x29,0x5d,0x2c,
0x31,0x36,0x55,0x29,0x3b,0x0a,0x78,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x6b,0x2e,0x73,0x32,0x3b,0x0a,0x6b,0x2e,0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,
0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x31,0x29,
0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,
0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6b,0x3b,0x0a,0x7d,
0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,0x64,0x28,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,
0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x30,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,
0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x31,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,
0x53,0x32,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x33,0x2c,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x58,0x2c,0x75,0x69,0x6e,0x74,0x34,0x20,0x6b,0x65,0x79,0x29,0x0a,0x7b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x30,0x20,0x5e,0x3d,
0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,
0x73,0x31,0x2c,0x31,0x29,0x5d,0x5e,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x33,0x5b,0x42,
0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x33,0x29,0x5d,0x3b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,
0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x31,0x29,0x5d,0x5e,0x41,0x45,
0x53,0x32,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,
0x33,0x29,0x5d,0x3b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x30,0x29,
0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x31,0x29,0x5d,0x5e,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,
0x2e,0x73,0x30,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x33,0x29,0x5d,0x3b,0x0a,0x6b,0x65,0x79,0x2e,
0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,
0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x31,0x29,0x5d,0x5e,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x32,0x29,0x5d,0x5e,0x41,
0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x33,0x29,0x5d,0x3b,0x0a,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6b,0x65,0x79,0x3b,0x0a,0x7d,
0x0a,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,0x64,0x5f,0x54,0x77,0x6f,0x5f,0x54,0x61,0x62,0x6c,0x65,0x73,0x28,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x30,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,
0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x31,0x2c,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x58,0x2c,0x75,0x69,0x6e,0x74,
0x34,0x20,0x6b,0x65,0x79,0x29,0x0a,0x7b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,
0x30,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x31,0x29,0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,
0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,
0x33,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,
0x58,0x2e,0x73,0x31,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x31,0x29,0x5d,0x5e,0x72,0x6f,0x74,0x61,
0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,
0x58,0x2e,0x73,0x30,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,
0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x31,0x29,0x5d,0x5e,0x72,
0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,
0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x6b,0x65,0x79,0x2e,0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,
0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x30,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x31,0x29,
0x5d,0x5e,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x32,0x29,0x5d,0x5e,0x41,0x45,0x53,0x31,
0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x33,0x29,0x5d,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6b,0x65,0x79,0x3b,
0x0a,0x7d,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x75,0x63,0x68,0x61,0x72,
0x20,0x72,0x63,0x6f,0x6e,0x5b,0x38,0x5d,0x3d,0x7b,0x20,0x30,0x78,0x38,0x64,0x2c,0x30,0x78,0x30,0x31,0x2c,0x30,0x78,0x30,0x32,0x2c,0x30,0x78,0x30,0x34,0x2c,0x30,
0x78,0x30,0x38,0x2c,0x30,0x78,0x31,0x30,0x2c,0x30,0x78,0x32,0x30,0x2c,0x30,0x78,0x34,0x30,0x20,0x7d,0x3b,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,
0x73,0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x75,0x63,0x68,0x61,0x72,0x20,0x73,0x62,0x6f,0x78,0x5b,0x32,0x35,0x36,0x5d,0x20,0x3d,0x0a,
0x7b,0x0a,0x30,0x78,0x36,0x33,0x2c,0x30,0x78,0x37,0x43,0x2c,0x30,0x78,0x37,0x37,0x2c,0x30,0x78,0x37,0x42,0x2c,0x30,0x78,0x46,0x32,0x2c,0x30,0x78,0x36,0x42,0x2c,
0x30,0x78,0x36,0x46,0x2c,0x30,0x78,0x43,0x35,0x2c,0x30,0x78,0x33,0x30,0x2c,0x30,0x78,0x30,0x31,0x2c,0x30,0x78,0x36,0x37,0x2c,0x30,0x78,0x32,0x42,0x2c,0x30,0x78,
0x46,0x45,0x2c,0x30,0x78,0x44,0x37,0x2c,0x30,0x78,0x41,0x42,0x2c,0x30,0x78,0x37,0x36,0x2c,0x0a,0x30,0x78,0x43,0x41,0x2c,0x30,0x78,0x38,0x32,0x2c,0x30,0x78,0x43,
0x39,0x2c,0x30,0x78,0x37,0x44,0x2c,0x30,0x78,0x46,0x41,0x2c,0x30,0x78,0x35,0x39,0x2c,0x30,0x78,0x34,0x37,0x2c,0x30,0x78,0x46,0x30,0x2c,0x30,0x78,0x41,0x44,0x2c,
0x30,0x78,0x44,0x34,0x2c,0x30,0x78,0x41,0x32,0x2c,0x30,0x78,0x41,0x46,0x2c,0x30,0x78,0x39,0x43,0x2c,0x30,0x78,0x41,0x34,0x2c,0x30,0x78,0x37,0x32,0x2c,0x30,0x78,
0x43,0x30,0x2c,0x0a,0x30,0x78,0x42,0x37,0x2c,0x30,0x78,0x46,0x44,0x2c,0x30,0x78,0x39,0x33,0x2c,0x30,0x78,0x32,0x36,0x2c,0x30,0x78,0x33,0x36,0x2c,0x30,0x78,0x33,
0x46,0x2c,0x30,0x78,0x46,0x37,0x2c,0x30,0x78,0x43,0x43,0x2c,0x30,0x78,0x33,0x34,0x2c,0x30,0x78,0x41,0x35,0x2c,0x30,0x78,0x45,0x35,0x2c,0x30,0x78,0x46,0x31,0x2c,
0x30,0x78,0x37,0x31,0x2c,0x30,0x78,0x44,0x38,0x2c,0x30,0x78,0x33,0x31,0x2c,0x30,0x78,0x31,0x35,0x2c,0x0a,0x30,0x78,0x30,0x34,0x2c,0x30,0x78,0x43,0x37,0x2c,0x30,
0x78,0x32,0x33,0x2c,0x30,0x78,0x43,0x33,0x2c,0x30,0x78,0x31,0x38,0x2c,0x30,0x78,0x39,0x36,0x2c,0x30,0x78,0x30,0x35,0x2c,0x30,0x78,0x39,0x41,0x2c,0x30,0x78,0x30,
0x37,0x2c,0x30,0x78,0x31,0x32,0x2c,0x30,0x78,0x38,0x30,0x2c,0x30,0x78,0x45,0x32,0x2c,0x30,0x78,0x45,0x42,0x2c,0x30,0x78,0x32,0x37,0x2c,0x30,0x78,0x42,0x32,0x2c,
0x30,0x78,0x37,0x35,0x2c,0x0a,0x30,0x78,0x30,0x39,0x2c,0x30,0x78,0x38,0x33,0x2c,0x30,0x78,0x32,0x43,0x2c,0x30,0x78,0x31,0x41,0x2c,0x30,0x78,0x31,0x42,0x2c,0x30,
0x78,0x36,0x45,0x2c,0x30,0x78,0x35,0x41,0x2c,0x30,0x78,0x41,0x30,0x2c,0x30,0x78,0x35,0x32,0x2c,0x30,0x78,0x33,0x42,0x2c,0x30,0x78,0x44,0x36,0x2c,0x30,0x78,0x42,
0x33,0x2c,0x30,0x78,0x32,0x39,0x2c,0x30,0x78,0x45,0x33,0x2c,0x30,0x78,0x32,0x46,0x2c,0x30,0x78,0x38,0x34,0x2c,0x0a,0x30,0x78,0x35,0x33,0x2c,0x30,0x78,0x44,0x31,
0x2c,0x30,0x78,0x30,0x30,0x2c,0x30,0x78,0x45,0x44,0x2c,0x30,0x78,0x32,0x30,0x2c,0x30,0x78,0x46,0x43,0x2c,0x30,0x78,0x42,0x31,0x2c,0x30,0x78,0x35,0x42,0x2c,0x30,
0x78,0x36,0x41,0x2c,0x30,0x78,0x43,0x42,0x2c,0x30,0x78,0x42,0x45,0x2c,0x30,0x78,0x33,0x39,0x2c,0x30,0x78,0x34,0x41,0x2c,0x30,0x78,0x34,0x43,0x2c,0x30,0x78,0x35,
0x38,0x2c,0x30,0x78,0x43,0x46,0x2c,0x0a,0x30,0x78,0x44,0x30,0x2c,0x30,0x78,0x45,0x46,0x2c,0x30,0x78,0x41,0x41,0x2c,0x30,0x78,0x46,0x42,0x2c,0x30,0x78,0x34,0x33,
0x2c,0x30,0x78,0x34,0x44,0x2c,0x30,0x78,0x33,0x33,0x2c,0x30,0x78,0x38,0x35,0x2c,0x30,0x78,0x34,0x35,0x2c,0x30,0x78,0x46,0x39,0x2c,0x30,0x78,0x30,0x32,0x2c,0x30,
0x78,0x37,0x46,0x2c,0x30,0x78,0x35,0x30,0x2c,0x30,0x78,0x33,0x43,0x2c,0x30,0x78,0x39,0x46,0x2c,0x30,0x78,0x41,0x38,0x2c,0x0a,0x30,0x78,0x35,0x31,0x2c,0x30,0x78,
0x41,0x33,0x2c,0x30,0x78,0x34,0x30,0x2c,0x30,0x78,0x38,0x46,0x2c,0x30,0x78,0x39,0x32,0x2c,0x30,0x78,0x39,0x44,0x2c,0x30,0x78,0x33,0x38,0x2c,0x30,0x78,0x46,0x35,
0x2c,0x30,0x78,0x42,0x43,0x2c,0x30,0x78,0x42,0x36,0x2c,0x30,0x78,0x44,0x41,0x2c,0x30,0x78,0x32,0x31,0x2c,0x30,0x78,0x31,0x30,0x2c,0x30,0x78,0x46,0x46,0x2c,0x30,
0x78,0x46,0x33,0x2c,0x30,0x78,0x44,0x32,0x2c,0x0a,0x30,0x78,0x43,0x44,0x2c,0x30,0x78,0x30,0x43,0x2c,0x30,0x78,0x31,0x33,0x2c,0x30,0x78,0x45,0x43,0x2c,0x30,0x78,
0x35,0x46,0x2c,0x30,0x78,0x39,0x37,0x2c,0x30,0x78,0x34,0x34,0x2c,0x30,0x78,0x31,0x37,0x2c,0x30,0x78,0x43,0x34,0x2c,0x30,0x78,0x41,0x37,0x2c,0x30,0x78,0x37,0x45,
0x2c,0x30,0x78,0x33,0x44,0x2c,0x30,0x78,0x36,0x34,0x2c,0x30,0x78,0x35,0x44,0x2c,0x30,0x78,0x31,0x39,0x2c,0x30,0x78,0x37,0x33,0x2c,0x0a,0x30,0x78,0x36,0x30,0x2c,
0x30,0x78,0x38,0x31,0x2c,0x30,0x78,0x34,0x46,0x2c,0x30,0x78,0x44,0x43,0x2c,0x30,0x78,0x32,0x32,0x2c,0x30,0x78,0x32,0x41,0x2c,0x30,0x78,0x39,0x30,0x2c,0x30,0x78,
0x38,0x38,0x2c,0x30,0x78,0x34,0x36,0x2c,0x30,0x78,0x45,0x45,0x2c,0x30,0x78,0x42,0x38,0x2c,0x30,0x78,0x31,0x34,0x2c,0x30,0x78,0x44,0x45,0x2c,0x30,0x78,0x35,0x45,
0x2c,0x30,0x78,0x30,0x42,0x2c,0x30,0x78,0x44,0x42,0x2c,0x0a,0x30,0x78,0x45,0x30,0x2c,0x30,0x78,0x33,0x32,0x2c,0x30,0x78,0x33,0x41,0x2c,0x30,0x78,0x30,0x41,0x2c,
0x30,0x78,0x34,0x39,0x2c,0x30,0x78,0x30,0x36,0x2c,0x30,0x78,0x32,0x34,0x2c,0x30,0x78,0x35,0x43,0x2c,0x30,0x78,0x43,0x32,0x2c,0x30,0x78,0x44,0x33,0x2c,0x30,0x78,
0x41,0x43,0x2c,0x30,0x78,0x36,0x32,0x2c,0x30,0x78,0x39,0x31,0x2c,0x30,0x78,0x39,0x35,0x2c,0x30,0x78,0x45,0x34,0x2c,0x30,0x78,0x37,0x39,0x2c,0x0a,0x30,0x78,0x45,
0x37,0x2c,0x30,0x78,0x43,0x38,0x2c,0x30,0x78,0x33,0x37,0x2c,0x30,0x78,0x36,0x44,0x2c,0x30,0x78,0x38,0x44,0x2c,0x30,0x78,0x44,0x35,0x2c,0x30,0x78,0x34,0x45,0x2c,
0x30,0x78,0x41,0x39,0x2c,0x30,0x78,0x36,0x43,0x2c,0x30,0x78,0x35,0x36,0x2c,0x30,0x78,0x46,0x34,0x2c,0x30,0x78,0x45,0x41,0x2c,0x30,0x78,0x36,0x35,0x2c,0x30,0x78,
0x37,0x41,0x2c,0x30,0x78,0x41,0x45,0x2c,0x30,0x78,0x30,0x38,0x2c,0x0a,0x30,0x78,0x42,0x41,0x2c,0x30,0x78,0x37,0x38,0x2c,0x30,0x78,0x32,0x35,0x2c,0x30,0x78,0x32,
0x45,0x2c,0x30,0x78,0x31,0x43,0x2c,0x30,0x78,0x41,0x36,0x2c,0x30,0x78,0x42,0x34,0x2c,0x30,0x78,0x43,0x36,0x2c,0x30,0x78,0x45,0x38,0x2c,0x30,0x78,0x44,0x44,0x2c,
0x30,0x78,0x37,0x34,0x2c,0x30,0x78,0x31,0x46,0x2c,0x30,0x78,0x34,0x42,0x2c,0x30,0x78,0x42,0x44,0x2c,0x30,0x78,0x38,0x42,0x2c,0x30,0x78,0x38,0x41,0x2c,0x0a,0x30,
0x78,0x37,0x30,0x2c,0x30,0x78,0x33,0x45,0x2c,0x30,0x78,0x42,0x35,0x2c,0x30,0x78,0x36,0x36,0x2c,0x30,0x78,0x34,0x38,0x2c,0x30,0x78,0x30,0x33,0x2c,0x30,0x78,0x46,
0x36,0x2c,0x30,0x78,0x30,0x45,0x2c,0x30,0x78,0x36,0x31,0x2c,0x30,0x78,0x33,0x35,0x2c,0x30,0x78,0x35,0x37,0x2c,0x30,0x78,0x42,0x39,0x2c,0x30,0x78,0x38,0x36,0x2c,
0x30,0x78,0x43,0x31,0x2c,0x30,0x78,0x31,0x44,0x2c,0x30,0x78,0x39,0x45,0x2c,0x0a,0x30,0x78,0x45,0x31,0x2c,0x30,0x78,0x46,0x38,0x2c,0x30,0x78,0x39,0x38,0x2c,0x30,
0x78,0x31,0x31,0x2c,0x30,0x78,0x36,0x39,0x2c,0x30,0x78,0x44,0x39,0x2c,0x30,0x78,0x38,0x45,0x2c,0x30,0x78,0x39,0x34,0x2c,0x30,0x78,0x39,0x42,0x2c,0x30,0x78,0x31,
0x45,0x2c,0x30,0x78,0x38,0x37,0x2c,0x30,0x78,0x45,0x39,0x2c,0x30,0x78,0x43,0x45,0x2c,0x30,0x78,0x35,0x35,0x2c,0x30,0x78,0x32,0x38,0x2c,0x30,0x78,0x44,0x46,0x2c,
0x0a,0x30,0x78,0x38,0x43,0x2c,0x30,0x78,0x41,0x31,0x2c,0x30,0x78,0x38,0x39,0x2c,0x30,0x78,0x30,0x44,0x2c,0x30,0x78,0x42,0x46,0x2c,0x30,0x78,0x45,0x36,0x2c,0x30,
0x78,0x34,0x32,0x2c,0x30,0x78,0x36,0x38,0x2c,0x30,0x78,0x34,0x31,0x2c,0x30,0x78,0x39,0x39,0x2c,0x30,0x78,0x32,0x44,0x2c,0x30,0x78,0x30,0x46,0x2c,0x30,0x78,0x42,
0x30,0x2c,0x30,0x78,0x35,0x34,0x2c,0x30,0x78,0x42,0x42,0x2c,0x30,0x78,0x31,0x36,0x0a,0x7d,0x3b,0x0a,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x53,0x75,0x62,0x57,
0x6f,0x72,0x64,0x28,0x69,0x6e,0x77,0x29,0x20,0x28,0x28,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x33,0x29,0x5d,0x20,0x3c,0x3c,
0x20,0x32,0x34,0x29,0x20,0x7c,0x20,0x28,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x32,0x29,0x5d,0x20,0x3c,0x3c,0x20,0x31,0x36,
0x29,0x20,0x7c,0x20,0x28,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x31,0x29,0x5d,0x20,0x3c,0x3c,0x20,0x38,0x29,0x20,0x7c,0x20,
0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x30,0x29,0x5d,0x29,0x0a,0x76,0x6f,0x69,0x64,0x20,0x41,0x45,0x53,0x45,0x78,0x70,0x61,
0x6e,0x64,0x4b,0x65,0x79,0x32,0x35,0x36,0x28,0x75,0x69,0x6e,0x74,0x20,0x2a,0x6b,0x65,0x79,0x62,0x75,0x66,0x29,0x0a,0x7b,0x0a,0x66,0x6f,0x72,0x20,0x28,0x75,0x69,
0x6e,0x74,0x20,0x63,0x3d,0x38,0x2c,0x69,0x3d,0x31,0x3b,0x20,0x63,0x3c,0x34,0x30,0x3b,0x20,0x2b,0x2b,0x63,0x29,0x20,0x7b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x74,0x3d,
0x28,0x28,0x21,0x28,0x63,0x26,0x37,0x29,0x29,0x7c,0x7c,0x28,0x28,0x63,0x26,0x37,0x29,0x3d,0x3d,0x34,0x29,0x29,0x3f,0x53,0x75,0x62,0x57,0x6f,0x72,0x64,0x28,0x6b,
0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x2d,0x31,0x5d,0x29,0x3a,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x2d,0x31,0x5d,0x3b,0x0a,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,
0x63,0x5d,0x3d,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x2d,0x38,0x5d,0x5e,0x28,0x28,0x21,0x28,0x63,0x26,0x37,0x29,0x29,0x3f,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,
0x74,0x2c,0x32,0x34,0x55,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x28,0x28,0x75,0x63,0x68,0x61,0x72,0x34,0x29,0x28,0x72,0x63,0x6f,0x6e,0x5b,0x69,0x2b,0x2b,
0x5d,0x2c,0x30,0x55,0x2c,0x30,0x55,0x2c,0x30,0x55,0x29,0x29,0x3a,0x74,0x29,0x3b,0x0a,0x7d,0x0a,0x7d,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x00
0x20,0x73,0x72,0x63,0x30,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,
0x75,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x0d,0x7b,0x0d,0x69,0x66,0x20,0x28,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,
0x68,0x29,0x20,0x3c,0x20,0x33,0x32,0x75,0x29,0x20,0x7b,0x0d,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x73,0x72,0x63,0x30,0x20,0x3c,0x3c,0x20,0x28,0x33,0x32,0x75,
0x20,0x2d,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2d,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x29,0x20,0x3e,0x3e,0x20,0x28,0x33,0x32,0x75,0x20,0x2d,0x20,0x77,0x69,
0x64,0x74,0x68,0x29,0x3b,0x0d,0x7d,0x0d,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x72,0x63,0x30,0x20,0x3e,0x3e,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x3b,0x0d,0x7d,
0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,
0x75,0x69,0x6e,0x74,0x20,0x41,0x45,0x53,0x30,0x5f,0x43,0x5b,0x32,0x35,0x36,0x5d,0x20,0x3d,0x0d,0x7b,0x0d,0x30,0x78,0x41,0x35,0x36,0x33,0x36,0x33,0x43,0x36,0x55,
0x2c,0x20,0x30,0x78,0x38,0x34,0x37,0x43,0x37,0x43,0x46,0x38,0x55,0x2c,0x20,0x30,0x78,0x39,0x39,0x37,0x37,0x37,0x37,0x45,0x45,0x55,0x2c,0x20,0x30,0x78,0x38,0x44,
0x37,0x42,0x37,0x42,0x46,0x36,0x55,0x2c,0x0d,0x30,0x78,0x30,0x44,0x46,0x32,0x46,0x32,0x46,0x46,0x55,0x2c,0x20,0x30,0x78,0x42,0x44,0x36,0x42,0x36,0x42,0x44,0x36,
0x55,0x2c,0x20,0x30,0x78,0x42,0x31,0x36,0x46,0x36,0x46,0x44,0x45,0x55,0x2c,0x20,0x30,0x78,0x35,0x34,0x43,0x35,0x43,0x35,0x39,0x31,0x55,0x2c,0x0d,0x30,0x78,0x35,
0x30,0x33,0x30,0x33,0x30,0x36,0x30,0x55,0x2c,0x20,0x30,0x78,0x30,0x33,0x30,0x31,0x30,0x31,0x30,0x32,0x55,0x2c,0x20,0x30,0x78,0x41,0x39,0x36,0x37,0x36,0x37,0x43,
0x45,0x55,0x2c,0x20,0x30,0x78,0x37,0x44,0x32,0x42,0x32,0x42,0x35,0x36,0x55,0x2c,0x0d,0x30,0x78,0x31,0x39,0x46,0x45,0x46,0x45,0x45,0x37,0x55,0x2c,0x20,0x30,0x78,
0x36,0x32,0x44,0x37,0x44,0x37,0x42,0x35,0x55,0x2c,0x20,0x30,0x78,0x45,0x36,0x41,0x42,0x41,0x42,0x34,0x44,0x55,0x2c,0x20,0x30,0x78,0x39,0x41,0x37,0x36,0x37,0x36,
0x45,0x43,0x55,0x2c,0x0d,0x30,0x78,0x34,0x35,0x43,0x41,0x43,0x41,0x38,0x46,0x55,0x2c,0x20,0x30,0x78,0x39,0x44,0x38,0x32,0x38,0x32,0x31,0x46,0x55,0x2c,0x20,0x30,
0x78,0x34,0x30,0x43,0x39,0x43,0x39,0x38,0x39,0x55,0x2c,0x20,0x30,0x78,0x38,0x37,0x37,0x44,0x37,0x44,0x46,0x41,0x55,0x2c,0x0d,0x30,0x78,0x31,0x35,0x46,0x41,0x46,
0x41,0x45,0x46,0x55,0x2c,0x20,0x30,0x78,0x45,0x42,0x35,0x39,0x35,0x39,0x42,0x32,0x55,0x2c,0x20,0x30,0x78,0x43,0x39,0x34,0x37,0x34,0x37,0x38,0x45,0x55,0x2c,0x20,
0x30,0x78,0x30,0x42,0x46,0x30,0x46,0x30,0x46,0x42,0x55,0x2c,0x0d,0x30,0x78,0x45,0x43,0x41,0x44,0x41,0x44,0x34,0x31,0x55,0x2c,0x20,0x30,0x78,0x36,0x37,0x44,0x34,
0x44,0x34,0x42,0x33,0x55,0x2c,0x20,0x30,0x78,0x46,0x44,0x41,0x32,0x41,0x32,0x35,0x46,0x55,0x2c,0x20,0x30,0x78,0x45,0x41,0x41,0x46,0x41,0x46,0x34,0x35,0x55,0x2c,
0x0d,0x30,0x78,0x42,0x46,0x39,0x43,0x39,0x43,0x32,0x33,0x55,0x2c,0x20,0x30,0x78,0x46,0x37,0x41,0x34,0x41,0x34,0x35,0x33,0x55,0x2c,0x20,0x30,0x78,0x39,0x36,0x37,
0x32,0x37,0x32,0x45,0x34,0x55,0x2c,0x20,0x30,0x78,0x35,0x42,0x43,0x30,0x43,0x30,0x39,0x42,0x55,0x2c,0x0d,0x30,0x78,0x43,0x32,0x42,0x37,0x42,0x37,0x37,0x35,0x55,
0x2c,0x20,0x30,0x78,0x31,0x43,0x46,0x44,0x46,0x44,0x45,0x31,0x55,0x2c,0x20,0x30,0x78,0x41,0x45,0x39,0x33,0x39,0x33,0x33,0x44,0x55,0x2c,0x20,0x30,0x78,0x36,0x41,
0x32,0x36,0x32,0x36,0x34,0x43,0x55,0x2c,0x0d,0x30,0x78,0x35,0x41,0x33,0x36,0x33,0x36,0x36,0x43,0x55,0x2c,0x20,0x30,0x78,0x34,0x31,0x33,0x46,0x33,0x46,0x37,0x45,
0x55,0x2c,0x20,0x30,0x78,0x30,0x32,0x46,0x37,0x46,0x37,0x46,0x35,0x55,0x2c,0x20,0x30,0x78,0x34,0x46,0x43,0x43,0x43,0x43,0x38,0x33,0x55,0x2c,0x0d,0x30,0x78,0x35,
0x43,0x33,0x34,0x33,0x34,0x36,0x38,0x55,0x2c,0x20,0x30,0x78,0x46,0x34,0x41,0x35,0x41,0x35,0x35,0x31,0x55,0x2c,0x20,0x30,0x78,0x33,0x34,0x45,0x35,0x45,0x35,0x44,
0x31,0x55,0x2c,0x20,0x30,0x78,0x30,0x38,0x46,0x31,0x46,0x31,0x46,0x39,0x55,0x2c,0x0d,0x30,0x78,0x39,0x33,0x37,0x31,0x37,0x31,0x45,0x32,0x55,0x2c,0x20,0x30,0x78,
0x37,0x33,0x44,0x38,0x44,0x38,0x41,0x42,0x55,0x2c,0x20,0x30,0x78,0x35,0x33,0x33,0x31,0x33,0x31,0x36,0x32,0x55,0x2c,0x20,0x30,0x78,0x33,0x46,0x31,0x35,0x31,0x35,
0x32,0x41,0x55,0x2c,0x0d,0x30,0x78,0x30,0x43,0x30,0x34,0x30,0x34,0x30,0x38,0x55,0x2c,0x20,0x30,0x78,0x35,0x32,0x43,0x37,0x43,0x37,0x39,0x35,0x55,0x2c,0x20,0x30,
0x78,0x36,0x35,0x32,0x33,0x32,0x33,0x34,0x36,0x55,0x2c,0x20,0x30,0x78,0x35,0x45,0x43,0x33,0x43,0x33,0x39,0x44,0x55,0x2c,0x0d,0x30,0x78,0x32,0x38,0x31,0x38,0x31,
0x38,0x33,0x30,0x55,0x2c,0x20,0x30,0x78,0x41,0x31,0x39,0x36,0x39,0x36,0x33,0x37,0x55,0x2c,0x20,0x30,0x78,0x30,0x46,0x30,0x35,0x30,0x35,0x30,0x41,0x55,0x2c,0x20,
0x30,0x78,0x42,0x35,0x39,0x41,0x39,0x41,0x32,0x46,0x55,0x2c,0x0d,0x30,0x78,0x30,0x39,0x30,0x37,0x30,0x37,0x30,0x45,0x55,0x2c,0x20,0x30,0x78,0x33,0x36,0x31,0x32,
0x31,0x32,0x32,0x34,0x55,0x2c,0x20,0x30,0x78,0x39,0x42,0x38,0x30,0x38,0x30,0x31,0x42,0x55,0x2c,0x20,0x30,0x78,0x33,0x44,0x45,0x32,0x45,0x32,0x44,0x46,0x55,0x2c,
0x0d,0x30,0x78,0x32,0x36,0x45,0x42,0x45,0x42,0x43,0x44,0x55,0x2c,0x20,0x30,0x78,0x36,0x39,0x32,0x37,0x32,0x37,0x34,0x45,0x55,0x2c,0x20,0x30,0x78,0x43,0x44,0x42,
0x32,0x42,0x32,0x37,0x46,0x55,0x2c,0x20,0x30,0x78,0x39,0x46,0x37,0x35,0x37,0x35,0x45,0x41,0x55,0x2c,0x0d,0x30,0x78,0x31,0x42,0x30,0x39,0x30,0x39,0x31,0x32,0x55,
0x2c,0x20,0x30,0x78,0x39,0x45,0x38,0x33,0x38,0x33,0x31,0x44,0x55,0x2c,0x20,0x30,0x78,0x37,0x34,0x32,0x43,0x32,0x43,0x35,0x38,0x55,0x2c,0x20,0x30,0x78,0x32,0x45,
0x31,0x41,0x31,0x41,0x33,0x34,0x55,0x2c,0x0d,0x30,0x78,0x32,0x44,0x31,0x42,0x31,0x42,0x33,0x36,0x55,0x2c,0x20,0x30,0x78,0x42,0x32,0x36,0x45,0x36,0x45,0x44,0x43,
0x55,0x2c,0x20,0x30,0x78,0x45,0x45,0x35,0x41,0x35,0x41,0x42,0x34,0x55,0x2c,0x20,0x30,0x78,0x46,0x42,0x41,0x30,0x41,0x30,0x35,0x42,0x55,0x2c,0x0d,0x30,0x78,0x46,
0x36,0x35,0x32,0x35,0x32,0x41,0x34,0x55,0x2c,0x20,0x30,0x78,0x34,0x44,0x33,0x42,0x33,0x42,0x37,0x36,0x55,0x2c,0x20,0x30,0x78,0x36,0x31,0x44,0x36,0x44,0x36,0x42,
0x37,0x55,0x2c,0x20,0x30,0x78,0x43,0x45,0x42,0x33,0x42,0x33,0x37,0x44,0x55,0x2c,0x0d,0x30,0x78,0x37,0x42,0x32,0x39,0x32,0x39,0x35,0x32,0x55,0x2c,0x20,0x30,0x78,
0x33,0x45,0x45,0x33,0x45,0x33,0x44,0x44,0x55,0x2c,0x20,0x30,0x78,0x37,0x31,0x32,0x46,0x32,0x46,0x35,0x45,0x55,0x2c,0x20,0x30,0x78,0x39,0x37,0x38,0x34,0x38,0x34,
0x31,0x33,0x55,0x2c,0x0d,0x30,0x78,0x46,0x35,0x35,0x33,0x35,0x33,0x41,0x36,0x55,0x2c,0x20,0x30,0x78,0x36,0x38,0x44,0x31,0x44,0x31,0x42,0x39,0x55,0x2c,0x20,0x30,
0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x55,0x2c,0x20,0x30,0x78,0x32,0x43,0x45,0x44,0x45,0x44,0x43,0x31,0x55,0x2c,0x0d,0x30,0x78,0x36,0x30,0x32,0x30,0x32,
0x30,0x34,0x30,0x55,0x2c,0x20,0x30,0x78,0x31,0x46,0x46,0x43,0x46,0x43,0x45,0x33,0x55,0x2c,0x20,0x30,0x78,0x43,0x38,0x42,0x31,0x42,0x31,0x37,0x39,0x55,0x2c,0x20,
0x30,0x78,0x45,0x44,0x35,0x42,0x35,0x42,0x42,0x36,0x55,0x2c,0x0d,0x30,0x78,0x42,0x45,0x36,0x41,0x36,0x41,0x44,0x34,0x55,0x2c,0x20,0x30,0x78,0x34,0x36,0x43,0x42,
0x43,0x42,0x38,0x44,0x55,0x2c,0x20,0x30,0x78,0x44,0x39,0x42,0x45,0x42,0x45,0x36,0x37,0x55,0x2c,0x20,0x30,0x78,0x34,0x42,0x33,0x39,0x33,0x39,0x37,0x32,0x55,0x2c,
0x0d,0x30,0x78,0x44,0x45,0x34,0x41,0x34,0x41,0x39,0x34,0x55,0x2c,0x20,0x30,0x78,0x44,0x34,0x34,0x43,0x34,0x43,0x39,0x38,0x55,0x2c,0x20,0x30,0x78,0x45,0x38,0x35,
0x38,0x35,0x38,0x42,0x30,0x55,0x2c,0x20,0x30,0x78,0x34,0x41,0x43,0x46,0x43,0x46,0x38,0x35,0x55,0x2c,0x0d,0x30,0x78,0x36,0x42,0x44,0x30,0x44,0x30,0x42,0x42,0x55,
0x2c,0x20,0x30,0x78,0x32,0x41,0x45,0x46,0x45,0x46,0x43,0x35,0x55,0x2c,0x20,0x30,0x78,0x45,0x35,0x41,0x41,0x41,0x41,0x34,0x46,0x55,0x2c,0x20,0x30,0x78,0x31,0x36,
0x46,0x42,0x46,0x42,0x45,0x44,0x55,0x2c,0x0d,0x30,0x78,0x43,0x35,0x34,0x33,0x34,0x33,0x38,0x36,0x55,0x2c,0x20,0x30,0x78,0x44,0x37,0x34,0x44,0x34,0x44,0x39,0x41,
0x55,0x2c,0x20,0x30,0x78,0x35,0x35,0x33,0x33,0x33,0x33,0x36,0x36,0x55,0x2c,0x20,0x30,0x78,0x39,0x34,0x38,0x35,0x38,0x35,0x31,0x31,0x55,0x2c,0x0d,0x30,0x78,0x43,
0x46,0x34,0x35,0x34,0x35,0x38,0x41,0x55,0x2c,0x20,0x30,0x78,0x31,0x30,0x46,0x39,0x46,0x39,0x45,0x39,0x55,0x2c,0x20,0x30,0x78,0x30,0x36,0x30,0x32,0x30,0x32,0x30,
0x34,0x55,0x2c,0x20,0x30,0x78,0x38,0x31,0x37,0x46,0x37,0x46,0x46,0x45,0x55,0x2c,0x0d,0x30,0x78,0x46,0x30,0x35,0x30,0x35,0x30,0x41,0x30,0x55,0x2c,0x20,0x30,0x78,
0x34,0x34,0x33,0x43,0x33,0x43,0x37,0x38,0x55,0x2c,0x20,0x30,0x78,0x42,0x41,0x39,0x46,0x39,0x46,0x32,0x35,0x55,0x2c,0x20,0x30,0x78,0x45,0x33,0x41,0x38,0x41,0x38,
0x34,0x42,0x55,0x2c,0x0d,0x30,0x78,0x46,0x33,0x35,0x31,0x35,0x31,0x41,0x32,0x55,0x2c,0x20,0x30,0x78,0x46,0x45,0x41,0x33,0x41,0x33,0x35,0x44,0x55,0x2c,0x20,0x30,
0x78,0x43,0x30,0x34,0x30,0x34,0x30,0x38,0x30,0x55,0x2c,0x20,0x30,0x78,0x38,0x41,0x38,0x46,0x38,0x46,0x30,0x35,0x55,0x2c,0x0d,0x30,0x78,0x41,0x44,0x39,0x32,0x39,
0x32,0x33,0x46,0x55,0x2c,0x20,0x30,0x78,0x42,0x43,0x39,0x44,0x39,0x44,0x32,0x31,0x55,0x2c,0x20,0x30,0x78,0x34,0x38,0x33,0x38,0x33,0x38,0x37,0x30,0x55,0x2c,0x20,
0x30,0x78,0x30,0x34,0x46,0x35,0x46,0x35,0x46,0x31,0x55,0x2c,0x0d,0x30,0x78,0x44,0x46,0x42,0x43,0x42,0x43,0x36,0x33,0x55,0x2c,0x20,0x30,0x78,0x43,0x31,0x42,0x36,
0x42,0x36,0x37,0x37,0x55,0x2c,0x20,0x30,0x78,0x37,0x35,0x44,0x41,0x44,0x41,0x41,0x46,0x55,0x2c,0x20,0x30,0x78,0x36,0x33,0x32,0x31,0x32,0x31,0x34,0x32,0x55,0x2c,
0x0d,0x30,0x78,0x33,0x30,0x31,0x30,0x31,0x30,0x32,0x30,0x55,0x2c,0x20,0x30,0x78,0x31,0x41,0x46,0x46,0x46,0x46,0x45,0x35,0x55,0x2c,0x20,0x30,0x78,0x30,0x45,0x46,
0x33,0x46,0x33,0x46,0x44,0x55,0x2c,0x20,0x30,0x78,0x36,0x44,0x44,0x32,0x44,0x32,0x42,0x46,0x55,0x2c,0x0d,0x30,0x78,0x34,0x43,0x43,0x44,0x43,0x44,0x38,0x31,0x55,
0x2c,0x20,0x30,0x78,0x31,0x34,0x30,0x43,0x30,0x43,0x31,0x38,0x55,0x2c,0x20,0x30,0x78,0x33,0x35,0x31,0x33,0x31,0x33,0x32,0x36,0x55,0x2c,0x20,0x30,0x78,0x32,0x46,
0x45,0x43,0x45,0x43,0x43,0x33,0x55,0x2c,0x0d,0x30,0x78,0x45,0x31,0x35,0x46,0x35,0x46,0x42,0x45,0x55,0x2c,0x20,0x30,0x78,0x41,0x32,0x39,0x37,0x39,0x37,0x33,0x35,
0x55,0x2c,0x20,0x30,0x78,0x43,0x43,0x34,0x34,0x34,0x34,0x38,0x38,0x55,0x2c,0x20,0x30,0x78,0x33,0x39,0x31,0x37,0x31,0x37,0x32,0x45,0x55,0x2c,0x0d,0x30,0x78,0x35,
0x37,0x43,0x34,0x43,0x34,0x39,0x33,0x55,0x2c,0x20,0x30,0x78,0x46,0x32,0x41,0x37,0x41,0x37,0x35,0x35,0x55,0x2c,0x20,0x30,0x78,0x38,0x32,0x37,0x45,0x37,0x45,0x46,
0x43,0x55,0x2c,0x20,0x30,0x78,0x34,0x37,0x33,0x44,0x33,0x44,0x37,0x41,0x55,0x2c,0x0d,0x30,0x78,0x41,0x43,0x36,0x34,0x36,0x34,0x43,0x38,0x55,0x2c,0x20,0x30,0x78,
0x45,0x37,0x35,0x44,0x35,0x44,0x42,0x41,0x55,0x2c,0x20,0x30,0x78,0x32,0x42,0x31,0x39,0x31,0x39,0x33,0x32,0x55,0x2c,0x20,0x30,0x78,0x39,0x35,0x37,0x33,0x37,0x33,
0x45,0x36,0x55,0x2c,0x0d,0x30,0x78,0x41,0x30,0x36,0x30,0x36,0x30,0x43,0x30,0x55,0x2c,0x20,0x30,0x78,0x39,0x38,0x38,0x31,0x38,0x31,0x31,0x39,0x55,0x2c,0x20,0x30,
0x78,0x44,0x31,0x34,0x46,0x34,0x46,0x39,0x45,0x55,0x2c,0x20,0x30,0x78,0x37,0x46,0x44,0x43,0x44,0x43,0x41,0x33,0x55,0x2c,0x0d,0x30,0x78,0x36,0x36,0x32,0x32,0x32,
0x32,0x34,0x34,0x55,0x2c,0x20,0x30,0x78,0x37,0x45,0x32,0x41,0x32,0x41,0x35,0x34,0x55,0x2c,0x20,0x30,0x78,0x41,0x42,0x39,0x30,0x39,0x30,0x33,0x42,0x55,0x2c,0x20,
0x30,0x78,0x38,0x33,0x38,0x38,0x38,0x38,0x30,0x42,0x55,0x2c,0x0d,0x30,0x78,0x43,0x41,0x34,0x36,0x34,0x36,0x38,0x43,0x55,0x2c,0x20,0x30,0x78,0x32,0x39,0x45,0x45,
0x45,0x45,0x43,0x37,0x55,0x2c,0x20,0x30,0x78,0x44,0x33,0x42,0x38,0x42,0x38,0x36,0x42,0x55,0x2c,0x20,0x30,0x78,0x33,0x43,0x31,0x34,0x31,0x34,0x32,0x38,0x55,0x2c,
0x0d,0x30,0x78,0x37,0x39,0x44,0x45,0x44,0x45,0x41,0x37,0x55,0x2c,0x20,0x30,0x78,0x45,0x32,0x35,0x45,0x35,0x45,0x42,0x43,0x55,0x2c,0x20,0x30,0x78,0x31,0x44,0x30,
0x42,0x30,0x42,0x31,0x36,0x55,0x2c,0x20,0x30,0x78,0x37,0x36,0x44,0x42,0x44,0x42,0x41,0x44,0x55,0x2c,0x0d,0x30,0x78,0x33,0x42,0x45,0x30,0x45,0x30,0x44,0x42,0x55,
0x2c,0x20,0x30,0x78,0x35,0x36,0x33,0x32,0x33,0x32,0x36,0x34,0x55,0x2c,0x20,0x30,0x78,0x34,0x45,0x33,0x41,0x33,0x41,0x37,0x34,0x55,0x2c,0x20,0x30,0x78,0x31,0x45,
0x30,0x41,0x30,0x41,0x31,0x34,0x55,0x2c,0x0d,0x30,0x78,0x44,0x42,0x34,0x39,0x34,0x39,0x39,0x32,0x55,0x2c,0x20,0x30,0x78,0x30,0x41,0x30,0x36,0x30,0x36,0x30,0x43,
0x55,0x2c,0x20,0x30,0x78,0x36,0x43,0x32,0x34,0x32,0x34,0x34,0x38,0x55,0x2c,0x20,0x30,0x78,0x45,0x34,0x35,0x43,0x35,0x43,0x42,0x38,0x55,0x2c,0x0d,0x30,0x78,0x35,
0x44,0x43,0x32,0x43,0x32,0x39,0x46,0x55,0x2c,0x20,0x30,0x78,0x36,0x45,0x44,0x33,0x44,0x33,0x42,0x44,0x55,0x2c,0x20,0x30,0x78,0x45,0x46,0x41,0x43,0x41,0x43,0x34,
0x33,0x55,0x2c,0x20,0x30,0x78,0x41,0x36,0x36,0x32,0x36,0x32,0x43,0x34,0x55,0x2c,0x0d,0x30,0x78,0x41,0x38,0x39,0x31,0x39,0x31,0x33,0x39,0x55,0x2c,0x20,0x30,0x78,
0x41,0x34,0x39,0x35,0x39,0x35,0x33,0x31,0x55,0x2c,0x20,0x30,0x78,0x33,0x37,0x45,0x34,0x45,0x34,0x44,0x33,0x55,0x2c,0x20,0x30,0x78,0x38,0x42,0x37,0x39,0x37,0x39,
0x46,0x32,0x55,0x2c,0x0d,0x30,0x78,0x33,0x32,0x45,0x37,0x45,0x37,0x44,0x35,0x55,0x2c,0x20,0x30,0x78,0x34,0x33,0x43,0x38,0x43,0x38,0x38,0x42,0x55,0x2c,0x20,0x30,
0x78,0x35,0x39,0x33,0x37,0x33,0x37,0x36,0x45,0x55,0x2c,0x20,0x30,0x78,0x42,0x37,0x36,0x44,0x36,0x44,0x44,0x41,0x55,0x2c,0x0d,0x30,0x78,0x38,0x43,0x38,0x44,0x38,
0x44,0x30,0x31,0x55,0x2c,0x20,0x30,0x78,0x36,0x34,0x44,0x35,0x44,0x35,0x42,0x31,0x55,0x2c,0x20,0x30,0x78,0x44,0x32,0x34,0x45,0x34,0x45,0x39,0x43,0x55,0x2c,0x20,
0x30,0x78,0x45,0x30,0x41,0x39,0x41,0x39,0x34,0x39,0x55,0x2c,0x0d,0x30,0x78,0x42,0x34,0x36,0x43,0x36,0x43,0x44,0x38,0x55,0x2c,0x20,0x30,0x78,0x46,0x41,0x35,0x36,
0x35,0x36,0x41,0x43,0x55,0x2c,0x20,0x30,0x78,0x30,0x37,0x46,0x34,0x46,0x34,0x46,0x33,0x55,0x2c,0x20,0x30,0x78,0x32,0x35,0x45,0x41,0x45,0x41,0x43,0x46,0x55,0x2c,
0x0d,0x30,0x78,0x41,0x46,0x36,0x35,0x36,0x35,0x43,0x41,0x55,0x2c,0x20,0x30,0x78,0x38,0x45,0x37,0x41,0x37,0x41,0x46,0x34,0x55,0x2c,0x20,0x30,0x78,0x45,0x39,0x41,
0x45,0x41,0x45,0x34,0x37,0x55,0x2c,0x20,0x30,0x78,0x31,0x38,0x30,0x38,0x30,0x38,0x31,0x30,0x55,0x2c,0x0d,0x30,0x78,0x44,0x35,0x42,0x41,0x42,0x41,0x36,0x46,0x55,
0x2c,0x20,0x30,0x78,0x38,0x38,0x37,0x38,0x37,0x38,0x46,0x30,0x55,0x2c,0x20,0x30,0x78,0x36,0x46,0x32,0x35,0x32,0x35,0x34,0x41,0x55,0x2c,0x20,0x30,0x78,0x37,0x32,
0x32,0x45,0x32,0x45,0x35,0x43,0x55,0x2c,0x0d,0x30,0x78,0x32,0x34,0x31,0x43,0x31,0x43,0x33,0x38,0x55,0x2c,0x20,0x30,0x78,0x46,0x31,0x41,0x36,0x41,0x36,0x35,0x37,
0x55,0x2c,0x20,0x30,0x78,0x43,0x37,0x42,0x34,0x42,0x34,0x37,0x33,0x55,0x2c,0x20,0x30,0x78,0x35,0x31,0x43,0x36,0x43,0x36,0x39,0x37,0x55,0x2c,0x0d,0x30,0x78,0x32,
0x33,0x45,0x38,0x45,0x38,0x43,0x42,0x55,0x2c,0x20,0x30,0x78,0x37,0x43,0x44,0x44,0x44,0x44,0x41,0x31,0x55,0x2c,0x20,0x30,0x78,0x39,0x43,0x37,0x34,0x37,0x34,0x45,
0x38,0x55,0x2c,0x20,0x30,0x78,0x32,0x31,0x31,0x46,0x31,0x46,0x33,0x45,0x55,0x2c,0x0d,0x30,0x78,0x44,0x44,0x34,0x42,0x34,0x42,0x39,0x36,0x55,0x2c,0x20,0x30,0x78,
0x44,0x43,0x42,0x44,0x42,0x44,0x36,0x31,0x55,0x2c,0x20,0x30,0x78,0x38,0x36,0x38,0x42,0x38,0x42,0x30,0x44,0x55,0x2c,0x20,0x30,0x78,0x38,0x35,0x38,0x41,0x38,0x41,
0x30,0x46,0x55,0x2c,0x0d,0x30,0x78,0x39,0x30,0x37,0x30,0x37,0x30,0x45,0x30,0x55,0x2c,0x20,0x30,0x78,0x34,0x32,0x33,0x45,0x33,0x45,0x37,0x43,0x55,0x2c,0x20,0x30,
0x78,0x43,0x34,0x42,0x35,0x42,0x35,0x37,0x31,0x55,0x2c,0x20,0x30,0x78,0x41,0x41,0x36,0x36,0x36,0x36,0x43,0x43,0x55,0x2c,0x0d,0x30,0x78,0x44,0x38,0x34,0x38,0x34,
0x38,0x39,0x30,0x55,0x2c,0x20,0x30,0x78,0x30,0x35,0x30,0x33,0x30,0x33,0x30,0x36,0x55,0x2c,0x20,0x30,0x78,0x30,0x31,0x46,0x36,0x46,0x36,0x46,0x37,0x55,0x2c,0x20,
0x30,0x78,0x31,0x32,0x30,0x45,0x30,0x45,0x31,0x43,0x55,0x2c,0x0d,0x30,0x78,0x41,0x33,0x36,0x31,0x36,0x31,0x43,0x32,0x55,0x2c,0x20,0x30,0x78,0x35,0x46,0x33,0x35,
0x33,0x35,0x36,0x41,0x55,0x2c,0x20,0x30,0x78,0x46,0x39,0x35,0x37,0x35,0x37,0x41,0x45,0x55,0x2c,0x20,0x30,0x78,0x44,0x30,0x42,0x39,0x42,0x39,0x36,0x39,0x55,0x2c,
0x0d,0x30,0x78,0x39,0x31,0x38,0x36,0x38,0x36,0x31,0x37,0x55,0x2c,0x20,0x30,0x78,0x35,0x38,0x43,0x31,0x43,0x31,0x39,0x39,0x55,0x2c,0x20,0x30,0x78,0x32,0x37,0x31,
0x44,0x31,0x44,0x33,0x41,0x55,0x2c,0x20,0x30,0x78,0x42,0x39,0x39,0x45,0x39,0x45,0x32,0x37,0x55,0x2c,0x0d,0x30,0x78,0x33,0x38,0x45,0x31,0x45,0x31,0x44,0x39,0x55,
0x2c,0x20,0x30,0x78,0x31,0x33,0x46,0x38,0x46,0x38,0x45,0x42,0x55,0x2c,0x20,0x30,0x78,0x42,0x33,0x39,0x38,0x39,0x38,0x32,0x42,0x55,0x2c,0x20,0x30,0x78,0x33,0x33,
0x31,0x31,0x31,0x31,0x32,0x32,0x55,0x2c,0x0d,0x30,0x78,0x42,0x42,0x36,0x39,0x36,0x39,0x44,0x32,0x55,0x2c,0x20,0x30,0x78,0x37,0x30,0x44,0x39,0x44,0x39,0x41,0x39,
0x55,0x2c,0x20,0x30,0x78,0x38,0x39,0x38,0x45,0x38,0x45,0x30,0x37,0x55,0x2c,0x20,0x30,0x78,0x41,0x37,0x39,0x34,0x39,0x34,0x33,0x33,0x55,0x2c,0x0d,0x30,0x78,0x42,
0x36,0x39,0x42,0x39,0x42,0x32,0x44,0x55,0x2c,0x20,0x30,0x78,0x32,0x32,0x31,0x45,0x31,0x45,0x33,0x43,0x55,0x2c,0x20,0x30,0x78,0x39,0x32,0x38,0x37,0x38,0x37,0x31,
0x35,0x55,0x2c,0x20,0x30,0x78,0x32,0x30,0x45,0x39,0x45,0x39,0x43,0x39,0x55,0x2c,0x0d,0x30,0x78,0x34,0x39,0x43,0x45,0x43,0x45,0x38,0x37,0x55,0x2c,0x20,0x30,0x78,
0x46,0x46,0x35,0x35,0x35,0x35,0x41,0x41,0x55,0x2c,0x20,0x30,0x78,0x37,0x38,0x32,0x38,0x32,0x38,0x35,0x30,0x55,0x2c,0x20,0x30,0x78,0x37,0x41,0x44,0x46,0x44,0x46,
0x41,0x35,0x55,0x2c,0x0d,0x30,0x78,0x38,0x46,0x38,0x43,0x38,0x43,0x30,0x33,0x55,0x2c,0x20,0x30,0x78,0x46,0x38,0x41,0x31,0x41,0x31,0x35,0x39,0x55,0x2c,0x20,0x30,
0x78,0x38,0x30,0x38,0x39,0x38,0x39,0x30,0x39,0x55,0x2c,0x20,0x30,0x78,0x31,0x37,0x30,0x44,0x30,0x44,0x31,0x41,0x55,0x2c,0x0d,0x30,0x78,0x44,0x41,0x42,0x46,0x42,
0x46,0x36,0x35,0x55,0x2c,0x20,0x30,0x78,0x33,0x31,0x45,0x36,0x45,0x36,0x44,0x37,0x55,0x2c,0x20,0x30,0x78,0x43,0x36,0x34,0x32,0x34,0x32,0x38,0x34,0x55,0x2c,0x20,
0x30,0x78,0x42,0x38,0x36,0x38,0x36,0x38,0x44,0x30,0x55,0x2c,0x0d,0x30,0x78,0x43,0x33,0x34,0x31,0x34,0x31,0x38,0x32,0x55,0x2c,0x20,0x30,0x78,0x42,0x30,0x39,0x39,
0x39,0x39,0x32,0x39,0x55,0x2c,0x20,0x30,0x78,0x37,0x37,0x32,0x44,0x32,0x44,0x35,0x41,0x55,0x2c,0x20,0x30,0x78,0x31,0x31,0x30,0x46,0x30,0x46,0x31,0x45,0x55,0x2c,
0x0d,0x30,0x78,0x43,0x42,0x42,0x30,0x42,0x30,0x37,0x42,0x55,0x2c,0x20,0x30,0x78,0x46,0x43,0x35,0x34,0x35,0x34,0x41,0x38,0x55,0x2c,0x20,0x30,0x78,0x44,0x36,0x42,
0x42,0x42,0x42,0x36,0x44,0x55,0x2c,0x20,0x30,0x78,0x33,0x41,0x31,0x36,0x31,0x36,0x32,0x43,0x55,0x0d,0x7d,0x3b,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x42,
0x59,0x54,0x45,0x28,0x78,0x2c,0x20,0x79,0x29,0x20,0x28,0x78,0x6d,0x72,0x69,0x67,0x5f,0x61,0x6d,0x64,0x5f,0x62,0x66,0x65,0x28,0x28,0x78,0x29,0x2c,0x20,0x28,0x79,
0x29,0x20,0x3c,0x3c,0x20,0x33,0x55,0x2c,0x20,0x38,0x55,0x29,0x29,0x0d,0x23,0x69,0x66,0x20,0x28,0x41,0x4c,0x47,0x4f,0x20,0x3d,0x3d,0x20,0x41,0x4c,0x47,0x4f,0x5f,
0x43,0x4e,0x5f,0x48,0x45,0x41,0x56,0x59,0x5f,0x54,0x55,0x42,0x45,0x29,0x0d,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,
0x52,0x6f,0x75,0x6e,0x64,0x5f,0x62,0x69,0x74,0x74,0x75,0x62,0x65,0x32,0x28,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,
0x74,0x20,0x2a,0x41,0x45,0x53,0x30,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,
0x31,0x2c,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x78,0x2c,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x6b,0x29,0x0d,0x7b,0x0d,0x78,0x20,0x3d,0x20,0x7e,0x78,0x3b,0x0d,0x6b,
0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,
0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,
0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,0x20,
0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x78,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x6b,0x2e,0x73,0x30,0x3b,0x0d,0x6b,0x2e,0x73,0x31,0x20,0x5e,0x3d,
0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,
0x28,0x78,0x2e,0x73,0x32,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,
0x73,0x33,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x20,0x33,0x29,0x5d,0x2c,0x20,0x31,
0x36,0x55,0x29,0x3b,0x0d,0x78,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x6b,0x2e,0x73,0x31,0x3b,0x0d,0x6b,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,
0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x33,0x2c,
0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x20,0x32,0x29,
0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x20,0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x78,
0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x6b,0x2e,0x73,0x32,0x3b,0x0d,0x6b,0x2e,0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,
0x2e,0x73,0x33,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x30,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,
0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x31,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,
0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x78,0x2e,0x73,0x32,0x2c,0x20,0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,
0x6b,0x3b,0x0d,0x7d,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,0x64,0x28,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x30,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,
0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x31,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,
0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x32,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,
0x53,0x33,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x58,0x2c,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x6b,0x65,0x79,0x29,0x0d,0x7b,0x0d,
0x6b,0x65,0x79,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,
0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,0x45,0x28,
0x58,0x2e,0x73,0x32,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x33,0x29,0x5d,0x3b,
0x0d,0x6b,0x65,0x79,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,
0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,0x45,
0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x33,0x29,0x5d,
0x3b,0x0d,0x6b,0x65,0x79,0x2e,0x73,0x32,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x30,0x29,0x5d,0x20,
0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,0x54,
0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x33,0x29,
0x5d,0x3b,0x0d,0x6b,0x65,0x79,0x2e,0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x30,0x29,0x5d,
0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x32,0x5b,0x42,0x59,
0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x33,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x33,
0x29,0x5d,0x3b,0x0d,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6b,0x65,0x79,0x3b,0x0d,0x7d,0x0d,0x75,0x69,0x6e,0x74,0x34,0x20,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,
0x64,0x5f,0x54,0x77,0x6f,0x5f,0x54,0x61,0x62,0x6c,0x65,0x73,0x28,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,
0x2a,0x41,0x45,0x53,0x30,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x20,0x2a,0x41,0x45,0x53,0x31,0x2c,
0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x58,0x2c,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x6b,0x65,0x79,0x29,0x0d,0x7b,0x0d,0x6b,0x65,0x79,
0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,
0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,
0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,
0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x6b,0x65,0x79,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,
0x2e,0x73,0x31,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,
0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,
0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x6b,0x65,0x79,0x2e,0x73,0x32,0x20,
0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,
0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,
0x58,0x2e,0x73,0x30,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x33,0x29,0x5d,0x2c,
0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x6b,0x65,0x79,0x2e,0x73,0x33,0x20,0x5e,0x3d,0x20,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x33,0x2c,
0x20,0x30,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x30,0x2c,0x20,0x31,0x29,0x5d,0x20,0x5e,0x20,0x72,0x6f,0x74,
0x61,0x74,0x65,0x28,0x41,0x45,0x53,0x30,0x5b,0x42,0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x31,0x2c,0x20,0x32,0x29,0x5d,0x20,0x5e,0x20,0x41,0x45,0x53,0x31,0x5b,0x42,
0x59,0x54,0x45,0x28,0x58,0x2e,0x73,0x32,0x2c,0x20,0x33,0x29,0x5d,0x2c,0x20,0x31,0x36,0x55,0x29,0x3b,0x0d,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6b,0x65,0x79,0x3b,
0x0d,0x7d,0x0d,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x75,0x63,0x68,0x61,0x72,
0x20,0x72,0x63,0x6f,0x6e,0x5b,0x38,0x5d,0x20,0x3d,0x20,0x7b,0x20,0x30,0x78,0x38,0x64,0x2c,0x20,0x30,0x78,0x30,0x31,0x2c,0x20,0x30,0x78,0x30,0x32,0x2c,0x20,0x30,
0x78,0x30,0x34,0x2c,0x20,0x30,0x78,0x30,0x38,0x2c,0x20,0x30,0x78,0x31,0x30,0x2c,0x20,0x30,0x78,0x32,0x30,0x2c,0x20,0x30,0x78,0x34,0x30,0x20,0x7d,0x3b,0x0d,0x73,
0x74,0x61,0x74,0x69,0x63,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x75,0x63,0x68,0x61,0x72,0x20,0x73,0x62,0x6f,
0x78,0x5b,0x32,0x35,0x36,0x5d,0x20,0x3d,0x0d,0x7b,0x0d,0x30,0x78,0x36,0x33,0x2c,0x20,0x30,0x78,0x37,0x43,0x2c,0x20,0x30,0x78,0x37,0x37,0x2c,0x20,0x30,0x78,0x37,
0x42,0x2c,0x20,0x30,0x78,0x46,0x32,0x2c,0x20,0x30,0x78,0x36,0x42,0x2c,0x20,0x30,0x78,0x36,0x46,0x2c,0x20,0x30,0x78,0x43,0x35,0x2c,0x20,0x30,0x78,0x33,0x30,0x2c,
0x20,0x30,0x78,0x30,0x31,0x2c,0x20,0x30,0x78,0x36,0x37,0x2c,0x20,0x30,0x78,0x32,0x42,0x2c,0x20,0x30,0x78,0x46,0x45,0x2c,0x20,0x30,0x78,0x44,0x37,0x2c,0x20,0x30,
0x78,0x41,0x42,0x2c,0x20,0x30,0x78,0x37,0x36,0x2c,0x0d,0x30,0x78,0x43,0x41,0x2c,0x20,0x30,0x78,0x38,0x32,0x2c,0x20,0x30,0x78,0x43,0x39,0x2c,0x20,0x30,0x78,0x37,
0x44,0x2c,0x20,0x30,0x78,0x46,0x41,0x2c,0x20,0x30,0x78,0x35,0x39,0x2c,0x20,0x30,0x78,0x34,0x37,0x2c,0x20,0x30,0x78,0x46,0x30,0x2c,0x20,0x30,0x78,0x41,0x44,0x2c,
0x20,0x30,0x78,0x44,0x34,0x2c,0x20,0x30,0x78,0x41,0x32,0x2c,0x20,0x30,0x78,0x41,0x46,0x2c,0x20,0x30,0x78,0x39,0x43,0x2c,0x20,0x30,0x78,0x41,0x34,0x2c,0x20,0x30,
0x78,0x37,0x32,0x2c,0x20,0x30,0x78,0x43,0x30,0x2c,0x0d,0x30,0x78,0x42,0x37,0x2c,0x20,0x30,0x78,0x46,0x44,0x2c,0x20,0x30,0x78,0x39,0x33,0x2c,0x20,0x30,0x78,0x32,
0x36,0x2c,0x20,0x30,0x78,0x33,0x36,0x2c,0x20,0x30,0x78,0x33,0x46,0x2c,0x20,0x30,0x78,0x46,0x37,0x2c,0x20,0x30,0x78,0x43,0x43,0x2c,0x20,0x30,0x78,0x33,0x34,0x2c,
0x20,0x30,0x78,0x41,0x35,0x2c,0x20,0x30,0x78,0x45,0x35,0x2c,0x20,0x30,0x78,0x46,0x31,0x2c,0x20,0x30,0x78,0x37,0x31,0x2c,0x20,0x30,0x78,0x44,0x38,0x2c,0x20,0x30,
0x78,0x33,0x31,0x2c,0x20,0x30,0x78,0x31,0x35,0x2c,0x0d,0x30,0x78,0x30,0x34,0x2c,0x20,0x30,0x78,0x43,0x37,0x2c,0x20,0x30,0x78,0x32,0x33,0x2c,0x20,0x30,0x78,0x43,
0x33,0x2c,0x20,0x30,0x78,0x31,0x38,0x2c,0x20,0x30,0x78,0x39,0x36,0x2c,0x20,0x30,0x78,0x30,0x35,0x2c,0x20,0x30,0x78,0x39,0x41,0x2c,0x20,0x30,0x78,0x30,0x37,0x2c,
0x20,0x30,0x78,0x31,0x32,0x2c,0x20,0x30,0x78,0x38,0x30,0x2c,0x20,0x30,0x78,0x45,0x32,0x2c,0x20,0x30,0x78,0x45,0x42,0x2c,0x20,0x30,0x78,0x32,0x37,0x2c,0x20,0x30,
0x78,0x42,0x32,0x2c,0x20,0x30,0x78,0x37,0x35,0x2c,0x0d,0x30,0x78,0x30,0x39,0x2c,0x20,0x30,0x78,0x38,0x33,0x2c,0x20,0x30,0x78,0x32,0x43,0x2c,0x20,0x30,0x78,0x31,
0x41,0x2c,0x20,0x30,0x78,0x31,0x42,0x2c,0x20,0x30,0x78,0x36,0x45,0x2c,0x20,0x30,0x78,0x35,0x41,0x2c,0x20,0x30,0x78,0x41,0x30,0x2c,0x20,0x30,0x78,0x35,0x32,0x2c,
0x20,0x30,0x78,0x33,0x42,0x2c,0x20,0x30,0x78,0x44,0x36,0x2c,0x20,0x30,0x78,0x42,0x33,0x2c,0x20,0x30,0x78,0x32,0x39,0x2c,0x20,0x30,0x78,0x45,0x33,0x2c,0x20,0x30,
0x78,0x32,0x46,0x2c,0x20,0x30,0x78,0x38,0x34,0x2c,0x0d,0x30,0x78,0x35,0x33,0x2c,0x20,0x30,0x78,0x44,0x31,0x2c,0x20,0x30,0x78,0x30,0x30,0x2c,0x20,0x30,0x78,0x45,
0x44,0x2c,0x20,0x30,0x78,0x32,0x30,0x2c,0x20,0x30,0x78,0x46,0x43,0x2c,0x20,0x30,0x78,0x42,0x31,0x2c,0x20,0x30,0x78,0x35,0x42,0x2c,0x20,0x30,0x78,0x36,0x41,0x2c,
0x20,0x30,0x78,0x43,0x42,0x2c,0x20,0x30,0x78,0x42,0x45,0x2c,0x20,0x30,0x78,0x33,0x39,0x2c,0x20,0x30,0x78,0x34,0x41,0x2c,0x20,0x30,0x78,0x34,0x43,0x2c,0x20,0x30,
0x78,0x35,0x38,0x2c,0x20,0x30,0x78,0x43,0x46,0x2c,0x0d,0x30,0x78,0x44,0x30,0x2c,0x20,0x30,0x78,0x45,0x46,0x2c,0x20,0x30,0x78,0x41,0x41,0x2c,0x20,0x30,0x78,0x46,
0x42,0x2c,0x20,0x30,0x78,0x34,0x33,0x2c,0x20,0x30,0x78,0x34,0x44,0x2c,0x20,0x30,0x78,0x33,0x33,0x2c,0x20,0x30,0x78,0x38,0x35,0x2c,0x20,0x30,0x78,0x34,0x35,0x2c,
0x20,0x30,0x78,0x46,0x39,0x2c,0x20,0x30,0x78,0x30,0x32,0x2c,0x20,0x30,0x78,0x37,0x46,0x2c,0x20,0x30,0x78,0x35,0x30,0x2c,0x20,0x30,0x78,0x33,0x43,0x2c,0x20,0x30,
0x78,0x39,0x46,0x2c,0x20,0x30,0x78,0x41,0x38,0x2c,0x0d,0x30,0x78,0x35,0x31,0x2c,0x20,0x30,0x78,0x41,0x33,0x2c,0x20,0x30,0x78,0x34,0x30,0x2c,0x20,0x30,0x78,0x38,
0x46,0x2c,0x20,0x30,0x78,0x39,0x32,0x2c,0x20,0x30,0x78,0x39,0x44,0x2c,0x20,0x30,0x78,0x33,0x38,0x2c,0x20,0x30,0x78,0x46,0x35,0x2c,0x20,0x30,0x78,0x42,0x43,0x2c,
0x20,0x30,0x78,0x42,0x36,0x2c,0x20,0x30,0x78,0x44,0x41,0x2c,0x20,0x30,0x78,0x32,0x31,0x2c,0x20,0x30,0x78,0x31,0x30,0x2c,0x20,0x30,0x78,0x46,0x46,0x2c,0x20,0x30,
0x78,0x46,0x33,0x2c,0x20,0x30,0x78,0x44,0x32,0x2c,0x0d,0x30,0x78,0x43,0x44,0x2c,0x20,0x30,0x78,0x30,0x43,0x2c,0x20,0x30,0x78,0x31,0x33,0x2c,0x20,0x30,0x78,0x45,
0x43,0x2c,0x20,0x30,0x78,0x35,0x46,0x2c,0x20,0x30,0x78,0x39,0x37,0x2c,0x20,0x30,0x78,0x34,0x34,0x2c,0x20,0x30,0x78,0x31,0x37,0x2c,0x20,0x30,0x78,0x43,0x34,0x2c,
0x20,0x30,0x78,0x41,0x37,0x2c,0x20,0x30,0x78,0x37,0x45,0x2c,0x20,0x30,0x78,0x33,0x44,0x2c,0x20,0x30,0x78,0x36,0x34,0x2c,0x20,0x30,0x78,0x35,0x44,0x2c,0x20,0x30,
0x78,0x31,0x39,0x2c,0x20,0x30,0x78,0x37,0x33,0x2c,0x0d,0x30,0x78,0x36,0x30,0x2c,0x20,0x30,0x78,0x38,0x31,0x2c,0x20,0x30,0x78,0x34,0x46,0x2c,0x20,0x30,0x78,0x44,
0x43,0x2c,0x20,0x30,0x78,0x32,0x32,0x2c,0x20,0x30,0x78,0x32,0x41,0x2c,0x20,0x30,0x78,0x39,0x30,0x2c,0x20,0x30,0x78,0x38,0x38,0x2c,0x20,0x30,0x78,0x34,0x36,0x2c,
0x20,0x30,0x78,0x45,0x45,0x2c,0x20,0x30,0x78,0x42,0x38,0x2c,0x20,0x30,0x78,0x31,0x34,0x2c,0x20,0x30,0x78,0x44,0x45,0x2c,0x20,0x30,0x78,0x35,0x45,0x2c,0x20,0x30,
0x78,0x30,0x42,0x2c,0x20,0x30,0x78,0x44,0x42,0x2c,0x0d,0x30,0x78,0x45,0x30,0x2c,0x20,0x30,0x78,0x33,0x32,0x2c,0x20,0x30,0x78,0x33,0x41,0x2c,0x20,0x30,0x78,0x30,
0x41,0x2c,0x20,0x30,0x78,0x34,0x39,0x2c,0x20,0x30,0x78,0x30,0x36,0x2c,0x20,0x30,0x78,0x32,0x34,0x2c,0x20,0x30,0x78,0x35,0x43,0x2c,0x20,0x30,0x78,0x43,0x32,0x2c,
0x20,0x30,0x78,0x44,0x33,0x2c,0x20,0x30,0x78,0x41,0x43,0x2c,0x20,0x30,0x78,0x36,0x32,0x2c,0x20,0x30,0x78,0x39,0x31,0x2c,0x20,0x30,0x78,0x39,0x35,0x2c,0x20,0x30,
0x78,0x45,0x34,0x2c,0x20,0x30,0x78,0x37,0x39,0x2c,0x0d,0x30,0x78,0x45,0x37,0x2c,0x20,0x30,0x78,0x43,0x38,0x2c,0x20,0x30,0x78,0x33,0x37,0x2c,0x20,0x30,0x78,0x36,
0x44,0x2c,0x20,0x30,0x78,0x38,0x44,0x2c,0x20,0x30,0x78,0x44,0x35,0x2c,0x20,0x30,0x78,0x34,0x45,0x2c,0x20,0x30,0x78,0x41,0x39,0x2c,0x20,0x30,0x78,0x36,0x43,0x2c,
0x20,0x30,0x78,0x35,0x36,0x2c,0x20,0x30,0x78,0x46,0x34,0x2c,0x20,0x30,0x78,0x45,0x41,0x2c,0x20,0x30,0x78,0x36,0x35,0x2c,0x20,0x30,0x78,0x37,0x41,0x2c,0x20,0x30,
0x78,0x41,0x45,0x2c,0x20,0x30,0x78,0x30,0x38,0x2c,0x0d,0x30,0x78,0x42,0x41,0x2c,0x20,0x30,0x78,0x37,0x38,0x2c,0x20,0x30,0x78,0x32,0x35,0x2c,0x20,0x30,0x78,0x32,
0x45,0x2c,0x20,0x30,0x78,0x31,0x43,0x2c,0x20,0x30,0x78,0x41,0x36,0x2c,0x20,0x30,0x78,0x42,0x34,0x2c,0x20,0x30,0x78,0x43,0x36,0x2c,0x20,0x30,0x78,0x45,0x38,0x2c,
0x20,0x30,0x78,0x44,0x44,0x2c,0x20,0x30,0x78,0x37,0x34,0x2c,0x20,0x30,0x78,0x31,0x46,0x2c,0x20,0x30,0x78,0x34,0x42,0x2c,0x20,0x30,0x78,0x42,0x44,0x2c,0x20,0x30,
0x78,0x38,0x42,0x2c,0x20,0x30,0x78,0x38,0x41,0x2c,0x0d,0x30,0x78,0x37,0x30,0x2c,0x20,0x30,0x78,0x33,0x45,0x2c,0x20,0x30,0x78,0x42,0x35,0x2c,0x20,0x30,0x78,0x36,
0x36,0x2c,0x20,0x30,0x78,0x34,0x38,0x2c,0x20,0x30,0x78,0x30,0x33,0x2c,0x20,0x30,0x78,0x46,0x36,0x2c,0x20,0x30,0x78,0x30,0x45,0x2c,0x20,0x30,0x78,0x36,0x31,0x2c,
0x20,0x30,0x78,0x33,0x35,0x2c,0x20,0x30,0x78,0x35,0x37,0x2c,0x20,0x30,0x78,0x42,0x39,0x2c,0x20,0x30,0x78,0x38,0x36,0x2c,0x20,0x30,0x78,0x43,0x31,0x2c,0x20,0x30,
0x78,0x31,0x44,0x2c,0x20,0x30,0x78,0x39,0x45,0x2c,0x0d,0x30,0x78,0x45,0x31,0x2c,0x20,0x30,0x78,0x46,0x38,0x2c,0x20,0x30,0x78,0x39,0x38,0x2c,0x20,0x30,0x78,0x31,
0x31,0x2c,0x20,0x30,0x78,0x36,0x39,0x2c,0x20,0x30,0x78,0x44,0x39,0x2c,0x20,0x30,0x78,0x38,0x45,0x2c,0x20,0x30,0x78,0x39,0x34,0x2c,0x20,0x30,0x78,0x39,0x42,0x2c,
0x20,0x30,0x78,0x31,0x45,0x2c,0x20,0x30,0x78,0x38,0x37,0x2c,0x20,0x30,0x78,0x45,0x39,0x2c,0x20,0x30,0x78,0x43,0x45,0x2c,0x20,0x30,0x78,0x35,0x35,0x2c,0x20,0x30,
0x78,0x32,0x38,0x2c,0x20,0x30,0x78,0x44,0x46,0x2c,0x0d,0x30,0x78,0x38,0x43,0x2c,0x20,0x30,0x78,0x41,0x31,0x2c,0x20,0x30,0x78,0x38,0x39,0x2c,0x20,0x30,0x78,0x30,
0x44,0x2c,0x20,0x30,0x78,0x42,0x46,0x2c,0x20,0x30,0x78,0x45,0x36,0x2c,0x20,0x30,0x78,0x34,0x32,0x2c,0x20,0x30,0x78,0x36,0x38,0x2c,0x20,0x30,0x78,0x34,0x31,0x2c,
0x20,0x30,0x78,0x39,0x39,0x2c,0x20,0x30,0x78,0x32,0x44,0x2c,0x20,0x30,0x78,0x30,0x46,0x2c,0x20,0x30,0x78,0x42,0x30,0x2c,0x20,0x30,0x78,0x35,0x34,0x2c,0x20,0x30,
0x78,0x42,0x42,0x2c,0x20,0x30,0x78,0x31,0x36,0x0d,0x7d,0x3b,0x0d,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x53,0x75,0x62,0x57,0x6f,0x72,0x64,0x28,0x69,0x6e,0x77,
0x29,0x20,0x28,0x28,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x33,0x29,0x5d,0x20,0x3c,0x3c,0x20,0x32,0x34,0x29,0x20,0x7c,0x20,
0x28,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x32,0x29,0x5d,0x20,0x3c,0x3c,0x20,0x31,0x36,0x29,0x20,0x7c,0x20,0x28,0x73,0x62,
0x6f,0x78,0x5b,0x42,0x59,0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x31,0x29,0x5d,0x20,0x3c,0x3c,0x20,0x38,0x29,0x20,0x7c,0x20,0x73,0x62,0x6f,0x78,0x5b,0x42,0x59,
0x54,0x45,0x28,0x69,0x6e,0x77,0x2c,0x20,0x30,0x29,0x5d,0x29,0x0d,0x76,0x6f,0x69,0x64,0x20,0x41,0x45,0x53,0x45,0x78,0x70,0x61,0x6e,0x64,0x4b,0x65,0x79,0x32,0x35,
0x36,0x28,0x75,0x69,0x6e,0x74,0x20,0x2a,0x6b,0x65,0x79,0x62,0x75,0x66,0x29,0x0d,0x7b,0x0d,0x66,0x6f,0x72,0x20,0x28,0x75,0x69,0x6e,0x74,0x20,0x63,0x20,0x3d,0x20,
0x38,0x2c,0x20,0x69,0x20,0x3d,0x20,0x31,0x3b,0x20,0x63,0x20,0x3c,0x20,0x34,0x30,0x3b,0x20,0x2b,0x2b,0x63,0x29,0x20,0x7b,0x0d,0x75,0x69,0x6e,0x74,0x20,0x74,0x20,
0x3d,0x20,0x28,0x28,0x21,0x28,0x63,0x20,0x26,0x20,0x37,0x29,0x29,0x20,0x7c,0x7c,0x20,0x28,0x28,0x63,0x20,0x26,0x20,0x37,0x29,0x20,0x3d,0x3d,0x20,0x34,0x29,0x29,
0x20,0x3f,0x20,0x53,0x75,0x62,0x57,0x6f,0x72,0x64,0x28,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x20,0x2d,0x20,0x31,0x5d,0x29,0x20,0x3a,0x20,0x6b,0x65,0x79,0x62,
0x75,0x66,0x5b,0x63,0x20,0x2d,0x20,0x31,0x5d,0x3b,0x0d,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x5d,0x20,0x3d,0x20,0x6b,0x65,0x79,0x62,0x75,0x66,0x5b,0x63,0x20,
0x2d,0x20,0x38,0x5d,0x20,0x5e,0x20,0x28,0x28,0x21,0x28,0x63,0x20,0x26,0x20,0x37,0x29,0x29,0x20,0x3f,0x20,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x74,0x2c,0x20,0x32,
0x34,0x55,0x29,0x20,0x5e,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x28,0x28,0x75,0x63,0x68,0x61,0x72,0x34,0x29,0x28,0x72,0x63,0x6f,0x6e,0x5b,0x69,0x2b,0x2b,0x5d,
0x2c,0x20,0x30,0x55,0x2c,0x20,0x30,0x55,0x2c,0x20,0x30,0x55,0x29,0x29,0x20,0x3a,0x20,0x74,0x29,0x3b,0x0d,0x7d,0x0d,0x7d,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,
0x00
};
static char cryptonight_r_cl[3424] = {
static char cryptonight_r_cl[3415] = {
0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x72,0x65,0x71,0x64,0x5f,0x77,0x6f,0x72,0x6b,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,
0x69,0x7a,0x65,0x28,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x2c,0x31,0x2c,0x31,0x29,0x29,0x29,0x0a,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,
0x69,0x7a,0x65,0x28,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x2c,0x31,0x2c,0x31,0x29,0x29,0x29,0x0d,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,
0x64,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x4e,0x41,0x4d,0x45,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x2a,0x69,0x6e,
0x70,0x75,0x74,0x2c,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x34,0x20,0x2a,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x2c,0x5f,
0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x2a,0x73,0x74,0x61,0x74,0x65,0x73,0x2c,0x75,0x69,0x6e,0x74,0x20,0x54,0x68,0x72,0x65,0x61,
0x64,0x73,0x29,0x0a,0x7b,0x0a,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x61,0x5b,0x32,0x5d,0x2c,0x62,0x5b,0x34,0x5d,0x3b,0x0a,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,
0x64,0x73,0x29,0x0d,0x7b,0x0d,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x61,0x5b,0x32,0x5d,0x2c,0x62,0x5b,0x34,0x5d,0x3b,0x0d,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,
0x69,0x6e,0x74,0x20,0x41,0x45,0x53,0x30,0x5b,0x32,0x35,0x36,0x5d,0x2c,0x41,0x45,0x53,0x31,0x5b,0x32,0x35,0x36,0x5d,0x2c,0x41,0x45,0x53,0x32,0x5b,0x32,0x35,0x36,
0x5d,0x2c,0x41,0x45,0x53,0x33,0x5b,0x32,0x35,0x36,0x5d,0x3b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x67,0x49,0x64,0x78,0x3d,0x67,0x65,
0x5d,0x2c,0x41,0x45,0x53,0x33,0x5b,0x32,0x35,0x36,0x5d,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x20,0x67,0x49,0x64,0x78,0x3d,0x67,0x65,
0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2d,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,
0x28,0x30,0x29,0x3b,0x0a,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x3d,0x67,0x65,0x74,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x20,
0x69,0x3c,0x32,0x35,0x36,0x3b,0x20,0x69,0x2b,0x3d,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x29,0x20,0x7b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,
0x20,0x74,0x6d,0x70,0x3d,0x41,0x45,0x53,0x30,0x5f,0x43,0x5b,0x69,0x5d,0x3b,0x0a,0x41,0x45,0x53,0x30,0x5b,0x69,0x5d,0x3d,0x74,0x6d,0x70,0x3b,0x0a,0x41,0x45,0x53,
0x31,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x74,0x6d,0x70,0x2c,0x38,0x55,0x29,0x3b,0x0a,0x41,0x45,0x53,0x32,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,
0x61,0x74,0x65,0x28,0x74,0x6d,0x70,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0a,0x41,0x45,0x53,0x33,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x74,0x6d,0x70,
0x2c,0x32,0x34,0x55,0x29,0x3b,0x0a,0x7d,0x0a,0x62,0x61,0x72,0x72,0x69,0x65,0x72,0x28,0x43,0x4c,0x4b,0x5f,0x4c,0x4f,0x43,0x41,0x4c,0x5f,0x4d,0x45,0x4d,0x5f,0x46,
0x45,0x4e,0x43,0x45,0x29,0x3b,0x0a,0x7b,0x0a,0x73,0x74,0x61,0x74,0x65,0x73,0x2b,0x3d,0x32,0x35,0x2a,0x67,0x49,0x64,0x78,0x3b,0x0a,0x23,0x69,0x66,0x20,0x64,0x65,
0x66,0x69,0x6e,0x65,0x64,0x28,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x29,0x0a,0x53,0x63,0x72,0x61,0x74,0x63,0x68,
0x70,0x61,0x64,0x2b,0x3d,0x67,0x49,0x64,0x78,0x2a,0x28,0x49,0x54,0x45,0x52,0x41,0x54,0x49,0x4f,0x4e,0x53,0x3e,0x3e,0x32,0x29,0x3b,0x0a,0x23,0x65,0x6c,0x73,0x65,
0x0a,0x23,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x30,0x29,0x0a,0x53,0x63,0x72,0x61,0x74,0x63,
0x68,0x70,0x61,0x64,0x2b,0x3d,0x67,0x49,0x64,0x78,0x2a,0x28,0x4d,0x45,0x4d,0x4f,0x52,0x59,0x3e,0x3e,0x34,0x29,0x3b,0x0a,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,
0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x31,0x29,0x0a,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x2b,0x3d,0x67,
0x49,0x64,0x78,0x3b,0x0a,0x23,0x65,0x6c,0x69,0x66,0x20,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x20,0x3d,0x3d,0x20,0x32,0x29,0x0a,
0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x2b,0x3d,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x2a,0x28,0x4d,0x45,0x4d,
0x4f,0x52,0x59,0x3e,0x3e,0x34,0x29,0x2a,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x2b,0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x2a,0x67,0x65,0x74,0x5f,0x6c,
0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x61,0x5b,0x30,0x5d,0x3d,0x73,
0x74,0x61,0x74,0x65,0x73,0x5b,0x30,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x34,0x5d,0x3b,0x0a,0x61,0x5b,0x31,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,
0x31,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x35,0x5d,0x3b,0x0a,0x62,0x5b,0x30,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x32,0x5d,0x5e,0x73,0x74,0x61,
0x74,0x65,0x73,0x5b,0x36,0x5d,0x3b,0x0a,0x62,0x5b,0x31,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x33,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x37,0x5d,
0x3b,0x0a,0x62,0x5b,0x32,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x38,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x30,0x5d,0x3b,0x0a,0x62,0x5b,0x33,
0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x39,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x31,0x5d,0x3b,0x0a,0x7d,0x0a,0x75,0x6c,0x6f,0x6e,0x67,0x32,
0x20,0x62,0x78,0x30,0x3d,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x62,0x29,0x5b,0x30,0x5d,0x3b,0x0a,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x62,0x78,
0x31,0x3d,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x62,0x29,0x5b,0x31,0x5d,0x3b,0x0a,0x6d,0x65,0x6d,0x5f,0x66,0x65,0x6e,0x63,0x65,0x28,0x43,0x4c,
0x4b,0x5f,0x4c,0x4f,0x43,0x41,0x4c,0x5f,0x4d,0x45,0x4d,0x5f,0x46,0x45,0x4e,0x43,0x45,0x29,0x3b,0x0a,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,
0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0a,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x20,0x73,0x63,0x72,0x61,
0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x5f,0x62,0x75,0x66,0x5b,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x5d,0x3b,0x0a,0x5f,0x5f,0x6c,0x6f,0x63,
0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x20,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,0x73,0x63,0x72,0x61,0x74,
0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x5f,0x62,0x75,0x66,0x2b,0x67,0x65,0x74,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x0a,
0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x7b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x72,0x30,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,0x73,
0x5b,0x31,0x32,0x5d,0x29,0x2e,0x73,0x30,0x3b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x72,0x31,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,
0x73,0x5b,0x31,0x32,0x5d,0x29,0x2e,0x73,0x31,0x3b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x72,0x32,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,
0x65,0x73,0x5b,0x31,0x33,0x5d,0x29,0x2e,0x73,0x30,0x3b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x72,0x33,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,
0x74,0x65,0x73,0x5b,0x31,0x33,0x5d,0x29,0x2e,0x73,0x31,0x3b,0x0a,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x20,0x43,0x4e,0x5f,0x55,
0x4e,0x52,0x4f,0x4c,0x4c,0x0a,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x3d,0x30,0x3b,0x20,0x69,0x3c,0x49,0x54,0x45,0x52,0x41,0x54,0x49,0x4f,0x4e,0x53,
0x3b,0x20,0x2b,0x2b,0x69,0x29,0x20,0x7b,0x0a,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,
0x4e,0x0a,0x75,0x69,0x6e,0x74,0x20,0x69,0x64,0x78,0x3d,0x61,0x5b,0x30,0x5d,0x26,0x30,0x78,0x31,0x46,0x46,0x46,0x43,0x30,0x3b,0x0a,0x75,0x69,0x6e,0x74,0x20,0x69,
0x64,0x78,0x31,0x3d,0x61,0x5b,0x30,0x5d,0x26,0x30,0x78,0x33,0x30,0x3b,0x0a,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,
0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,
0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3b,0x0a,0x23,0x65,0x6c,0x73,0x65,0x0a,0x75,0x69,
0x6e,0x74,0x20,0x69,0x64,0x78,0x3d,0x61,0x5b,0x30,0x5d,0x26,0x4d,0x41,0x53,0x4b,0x3b,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x75,0x69,0x6e,0x74,0x34,0x20,0x63,
0x3d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3b,0x0a,0x63,0x3d,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,
0x64,0x28,0x41,0x45,0x53,0x30,0x2c,0x41,0x45,0x53,0x31,0x2c,0x41,0x45,0x53,0x32,0x2c,0x41,0x45,0x53,0x33,0x2c,0x63,0x2c,0x28,0x28,0x75,0x69,0x6e,0x74,0x34,0x20,
0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x29,0x3b,0x0a,0x7b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x31,0x3d,
0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x29,0x3b,0x0a,
0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x32,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,
0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x29,0x3b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,
0x20,0x63,0x68,0x75,0x6e,0x6b,0x33,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,
0x4e,0x4b,0x28,0x33,0x29,0x29,0x3b,0x0a,0x63,0x20,0x5e,0x3d,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x29,0x5e,0x61,0x73,
0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x29,0x3b,
0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,
0x75,0x6e,0x6b,0x33,0x2b,0x62,0x78,0x31,0x29,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x3d,0x61,
0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x2b,0x62,0x78,0x30,0x29,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,
0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x2b,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,
0x32,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x29,0x3b,0x0a,0x7d,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,
0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x30,0x29,0x5e,0x63,0x3b,0x0a,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,
0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0a,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,
0x28,0x30,0x29,0x3b,0x0d,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x3d,0x67,0x65,0x74,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x20,
0x69,0x3c,0x32,0x35,0x36,0x3b,0x20,0x69,0x2b,0x3d,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x29,0x20,0x7b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,
0x20,0x74,0x6d,0x70,0x3d,0x41,0x45,0x53,0x30,0x5f,0x43,0x5b,0x69,0x5d,0x3b,0x0d,0x41,0x45,0x53,0x30,0x5b,0x69,0x5d,0x3d,0x74,0x6d,0x70,0x3b,0x0d,0x41,0x45,0x53,
0x31,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x74,0x6d,0x70,0x2c,0x38,0x55,0x29,0x3b,0x0d,0x41,0x45,0x53,0x32,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,
0x61,0x74,0x65,0x28,0x74,0x6d,0x70,0x2c,0x31,0x36,0x55,0x29,0x3b,0x0d,0x41,0x45,0x53,0x33,0x5b,0x69,0x5d,0x3d,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x74,0x6d,0x70,
0x2c,0x32,0x34,0x55,0x29,0x3b,0x0d,0x7d,0x0d,0x62,0x61,0x72,0x72,0x69,0x65,0x72,0x28,0x43,0x4c,0x4b,0x5f,0x4c,0x4f,0x43,0x41,0x4c,0x5f,0x4d,0x45,0x4d,0x5f,0x46,
0x45,0x4e,0x43,0x45,0x29,0x3b,0x0d,0x7b,0x0d,0x73,0x74,0x61,0x74,0x65,0x73,0x2b,0x3d,0x32,0x35,0x2a,0x67,0x49,0x64,0x78,0x3b,0x0d,0x23,0x69,0x66,0x20,0x64,0x65,
0x66,0x69,0x6e,0x65,0x64,0x28,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x29,0x0d,0x53,0x63,0x72,0x61,0x74,0x63,0x68,
0x70,0x61,0x64,0x2b,0x3d,0x67,0x49,0x64,0x78,0x2a,0x28,0x49,0x54,0x45,0x52,0x41,0x54,0x49,0x4f,0x4e,0x53,0x3e,0x3e,0x32,0x29,0x3b,0x0d,0x23,0x65,0x6c,0x73,0x65,
0x0d,0x23,0x69,0x66,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x3d,0x3d,0x30,0x29,0x0d,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,
0x64,0x2b,0x3d,0x67,0x49,0x64,0x78,0x2a,0x28,0x4d,0x45,0x4d,0x4f,0x52,0x59,0x3e,0x3e,0x34,0x29,0x3b,0x0d,0x23,0x65,0x6c,0x69,0x66,0x28,0x53,0x54,0x52,0x49,0x44,
0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x3d,0x3d,0x31,0x29,0x0d,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x2b,0x3d,0x67,0x49,0x64,0x78,0x3b,0x0d,0x23,
0x65,0x6c,0x69,0x66,0x28,0x53,0x54,0x52,0x49,0x44,0x45,0x44,0x5f,0x49,0x4e,0x44,0x45,0x58,0x3d,0x3d,0x32,0x29,0x0d,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,
0x64,0x2b,0x3d,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x2a,0x28,0x4d,0x45,0x4d,0x4f,0x52,0x59,0x3e,0x3e,0x34,0x29,0x2a,0x57,
0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x2b,0x4d,0x45,0x4d,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x2a,0x67,0x65,0x74,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,
0x29,0x3b,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x61,0x5b,0x30,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x30,0x5d,0x5e,
0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x34,0x5d,0x3b,0x0d,0x61,0x5b,0x31,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,
0x5b,0x35,0x5d,0x3b,0x0d,0x62,0x5b,0x30,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x32,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x36,0x5d,0x3b,0x0d,0x62,
0x5b,0x31,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x33,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x37,0x5d,0x3b,0x0d,0x62,0x5b,0x32,0x5d,0x3d,0x73,0x74,
0x61,0x74,0x65,0x73,0x5b,0x38,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x30,0x5d,0x3b,0x0d,0x62,0x5b,0x33,0x5d,0x3d,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,
0x39,0x5d,0x5e,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x31,0x5d,0x3b,0x0d,0x7d,0x0d,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x62,0x78,0x30,0x3d,0x28,0x28,0x75,0x6c,
0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x62,0x29,0x5b,0x30,0x5d,0x3b,0x0d,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x62,0x78,0x31,0x3d,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,
0x32,0x20,0x2a,0x29,0x62,0x29,0x5b,0x31,0x5d,0x3b,0x0d,0x6d,0x65,0x6d,0x5f,0x66,0x65,0x6e,0x63,0x65,0x28,0x43,0x4c,0x4b,0x5f,0x4c,0x4f,0x43,0x41,0x4c,0x5f,0x4d,
0x45,0x4d,0x5f,0x46,0x45,0x4e,0x43,0x45,0x29,0x3b,0x0d,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,
0x49,0x4f,0x4e,0x0d,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x20,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,
0x6e,0x65,0x5f,0x62,0x75,0x66,0x5b,0x57,0x4f,0x52,0x4b,0x53,0x49,0x5a,0x45,0x5d,0x3b,0x0d,0x5f,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,
0x2a,0x20,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,
0x65,0x5f,0x62,0x75,0x66,0x2b,0x67,0x65,0x74,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x7b,0x0d,
0x75,0x69,0x6e,0x74,0x20,0x72,0x30,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x32,0x5d,0x29,0x2e,0x73,0x30,0x3b,
0x0d,0x75,0x69,0x6e,0x74,0x20,0x72,0x31,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x32,0x5d,0x29,0x2e,0x73,0x31,
0x3b,0x0d,0x75,0x69,0x6e,0x74,0x20,0x72,0x32,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x33,0x5d,0x29,0x2e,0x73,
0x30,0x3b,0x0d,0x75,0x69,0x6e,0x74,0x20,0x72,0x33,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x73,0x74,0x61,0x74,0x65,0x73,0x5b,0x31,0x33,0x5d,0x29,0x2e,
0x73,0x31,0x3b,0x0d,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x20,0x43,0x4e,0x5f,0x55,0x4e,0x52,0x4f,0x4c,0x4c,0x0d,0x66,0x6f,0x72,
0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x3d,0x30,0x3b,0x20,0x69,0x3c,0x49,0x54,0x45,0x52,0x41,0x54,0x49,0x4f,0x4e,0x53,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x20,0x7b,0x0d,
0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0d,0x75,0x69,0x6e,0x74,0x20,0x69,0x64,
0x78,0x3d,0x61,0x5b,0x30,0x5d,0x26,0x30,0x78,0x31,0x46,0x46,0x46,0x43,0x30,0x3b,0x0d,0x75,0x69,0x6e,0x74,0x20,0x69,0x64,0x78,0x31,0x3d,0x61,0x5b,0x30,0x5d,0x26,
0x30,0x78,0x33,0x30,0x3b,0x0d,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,
0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,
0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3b,0x0d,0x23,0x65,0x6c,0x73,0x65,0x0d,0x75,0x69,0x6e,0x74,0x20,0x69,0x64,0x78,0x3d,0x61,0x5b,
0x30,0x5d,0x26,0x4d,0x41,0x53,0x4b,0x3b,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x75,0x69,0x6e,0x74,0x34,0x20,0x63,0x3d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,
0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3b,0x0d,0x63,0x3d,0x41,0x45,0x53,0x5f,0x52,0x6f,0x75,0x6e,0x64,0x28,0x41,0x45,0x53,0x30,0x2c,0x41,0x45,
0x53,0x31,0x2c,0x41,0x45,0x53,0x32,0x2c,0x41,0x45,0x53,0x33,0x2c,0x63,0x2c,0x28,0x28,0x75,0x69,0x6e,0x74,0x34,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x29,0x3b,
0x0d,0x7b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,
0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x29,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,
0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x32,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,
0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x29,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x33,0x3d,0x61,
0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x29,0x3b,0x0d,0x63,
0x20,0x5e,0x3d,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,
0x75,0x6e,0x6b,0x32,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x29,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,
0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x2b,0x62,0x78,0x31,0x29,
0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,
0x68,0x75,0x6e,0x6b,0x31,0x2b,0x62,0x78,0x30,0x29,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x3d,
0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x2b,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,
0x29,0x3b,0x0d,0x7d,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,
0x34,0x28,0x62,0x78,0x30,0x29,0x5e,0x63,0x3b,0x0d,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,
0x4f,0x4e,0x0d,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,
0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3d,0x2a,0x73,0x63,0x72,0x61,0x74,
0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3b,0x0d,0x69,0x64,0x78,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,
0x30,0x78,0x31,0x46,0x46,0x46,0x43,0x30,0x3b,0x0d,0x69,0x64,0x78,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,0x30,
0x78,0x33,0x30,0x3b,0x0d,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,
0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,
0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3b,0x0d,0x23,0x65,0x6c,0x73,0x65,0x0d,0x69,0x64,0x78,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,
0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,0x4d,0x41,0x53,0x4b,0x3b,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x75,0x69,0x6e,0x74,0x34,0x20,0x74,0x6d,0x70,0x3d,0x53,
0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3b,0x0d,0x74,0x6d,0x70,0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x72,0x30,
0x2b,0x72,0x31,0x3b,0x0d,0x74,0x6d,0x70,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x72,0x32,0x2b,0x72,0x33,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,
0x20,0x72,0x34,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,0x2e,0x73,0x30,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,
0x74,0x20,0x72,0x35,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x31,0x5d,0x29,0x2e,0x73,0x30,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,
0x6e,0x74,0x20,0x72,0x36,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x30,0x29,0x2e,0x73,0x30,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,
0x6e,0x74,0x20,0x72,0x37,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x31,0x29,0x2e,0x73,0x30,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,
0x6e,0x74,0x20,0x72,0x38,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x31,0x29,0x2e,0x73,0x32,0x3b,0x0d,0x58,0x4d,0x52,0x49,0x47,0x5f,0x49,0x4e,
0x43,0x4c,0x55,0x44,0x45,0x5f,0x52,0x41,0x4e,0x44,0x4f,0x4d,0x5f,0x4d,0x41,0x54,0x48,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x32,0x20,0x61,0x6c,
0x3d,0x28,0x75,0x69,0x6e,0x74,0x32,0x29,0x28,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,0x2e,0x73,0x30,0x5e,0x72,0x32,0x2c,0x61,0x73,
0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,0x2e,0x73,0x31,0x5e,0x72,0x33,0x29,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x32,
0x20,0x61,0x68,0x3d,0x28,0x75,0x69,0x6e,0x74,0x32,0x29,0x28,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x31,0x5d,0x29,0x2e,0x73,0x30,0x5e,0x72,0x30,
0x2c,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x31,0x5d,0x29,0x2e,0x73,0x31,0x5e,0x72,0x31,0x29,0x3b,0x0d,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x74,
0x3b,0x0d,0x74,0x2e,0x73,0x30,0x3d,0x6d,0x75,0x6c,0x5f,0x68,0x69,0x28,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x2c,0x61,0x73,
0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x74,0x6d,0x70,0x29,0x2e,0x73,0x30,0x29,0x3b,0x0d,0x74,0x2e,0x73,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,
0x28,0x63,0x29,0x2e,0x73,0x30,0x2a,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x74,0x6d,0x70,0x29,0x2e,0x73,0x30,0x3b,0x0d,0x7b,0x0d,0x63,0x6f,0x6e,0x73,
0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,
0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x29,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,
0x6e,0x6b,0x32,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,
0x29,0x29,0x3b,0x0d,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x33,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,
0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x29,0x3b,0x0d,0x63,0x20,0x5e,0x3d,0x20,0x61,0x73,0x5f,
0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x29,0x5e,0x61,
0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x29,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,
0x4b,0x28,0x31,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x2b,0x62,0x78,0x31,0x29,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,
0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x2b,0x62,
0x78,0x30,0x29,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,
0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x2b,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x29,0x3b,0x0d,0x7d,0x0d,0x61,0x5b,
0x31,0x5d,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x28,0x61,0x68,0x29,0x2b,0x74,0x2e,0x73,0x31,0x3b,0x0d,0x61,0x5b,0x30,0x5d,0x3d,0x61,0x73,0x5f,0x75,0x6c,
0x6f,0x6e,0x67,0x28,0x61,0x6c,0x29,0x2b,0x74,0x2e,0x73,0x30,0x3b,0x0d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,
0x29,0x3d,0x28,0x28,0x75,0x69,0x6e,0x74,0x34,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x3b,0x0d,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,
0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0d,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,
0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,
0x78,0x29,0x3d,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3b,0x0a,0x69,0x64,0x78,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,
0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,0x30,0x78,0x31,0x46,0x46,0x46,0x43,0x30,0x3b,0x0a,0x69,0x64,0x78,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,
0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,0x30,0x78,0x33,0x30,0x3b,0x0a,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3d,0x2a,
0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,
0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3b,0x0a,0x23,0x65,0x6c,0x73,0x65,0x0a,0x69,0x64,0x78,
0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x26,0x4d,0x41,0x53,0x4b,0x3b,0x0a,0x23,0x65,0x6e,0x64,0x69,0x66,0x0a,0x75,0x69,
0x6e,0x74,0x34,0x20,0x74,0x6d,0x70,0x3d,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3b,0x0a,0x74,0x6d,0x70,
0x2e,0x73,0x30,0x20,0x5e,0x3d,0x20,0x72,0x30,0x2b,0x72,0x31,0x3b,0x0a,0x74,0x6d,0x70,0x2e,0x73,0x31,0x20,0x5e,0x3d,0x20,0x72,0x32,0x2b,0x72,0x33,0x3b,0x0a,0x63,
0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x72,0x34,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,0x2e,0x73,0x30,0x3b,0x0a,
0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x72,0x35,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x31,0x5d,0x29,0x2e,0x73,0x30,0x3b,
0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x72,0x36,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x30,0x29,0x2e,0x73,0x30,0x3b,
0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x72,0x37,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x31,0x29,0x2e,0x73,0x30,0x3b,
0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x72,0x38,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x62,0x78,0x31,0x29,0x2e,0x73,0x32,0x3b,
0x0a,0x58,0x4d,0x52,0x49,0x47,0x5f,0x49,0x4e,0x43,0x4c,0x55,0x44,0x45,0x5f,0x52,0x41,0x4e,0x44,0x4f,0x4d,0x5f,0x4d,0x41,0x54,0x48,0x0a,0x63,0x6f,0x6e,0x73,0x74,
0x20,0x75,0x69,0x6e,0x74,0x32,0x20,0x61,0x6c,0x3d,0x28,0x75,0x69,0x6e,0x74,0x32,0x29,0x28,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,
0x2e,0x73,0x30,0x5e,0x72,0x32,0x2c,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x30,0x5d,0x29,0x2e,0x73,0x31,0x5e,0x72,0x33,0x29,0x3b,0x0a,0x63,0x6f,
0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x32,0x20,0x61,0x68,0x3d,0x28,0x75,0x69,0x6e,0x74,0x32,0x29,0x28,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,
0x31,0x5d,0x29,0x2e,0x73,0x30,0x5e,0x72,0x30,0x2c,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x32,0x28,0x61,0x5b,0x31,0x5d,0x29,0x2e,0x73,0x31,0x5e,0x72,0x31,0x29,0x3b,
0x0a,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x74,0x3b,0x0a,0x74,0x2e,0x73,0x30,0x3d,0x6d,0x75,0x6c,0x5f,0x68,0x69,0x28,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,
0x28,0x63,0x29,0x2e,0x73,0x30,0x2c,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x74,0x6d,0x70,0x29,0x2e,0x73,0x30,0x29,0x3b,0x0a,0x74,0x2e,0x73,0x31,0x3d,
0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x2e,0x73,0x30,0x2a,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x74,0x6d,0x70,0x29,0x2e,0x73,
0x30,0x3b,0x0a,0x7b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x31,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,
0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x29,0x3b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,
0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x32,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,
0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x29,0x3b,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x63,0x68,0x75,0x6e,0x6b,0x33,
0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,0x29,0x29,0x3b,
0x0a,0x63,0x20,0x5e,0x3d,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,
0x63,0x68,0x75,0x6e,0x6b,0x32,0x29,0x5e,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x29,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,
0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x31,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x33,0x2b,0x62,0x78,
0x31,0x29,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x32,0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,
0x28,0x63,0x68,0x75,0x6e,0x6b,0x31,0x2b,0x62,0x78,0x30,0x29,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x33,
0x29,0x3d,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x63,0x68,0x75,0x6e,0x6b,0x32,0x2b,0x28,0x28,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x20,0x2a,0x29,0x61,0x29,0x5b,
0x30,0x5d,0x29,0x3b,0x0a,0x7d,0x0a,0x61,0x5b,0x31,0x5d,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x28,0x61,0x68,0x29,0x2b,0x74,0x2e,0x73,0x31,0x3b,0x0a,0x61,
0x5b,0x30,0x5d,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x28,0x61,0x6c,0x29,0x2b,0x74,0x2e,0x73,0x30,0x3b,0x0a,0x53,0x43,0x52,0x41,0x54,0x43,0x48,0x50,0x41,
0x44,0x5f,0x43,0x48,0x55,0x4e,0x4b,0x28,0x30,0x29,0x3d,0x28,0x28,0x75,0x69,0x6e,0x74,0x34,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x3b,0x0a,0x23,0x69,0x66,0x64,
0x65,0x66,0x20,0x5f,0x5f,0x4e,0x56,0x5f,0x43,0x4c,0x5f,0x43,0x5f,0x56,0x45,0x52,0x53,0x49,0x4f,0x4e,0x0a,0x2a,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,
0x75,0x69,0x6e,0x74,0x31,0x36,0x2a,0x29,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x63,0x68,0x61,0x72,0x2a,0x29,0x28,0x53,0x63,0x72,0x61,0x74,
0x63,0x68,0x70,0x61,0x64,0x29,0x2b,0x69,0x64,0x78,0x29,0x3d,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3b,0x0a,0x23,0x65,
0x6e,0x64,0x69,0x66,0x0a,0x28,0x28,0x75,0x69,0x6e,0x74,0x34,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x20,0x5e,0x3d,0x20,0x74,0x6d,0x70,0x3b,0x0a,0x62,0x78,0x31,
0x3d,0x62,0x78,0x30,0x3b,0x0a,0x62,0x78,0x30,0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x3b,0x0a,0x7d,0x0a,0x7d,0x0a,0x6d,0x65,0x6d,0x5f,
0x66,0x65,0x6e,0x63,0x65,0x28,0x43,0x4c,0x4b,0x5f,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x4d,0x45,0x4d,0x5f,0x46,0x45,0x4e,0x43,0x45,0x29,0x3b,0x0a,0x7d,0x0a,0x00
0x78,0x29,0x3d,0x2a,0x73,0x63,0x72,0x61,0x74,0x63,0x68,0x70,0x61,0x64,0x5f,0x6c,0x69,0x6e,0x65,0x3b,0x0d,0x23,0x65,0x6e,0x64,0x69,0x66,0x0d,0x28,0x28,0x75,0x69,
0x6e,0x74,0x34,0x20,0x2a,0x29,0x61,0x29,0x5b,0x30,0x5d,0x20,0x5e,0x3d,0x20,0x74,0x6d,0x70,0x3b,0x0d,0x62,0x78,0x31,0x3d,0x62,0x78,0x30,0x3b,0x0d,0x62,0x78,0x30,
0x3d,0x61,0x73,0x5f,0x75,0x6c,0x6f,0x6e,0x67,0x32,0x28,0x63,0x29,0x3b,0x0d,0x7d,0x0d,0x7d,0x0d,0x6d,0x65,0x6d,0x5f,0x66,0x65,0x6e,0x63,0x65,0x28,0x43,0x4c,0x4b,
0x5f,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x4d,0x45,0x4d,0x5f,0x46,0x45,0x4e,0x43,0x45,0x29,0x3b,0x0d,0x7d,0x0d,0x00
};
} // namespace xmrig

View file

@ -6,6 +6,8 @@
#include "randomx_constants_wow.h"
#elif (ALGO == ALGO_RX_LOKI)
#include "randomx_constants_loki.h"
#elif (ALGO == ALGO_RX_ARQMA)
#include "randomx_constants_arqma.h"
#endif
#include "aes.cl"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
/*
Copyright (c) 2019 SChernykh
This file is part of RandomX OpenCL.
RandomX OpenCL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
RandomX OpenCL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with RandomX OpenCL. If not, see <http://www.gnu.org/licenses/>.
*/
//Dataset base size in bytes. Must be a power of 2.
#define RANDOMX_DATASET_BASE_SIZE 2147483648
//Dataset extra size. Must be divisible by 64.
#define RANDOMX_DATASET_EXTRA_SIZE 33554368
//Scratchpad L3 size in bytes. Must be a power of 2.
#define RANDOMX_SCRATCHPAD_L3 262144
//Scratchpad L2 size in bytes. Must be a power of two and less than or equal to RANDOMX_SCRATCHPAD_L3.
#define RANDOMX_SCRATCHPAD_L2 131072
//Scratchpad L1 size in bytes. Must be a power of two (minimum 64) and less than or equal to RANDOMX_SCRATCHPAD_L2.
#define RANDOMX_SCRATCHPAD_L1 16384
//Jump condition mask size in bits.
#define RANDOMX_JUMP_BITS 8
//Jump condition mask offset in bits. The sum of RANDOMX_JUMP_BITS and RANDOMX_JUMP_OFFSET must not exceed 16.
#define RANDOMX_JUMP_OFFSET 8
//Integer instructions
#define RANDOMX_FREQ_IADD_RS 16
#define RANDOMX_FREQ_IADD_M 7
#define RANDOMX_FREQ_ISUB_R 16
#define RANDOMX_FREQ_ISUB_M 7
#define RANDOMX_FREQ_IMUL_R 16
#define RANDOMX_FREQ_IMUL_M 4
#define RANDOMX_FREQ_IMULH_R 4
#define RANDOMX_FREQ_IMULH_M 1
#define RANDOMX_FREQ_ISMULH_R 4
#define RANDOMX_FREQ_ISMULH_M 1
#define RANDOMX_FREQ_IMUL_RCP 8
#define RANDOMX_FREQ_INEG_R 2
#define RANDOMX_FREQ_IXOR_R 15
#define RANDOMX_FREQ_IXOR_M 5
#define RANDOMX_FREQ_IROR_R 8
#define RANDOMX_FREQ_IROL_R 2
#define RANDOMX_FREQ_ISWAP_R 4
//Floating point instructions
#define RANDOMX_FREQ_FSWAP_R 4
#define RANDOMX_FREQ_FADD_R 16
#define RANDOMX_FREQ_FADD_M 5
#define RANDOMX_FREQ_FSUB_R 16
#define RANDOMX_FREQ_FSUB_M 5
#define RANDOMX_FREQ_FSCAL_R 6
#define RANDOMX_FREQ_FMUL_R 32
#define RANDOMX_FREQ_FDIV_M 4
#define RANDOMX_FREQ_FSQRT_R 6
//Control instructions
#define RANDOMX_FREQ_CBRANCH 25
#define RANDOMX_FREQ_CFROUND 1
//Store instruction
#define RANDOMX_FREQ_ISTORE 16
//No-op instruction
#define RANDOMX_FREQ_NOP 0
#define RANDOMX_DATASET_ITEM_SIZE 64
#define RANDOMX_PROGRAM_SIZE 256
#define HASH_SIZE 64
#define ENTROPY_SIZE (128 + RANDOMX_PROGRAM_SIZE * 8)
#define REGISTERS_SIZE 256
#define IMM_BUF_SIZE (RANDOMX_PROGRAM_SIZE * 4 - REGISTERS_SIZE)
#define IMM_INDEX_COUNT ((IMM_BUF_SIZE / 4) - 2)
#define VM_STATE_SIZE (REGISTERS_SIZE + IMM_BUF_SIZE + RANDOMX_PROGRAM_SIZE * 4)
#define ROUNDING_MODE (RANDOMX_FREQ_CFROUND ? -1 : 0)
// Scratchpad L1/L2/L3 bits
#define LOC_L1 (32 - 14)
#define LOC_L2 (32 - 17)
#define LOC_L3 (32 - 18)

View file

@ -13,6 +13,7 @@ if (WITH_OPENCL)
src/backend/opencl/OclBackend.h
src/backend/opencl/OclCache.h
src/backend/opencl/OclConfig.h
src/backend/opencl/OclConfig_gen.h
src/backend/opencl/OclGenerator.h
src/backend/opencl/OclLaunchData.h
src/backend/opencl/OclThread.h

View file

@ -122,7 +122,7 @@ void xmrig::OclRxBaseRunner::set(const Job &job, uint8_t *blob)
m_seed = job.seed();
auto dataset = Rx::dataset(job, 0);
enqueueWriteBuffer(m_dataset, CL_TRUE, 0, dataset->size(), dataset->raw());
enqueueWriteBuffer(m_dataset, CL_TRUE, 0, RxDataset::maxSize(), dataset->raw());
}
if (job.size() < Job::kMaxBlobSize) {

View file

@ -98,7 +98,14 @@ elseif (APPLE)
else()
set(SOURCES_OS
src/base/io/json/Json_unix.cpp
src/base/kernel//Platform_unix.cpp
src/base/kernel/Platform_unix.cpp
)
endif()
if (WITH_HWLOC)
list(APPEND SOURCES_OS
src/base/kernel/Platform_hwloc.cpp
)
endif()

View file

@ -31,10 +31,10 @@
#include <algorithm>
#include <cstring>
#include <ctime>
#include <mutex>
#include <string.h>
#include <string>
#include <time.h>
#include <uv.h>
#include <vector>
@ -42,6 +42,7 @@
#include "base/io/log/Log.h"
#include "base/kernel/interfaces/ILogBackend.h"
#include "base/tools/Chrono.h"
#include "base/tools/Object.h"
namespace xmrig {
@ -67,10 +68,10 @@ static const char *colors_map[] = {
class LogPrivate
{
public:
inline LogPrivate() :
m_buf()
{
}
XMRIG_DISABLE_COPY_MOVE(LogPrivate)
LogPrivate() = default;
inline ~LogPrivate()
@ -134,7 +135,7 @@ private:
const uint64_t ms = Chrono::currentMSecsSinceEpoch();
time_t now = ms / 1000;
tm stime;
tm stime{};
# ifdef _WIN32
localtime_s(&stime, &now);
@ -188,7 +189,7 @@ private:
}
char m_buf[4096];
char m_buf[4096]{};
std::mutex m_mutex;
std::vector<ILogBackend*> m_backends;
};

View file

@ -47,7 +47,6 @@ xmrig::ConsoleLog::ConsoleLog()
}
uv_tty_set_mode(m_tty, UV_TTY_MODE_NORMAL);
m_stream = reinterpret_cast<uv_stream_t*>(m_tty);
# ifdef WIN32
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
@ -68,25 +67,14 @@ xmrig::ConsoleLog::~ConsoleLog()
}
void xmrig::ConsoleLog::print(int, const char *line, size_t, size_t size, bool colors)
void xmrig::ConsoleLog::print(int, const char *line, size_t, size_t, bool colors)
{
if (!m_tty || Log::colors != colors) {
return;
}
# ifdef _WIN32
uv_buf_t buf = uv_buf_init(const_cast<char *>(line), static_cast<unsigned int>(size));
# else
uv_buf_t buf = uv_buf_init(const_cast<char *>(line), size);
# endif
if (!isWritable()) {
fputs(line, stdout);
fflush(stdout);
}
else {
uv_try_write(m_stream, &buf, 1);
}
fputs(line, stdout);
fflush(stdout);
}
@ -95,13 +83,3 @@ bool xmrig::ConsoleLog::isSupported() const
const uv_handle_type type = uv_guess_handle(1);
return type == UV_TTY || type == UV_NAMED_PIPE;
}
bool xmrig::ConsoleLog::isWritable() const
{
if (!m_stream || uv_is_writable(m_stream) != 1) {
return false;
}
return isSupported();
}

View file

@ -51,10 +51,8 @@ protected:
private:
bool isSupported() const;
bool isWritable() const;
uv_stream_t *m_stream = nullptr;
uv_tty_t *m_tty = nullptr;
uv_tty_t *m_tty = nullptr;
};

View file

@ -23,7 +23,10 @@
*/
#include <string.h>
#include "base/kernel/Platform.h"
#include <cstring>
#include <uv.h>
@ -33,13 +36,14 @@
#endif
#include "Platform.h"
namespace xmrig {
String Platform::m_userAgent;
} // namespace xmrig
xmrig::String Platform::m_userAgent;
void Platform::init(const char *userAgent)
void xmrig::Platform::init(const char *userAgent)
{
# ifdef XMRIG_FEATURE_TLS
SSL_library_init();

View file

@ -26,12 +26,15 @@
#define XMRIG_PLATFORM_H
#include <stdint.h>
#include <cstdint>
#include "base/tools/String.h"
namespace xmrig {
class Platform
{
public:
@ -56,8 +59,11 @@ public:
private:
static char *createUserAgent();
static xmrig::String m_userAgent;
static String m_userAgent;
};
} // namespace xmrig
#endif /* XMRIG_PLATFORM_H */

View file

@ -0,0 +1,49 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "base/kernel/Platform.h"
#include "backend/cpu/platform/HwlocCpuInfo.h"
#include "backend/cpu/Cpu.h"
#include <hwloc.h>
bool xmrig::Platform::setThreadAffinity(uint64_t cpu_id)
{
auto cpu = static_cast<HwlocCpuInfo *>(Cpu::info());
hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(cpu->topology(), static_cast<unsigned>(cpu_id));
if (pu == nullptr) {
return false;
}
if (hwloc_set_cpubind(cpu->topology(), pu->cpuset, HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT) >= 0) {
return true;
}
return hwloc_set_cpubind(cpu->topology(), pu->cpuset, HWLOC_CPUBIND_THREAD) >= 0;
}

View file

@ -30,7 +30,7 @@
#include <uv.h>
#include "Platform.h"
#include "base/kernel/Platform.h"
#include "version.h"
#ifdef XMRIG_NVIDIA_PROJECT
@ -38,7 +38,7 @@
#endif
char *Platform::createUserAgent()
char *xmrig::Platform::createUserAgent()
{
constexpr const size_t max = 256;
@ -60,7 +60,8 @@ char *Platform::createUserAgent()
}
bool Platform::setThreadAffinity(uint64_t cpu_id)
#ifndef XMRIG_FEATURE_HWLOC
bool xmrig::Platform::setThreadAffinity(uint64_t cpu_id)
{
thread_port_t mach_thread;
thread_affinity_policy_data_t policy = { static_cast<integer_t>(cpu_id) };
@ -68,25 +69,26 @@ bool Platform::setThreadAffinity(uint64_t cpu_id)
return thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1) == KERN_SUCCESS;
}
#endif
uint32_t Platform::setTimerResolution(uint32_t resolution)
uint32_t xmrig::Platform::setTimerResolution(uint32_t resolution)
{
return resolution;
}
void Platform::restoreTimerResolution()
void xmrig::Platform::restoreTimerResolution()
{
}
void Platform::setProcessPriority(int priority)
void xmrig::Platform::setProcessPriority(int priority)
{
}
void Platform::setThreadPriority(int priority)
void xmrig::Platform::setThreadPriority(int priority)
{
if (priority == -1) {
return;

View file

@ -39,7 +39,7 @@
#include <uv.h>
#include "Platform.h"
#include "base/kernel/Platform.h"
#include "version.h"
#ifdef XMRIG_NVIDIA_PROJECT
@ -52,7 +52,7 @@ typedef cpuset_t cpu_set_t;
#endif
char *Platform::createUserAgent()
char *xmrig::Platform::createUserAgent()
{
constexpr const size_t max = 256;
@ -84,7 +84,8 @@ char *Platform::createUserAgent()
}
bool Platform::setThreadAffinity(uint64_t cpu_id)
#ifndef XMRIG_FEATURE_HWLOC
bool xmrig::Platform::setThreadAffinity(uint64_t cpu_id)
{
cpu_set_t mn;
CPU_ZERO(&mn);
@ -96,25 +97,26 @@ bool Platform::setThreadAffinity(uint64_t cpu_id)
return sched_setaffinity(gettid(), sizeof(cpu_set_t), &mn) == 0;
# endif
}
#endif
uint32_t Platform::setTimerResolution(uint32_t resolution)
uint32_t xmrig::Platform::setTimerResolution(uint32_t resolution)
{
return resolution;
}
void Platform::restoreTimerResolution()
void xmrig::Platform::restoreTimerResolution()
{
}
void Platform::setProcessPriority(int priority)
void xmrig::Platform::setProcessPriority(int priority)
{
}
void Platform::setThreadPriority(int priority)
void xmrig::Platform::setThreadPriority(int priority)
{
if (priority == -1) {
return;

View file

@ -29,8 +29,8 @@
#include <uv.h>
#include "base/kernel/Platform.h"
#include "base/io/log/Log.h"
#include "Platform.h"
#include "version.h"
@ -51,10 +51,10 @@ static inline OSVERSIONINFOEX winOsVersion()
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
if (ntdll ) {
RtlGetVersionFunction pRtlGetVersion = reinterpret_cast<RtlGetVersionFunction>(GetProcAddress(ntdll, "RtlGetVersion"));
auto pRtlGetVersion = reinterpret_cast<RtlGetVersionFunction>(GetProcAddress(ntdll, "RtlGetVersion"));
if (pRtlGetVersion) {
pRtlGetVersion((LPOSVERSIONINFO) &result);
pRtlGetVersion(reinterpret_cast<LPOSVERSIONINFO>(&result));
}
}
@ -62,7 +62,7 @@ static inline OSVERSIONINFOEX winOsVersion()
}
char *Platform::createUserAgent()
char *xmrig::Platform::createUserAgent()
{
const auto osver = winOsVersion();
constexpr const size_t max = 256;
@ -91,7 +91,8 @@ char *Platform::createUserAgent()
}
bool Platform::setThreadAffinity(uint64_t cpu_id)
#ifndef XMRIG_FEATURE_HWLOC
bool xmrig::Platform::setThreadAffinity(uint64_t cpu_id)
{
if (cpu_id >= 64) {
LOG_ERR("Unable to set affinity. Windows supports only affinity up to 63.");
@ -99,9 +100,10 @@ bool Platform::setThreadAffinity(uint64_t cpu_id)
return SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpu_id) != 0;
}
#endif
uint32_t Platform::setTimerResolution(uint32_t resolution)
uint32_t xmrig::Platform::setTimerResolution(uint32_t resolution)
{
# ifdef XMRIG_AMD_PROJECT
TIMECAPS tc;
@ -119,7 +121,7 @@ uint32_t Platform::setTimerResolution(uint32_t resolution)
}
void Platform::restoreTimerResolution()
void xmrig::Platform::restoreTimerResolution()
{
# ifdef XMRIG_AMD_PROJECT
if (timerResolution) {
@ -129,7 +131,7 @@ void Platform::restoreTimerResolution()
}
void Platform::setProcessPriority(int priority)
void xmrig::Platform::setProcessPriority(int priority)
{
if (priority == -1) {
return;
@ -166,7 +168,7 @@ void Platform::setProcessPriority(int priority)
}
void Platform::setThreadPriority(int priority)
void xmrig::Platform::setThreadPriority(int priority)
{
if (priority == -1) {
return;

View file

@ -56,7 +56,6 @@ public:
inline const String &apiId() const { return m_apiId; }
inline const String &apiWorkerId() const { return m_apiWorkerId; }
inline uint32_t printTime() const { return m_printTime; }
inline uint32_t version() const { return m_version; }
inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); }
inline const String &fileName() const override { return m_fileName; }

View file

@ -89,6 +89,7 @@ public:
RandomXInitKey = 1022,
RandomXNumaKey = 1023,
CPUMaxThreadsKey = 1026,
MemoryPoolKey = 1027,
// xmrig amd
OclPlatformKey = 1400,

View file

@ -908,8 +908,14 @@ void xmrig::Client::onConnect(uv_connect_t *req, int status)
LOG_ERR("[%s] connect error: \"%s\"", client->url(), uv_strerror(status));
}
if (client->state() == ReconnectingState) {
return;
}
if (client->state() != ConnectingState) {
LOG_ERR("[%s] connect error: \"invalid state: %d\"", client->url(), client->state());
if (!client->isQuiet()) {
LOG_ERR("[%s] connect error: \"invalid state: %d\"", client->url(), client->state());
}
return;
}

View file

@ -23,6 +23,7 @@
"huge-pages": true,
"hw-aes": null,
"priority": null,
"memory-pool": false,
"max-threads-hint": 100,
"asm": true,
"argon2-impl": null,

View file

@ -23,15 +23,17 @@
*/
#include <cassert>
#include "backend/cpu/Cpu.h"
#include "core/Controller.h"
#include "backend/cpu/Cpu.h"
#include "core/config/Config.h"
#include "core/Miner.h"
#include "crypto/common/VirtualMemory.h"
#include "net/Network.h"
#include <cassert>
xmrig::Controller::Controller(Process *process) :
Base(process)
{
@ -41,6 +43,8 @@ xmrig::Controller::Controller(Process *process) :
xmrig::Controller::~Controller()
{
delete m_network;
VirtualMemory::destroy();
}
@ -48,7 +52,10 @@ int xmrig::Controller::init()
{
Base::init();
VirtualMemory::init(config()->cpu().memPoolSize(), config()->cpu().isHugePages());
m_network = new Network(this);
return 0;
}

View file

@ -72,12 +72,8 @@ class MinerPrivate
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(MinerPrivate)
inline MinerPrivate(Controller *controller) : controller(controller)
{
# ifdef XMRIG_ALGO_RANDOMX
Rx::init();
# endif
}
inline MinerPrivate(Controller *controller) : controller(controller) {}
inline ~MinerPrivate()
@ -232,9 +228,9 @@ public:
# ifdef XMRIG_ALGO_RANDOMX
bool initRX(IRxListener *listener)
inline bool initRX()
{
return Rx::init(job, controller->config()->rx().threads(), controller->config()->cpu().isHugePages(), controller->config()->rx().isNUMA(), listener);
return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages());
}
# endif
@ -261,6 +257,10 @@ public:
xmrig::Miner::Miner(Controller *controller)
: d_ptr(new MinerPrivate(controller))
{
# ifdef XMRIG_ALGO_RANDOMX
Rx::init(this);
# endif
controller->addListener(this);
# ifdef XMRIG_FEATURE_API
@ -402,7 +402,7 @@ void xmrig::Miner::setJob(const Job &job, bool donate)
}
# ifdef XMRIG_ALGO_RANDOMX
const bool ready = d_ptr->initRX(this);
const bool ready = d_ptr->initRX();
# else
constexpr const bool ready = true;
# endif

View file

@ -33,6 +33,7 @@
#include "base/api/interfaces/IApiListener.h"
#include "base/kernel/interfaces/IBaseListener.h"
#include "base/kernel/interfaces/ITimerListener.h"
#include "base/tools/Object.h"
#include "crypto/common/Algorithm.h"
@ -48,6 +49,8 @@ class IBackend;
class Miner : public ITimerListener, public IBaseListener, public IApiListener, public IRxListener
{
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(Miner)
Miner(Controller *controller);
~Miner() override;

View file

@ -23,9 +23,9 @@
*/
#include <algorithm>
#include <string.h>
#include <cstring>
#include <uv.h>
#include <inttypes.h>
#include <cinttypes>
#include "backend/cpu/Cpu.h"
@ -50,8 +50,7 @@
namespace xmrig {
static const char *kCPU = "cpu";
static constexpr const uint32_t kVersion = 1;
static const char *kCPU = "cpu";
#ifdef XMRIG_ALGO_RANDOMX
static const char *kRandomX = "randomx";
@ -79,7 +78,7 @@ public:
}
xmrig::Config::Config() : BaseConfig(),
xmrig::Config::Config() :
d_ptr(new ConfigPrivate())
{
}
@ -119,10 +118,6 @@ bool xmrig::Config::isShouldSave() const
return false;
}
if (version() < kVersion) {
return true;
}
# ifdef XMRIG_FEATURE_OPENCL
if (cl().isShouldSave()) {
return true;
@ -139,7 +134,7 @@ bool xmrig::Config::read(const IJsonReader &reader, const char *fileName)
return false;
}
d_ptr->cpu.read(reader.getValue(kCPU), version());
d_ptr->cpu.read(reader.getValue(kCPU));
# ifdef XMRIG_ALGO_RANDOMX
if (!d_ptr->rx.read(reader.getValue(kRandomX))) {
@ -170,7 +165,6 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const
doc.AddMember("api", api, allocator);
doc.AddMember("http", m_http.toJSON(doc), allocator);
doc.AddMember("autosave", isAutoSave(), allocator);
doc.AddMember("version", kVersion, allocator);
doc.AddMember("background", isBackground(), allocator);
doc.AddMember("colors", Log::colors, allocator);

View file

@ -26,11 +26,12 @@
#define XMRIG_CONFIG_H
#include <stdint.h>
#include <cstdint>
#include "backend/cpu/CpuConfig.h"
#include "base/kernel/config/BaseConfig.h"
#include "base/tools/Object.h"
#include "rapidjson/fwd.h"
@ -46,6 +47,8 @@ class OclConfig;
class Config : public BaseConfig
{
public:
XMRIG_DISABLE_COPY_MOVE(Config);
Config();
~Config() override;

View file

@ -141,6 +141,10 @@ void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const
case IConfig::CPUMaxThreadsKey: /* --cpu-max-threads-hint */
return set(doc, kCpu, "max-threads-hint", static_cast<uint64_t>(strtol(arg, nullptr, 10)));
case IConfig::MemoryPoolKey: /* --cpu-memory-pool */
return set(doc, kCpu, "memory-pool", static_cast<int64_t>(strtol(arg, nullptr, 10)));
break;
# ifdef XMRIG_FEATURE_ASM
case IConfig::AssemblyKey: /* --asm */
return set(doc, kCpu, "asm", arg);

View file

@ -57,6 +57,7 @@ R"===(
"huge-pages": true,
"hw-aes": null,
"priority": null,
"memory-pool": false,
"max-threads-hint": 100,
"asm": true,
"argon2-impl": null,

View file

@ -84,6 +84,7 @@ static const option options[] = {
{ "no-cpu", 0, nullptr, IConfig::CPUKey },
{ "max-cpu-usage", 1, nullptr, IConfig::CPUMaxThreadsKey },
{ "cpu-max-threads-hint", 1, nullptr, IConfig::CPUMaxThreadsKey },
{ "cpu-memory-pool", 1, nullptr, IConfig::MemoryPoolKey },
# ifdef XMRIG_FEATURE_TLS
{ "tls", 0, nullptr, IConfig::TlsKey },
{ "tls-fingerprint", 1, nullptr, IConfig::FingerprintKey },

View file

@ -46,6 +46,7 @@ static inline const std::string &usage()
u += "Usage: " APP_ID " [OPTIONS]\n\nNetwork:\n";
u += " -o, --url=URL URL of mining server\n";
u += " -a, --algo=ALGO mining algorithm https://xmrig.com/docs/algorithms\n";
u += " --coin=COIN specify coin instead of algorithm\n";
u += " -u, --user=USERNAME username for mining server\n";
u += " -p, --pass=PASSWORD password for mining server\n";
u += " -O, --userpass=U:P username:password pair for mining server\n";
@ -77,6 +78,7 @@ static inline const std::string &usage()
u += " --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n";
u += " --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n";
u += " --cpu-max-threads-hint=N maximum CPU threads count (in percentage) hint for autoconfig\n";
u += " --cpu-memory-pool=N number of 2 MB pages for persistent memory pool, -1 (auto), 0 (disable)\n";
u += " --no-huge-pages disable huge pages support\n";
u += " --asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer\n";

View file

@ -110,6 +110,8 @@ static AlgoName const algorithm_names[] = {
{ "RandomWOW", nullptr, Algorithm::RX_WOW },
{ "randomx/loki", "rx/loki", Algorithm::RX_LOKI },
{ "RandomXL", nullptr, Algorithm::RX_LOKI },
{ "randomx/arq", "rx/arq", Algorithm::RX_ARQ },
{ "RandomARQ", nullptr, Algorithm::RX_ARQ },
# endif
# ifdef XMRIG_ALGO_ARGON2
{ "argon2/chukwa", nullptr, Algorithm::AR2_CHUKWA },
@ -141,6 +143,9 @@ size_t xmrig::Algorithm::l2() const
case RX_WOW:
return 0x20000;
case RX_ARQ:
return 0x10000;
default:
break;
}
@ -171,6 +176,9 @@ size_t xmrig::Algorithm::l3() const
case RX_WOW:
return oneMiB;
case RX_ARQ:
return oneMiB / 4;
default:
break;
}
@ -261,6 +269,7 @@ xmrig::Algorithm::Family xmrig::Algorithm::family(Id id)
case RX_0:
case RX_WOW:
case RX_LOKI:
case RX_ARQ:
return RANDOM_X;
# endif

View file

@ -66,6 +66,7 @@ public:
RX_0, // "rx/0" RandomX (reference configuration).
RX_WOW, // "rx/wow" RandomWOW (Wownero).
RX_LOKI, // "rx/loki" RandomXL (Loki).
RX_ARQ, // "rx/arq" RandomARQ (Arqma).
AR2_CHUKWA, // "argon2/chukwa" Argon2id (Chukwa).
AR2_WRKZ, // "argon2/wrkz" Argon2id (WRKZ)
MAX

View file

@ -49,6 +49,8 @@ struct CoinName
static CoinName const coin_names[] = {
{ "monero", Coin::MONERO },
{ "xmr", Coin::MONERO },
{ "arqma", Coin::ARQMA },
{ "arq", Coin::ARQMA }
};
@ -58,8 +60,15 @@ static CoinName const coin_names[] = {
xmrig::Algorithm::Id xmrig::Coin::algorithm(uint8_t blobVersion) const
{
if (id() == MONERO) {
switch (id()) {
case MONERO:
return (blobVersion >= 12) ? Algorithm::RX_0 : Algorithm::CN_R;
case ARQMA:
return (blobVersion >= 15) ? Algorithm::RX_ARQ : Algorithm::CN_PICO_0;
case INVALID:
break;
}
return Algorithm::INVALID;

View file

@ -40,6 +40,7 @@ public:
enum Id : int {
INVALID = -1,
MONERO,
ARQMA
};

View file

@ -0,0 +1,95 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/common/MemoryPool.h"
#include "crypto/common/VirtualMemory.h"
#include <cassert>
namespace xmrig {
constexpr size_t pageSize = 2 * 1024 * 1024;
} // namespace xmrig
xmrig::MemoryPool::MemoryPool(size_t size, bool hugePages, uint32_t node) :
m_size(size)
{
if (!size) {
return;
}
m_memory = new VirtualMemory(size * pageSize, hugePages, false, node);
}
xmrig::MemoryPool::~MemoryPool()
{
delete m_memory;
}
bool xmrig::MemoryPool::isHugePages(uint32_t) const
{
return m_memory && m_memory->isHugePages();
}
uint8_t *xmrig::MemoryPool::get(size_t size, uint32_t)
{
assert(!(size % pageSize));
if (!m_memory || (m_memory->size() - m_offset) < size) {
return nullptr;
}
uint8_t *out = m_memory->scratchpad() + m_offset;
m_offset += size;
++m_refs;
return out;
}
void xmrig::MemoryPool::release(uint32_t)
{
assert(m_refs > 0);
if (m_refs > 0) {
--m_refs;
}
if (m_refs == 0) {
m_offset = 0;
}
}

View file

@ -0,0 +1,66 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_MEMORYPOOL_H
#define XMRIG_MEMORYPOOL_H
#include "backend/common/interfaces/IMemoryPool.h"
#include "base/tools/Object.h"
namespace xmrig {
class VirtualMemory;
class MemoryPool : public IMemoryPool
{
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(MemoryPool)
MemoryPool(size_t size, bool hugePages, uint32_t node = 0);
~MemoryPool() override;
protected:
bool isHugePages(uint32_t node) const override;
uint8_t *get(size_t size, uint32_t node) override;
void release(uint32_t node) override;
private:
size_t m_size = 0;
size_t m_refs = 0;
size_t m_offset = 0;
VirtualMemory *m_memory = nullptr;
};
} /* namespace xmrig */
#endif /* XMRIG_MEMORYPOOL_H */

View file

@ -0,0 +1,106 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/common/NUMAMemoryPool.h"
#include "crypto/common/VirtualMemory.h"
#include "backend/cpu/Cpu.h"
#include "crypto/common/MemoryPool.h"
#include <algorithm>
namespace xmrig {
constexpr size_t pageSize = 2 * 1024 * 1024;
} // namespace xmrig
xmrig::NUMAMemoryPool::NUMAMemoryPool(size_t size, bool hugePages) :
m_hugePages(hugePages),
m_nodeSize(std::max<size_t>(size / Cpu::info()->nodes(), 1)),
m_size(size)
{
}
xmrig::NUMAMemoryPool::~NUMAMemoryPool()
{
for (auto kv : m_map) {
delete kv.second;
}
}
bool xmrig::NUMAMemoryPool::isHugePages(uint32_t node) const
{
if (!m_size) {
return false;
}
return getOrCreate(node)->isHugePages(node);
}
uint8_t *xmrig::NUMAMemoryPool::get(size_t size, uint32_t node)
{
if (!m_size) {
return nullptr;
}
return getOrCreate(node)->get(size, node);
}
void xmrig::NUMAMemoryPool::release(uint32_t node)
{
const auto pool = get(node);
if (pool) {
pool->release(node);
}
}
xmrig::IMemoryPool *xmrig::NUMAMemoryPool::get(uint32_t node) const
{
return m_map.count(node) ? m_map.at(node) : nullptr;
}
xmrig::IMemoryPool *xmrig::NUMAMemoryPool::getOrCreate(uint32_t node) const
{
auto pool = get(node);
if (!pool) {
pool = new MemoryPool(m_nodeSize, m_hugePages, node);
m_map.insert({ node, pool });
}
return pool;
}

View file

@ -0,0 +1,72 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_NUMAMEMORYPOOL_H
#define XMRIG_NUMAMEMORYPOOL_H
#include "backend/common/interfaces/IMemoryPool.h"
#include "base/tools/Object.h"
#include <map>
namespace xmrig {
class IMemoryPool;
class NUMAMemoryPool : public IMemoryPool
{
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(NUMAMemoryPool)
NUMAMemoryPool(size_t size, bool hugePages);
~NUMAMemoryPool() override;
protected:
bool isHugePages(uint32_t node) const override;
uint8_t *get(size_t size, uint32_t node) override;
void release(uint32_t node) override;
private:
IMemoryPool *get(uint32_t node) const;
IMemoryPool *getOrCreate(uint32_t node) const;
bool m_hugePages = true;
size_t m_nodeSize = 0;
size_t m_size = 0;
mutable std::map<uint32_t, IMemoryPool *> m_map;
};
} /* namespace xmrig */
#endif /* XMRIG_NUMAMEMORYPOOL_H */

View file

@ -25,61 +25,102 @@
*/
#include "crypto/common/VirtualMemory.h"
#include "backend/cpu/Cpu.h"
#include "base/io/log/Log.h"
#include "crypto/common/MemoryPool.h"
#include "crypto/common/portable/mm_malloc.h"
#ifdef XMRIG_FEATURE_HWLOC
# include <hwloc.h>
# include "backend/cpu/platform/HwlocCpuInfo.h"
#
# if HWLOC_API_VERSION < 0x00010b00
# define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE
# endif
# include "crypto/common/NUMAMemoryPool.h"
#endif
#include "base/io/log/Log.h"
#include "crypto/common/VirtualMemory.h"
#include <cinttypes>
#include <mutex>
uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t affinity)
namespace xmrig {
static IMemoryPool *pool = nullptr;
static std::mutex mutex;
} // namespace xmrig
xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, bool usePool, uint32_t node, size_t alignSize) :
m_size(align(size)),
m_node(node)
{
# ifdef XMRIG_FEATURE_HWLOC
if (affinity < 0 || !HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) {
return 0;
}
if (usePool) {
std::lock_guard<std::mutex> lock(mutex);
if (hugePages && !pool->isHugePages(node) && allocateLargePagesMemory()) {
return;
}
hwloc_topology_t topology;
hwloc_topology_init(&topology);
hwloc_topology_load(topology);
m_scratchpad = pool->get(m_size, node);
if (m_scratchpad) {
m_flags.set(FLAG_HUGEPAGES, pool->isHugePages(node));
m_flags.set(FLAG_EXTERNAL, true);
const unsigned puId = static_cast<unsigned>(affinity);
hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(topology, puId);
# if HWLOC_API_VERSION >= 0x20000
if (pu == nullptr || hwloc_set_membind(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET) < 0) {
# else
if (pu == nullptr || hwloc_set_membind_nodeset(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD) < 0) {
# endif
LOG_WARN("CPU #%02u warning: \"can't bind memory\"", puId);
}
uint32_t nodeId = 0;
if (pu) {
hwloc_obj_t node = nullptr;
while ((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) {
if (hwloc_bitmap_intersects(node->cpuset, pu->cpuset)) {
nodeId = node->os_index;
break;
}
return;
}
}
hwloc_topology_destroy(topology);
if (hugePages && allocateLargePagesMemory()) {
return;
}
return nodeId;
# else
return 0;
# endif
m_scratchpad = static_cast<uint8_t*>(_mm_malloc(m_size, alignSize));
}
xmrig::VirtualMemory::~VirtualMemory()
{
if (!m_scratchpad) {
return;
}
if (m_flags.test(FLAG_EXTERNAL)) {
std::lock_guard<std::mutex> lock(mutex);
pool->release(m_node);
}
else if (isHugePages()) {
freeLargePagesMemory();
}
else {
_mm_free(m_scratchpad);
}
}
#ifndef XMRIG_FEATURE_HWLOC
uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t)
{
return 0;
}
#endif
void xmrig::VirtualMemory::destroy()
{
delete pool;
}
void xmrig::VirtualMemory::init(size_t poolSize, bool hugePages)
{
if (!pool) {
osInit();
}
# ifdef XMRIG_FEATURE_HWLOC
if (Cpu::info()->nodes() > 1) {
pool = new NUMAMemoryPool(align(poolSize, Cpu::info()->nodes()), hugePages);
} else
# endif
{
pool = new MemoryPool(poolSize, hugePages);
}
}

View file

@ -28,8 +28,12 @@
#define XMRIG_VIRTUALMEMORY_H
#include <stddef.h>
#include <stdint.h>
#include "base/tools/Object.h"
#include <bitset>
#include <cstddef>
#include <cstdint>
#include <utility>
@ -39,43 +43,50 @@ namespace xmrig {
class VirtualMemory
{
public:
inline VirtualMemory() {}
VirtualMemory(size_t size, bool hugePages = true, size_t align = 64);
XMRIG_DISABLE_COPY_MOVE_DEFAULT(VirtualMemory)
VirtualMemory(size_t size, bool hugePages, bool usePool, uint32_t node = 0, size_t alignSize = 64);
~VirtualMemory();
inline bool isHugePages() const { return m_flags & HUGEPAGES; }
inline bool isHugePages() const { return m_flags.test(FLAG_HUGEPAGES); }
inline size_t size() const { return m_size; }
inline uint8_t *scratchpad() const { return m_scratchpad; }
inline std::pair<size_t, size_t> hugePages() const
{
return std::pair<size_t, size_t>(isHugePages() ? (align(size()) / 2097152) : 0, align(size()) / 2097152);
return { isHugePages() ? (align(size()) / 2097152) : 0, align(size()) / 2097152 };
}
static bool isHugepagesAvailable();
static uint32_t bindToNUMANode(int64_t affinity);
static void *allocateExecutableMemory(size_t size);
static void *allocateLargePagesMemory(size_t size);
static void destroy();
static void flushInstructionCache(void *p, size_t size);
static void freeLargePagesMemory(void *p, size_t size);
static void init(bool hugePages);
static void init(size_t poolSize, bool hugePages);
static void protectExecutableMemory(void *p, size_t size);
static void unprotectExecutableMemory(void *p, size_t size);
static inline bool isHugepagesAvailable() { return (m_globalFlags & HUGEPAGES_AVAILABLE) != 0; }
static inline constexpr size_t align(size_t pos, size_t align = 2097152) { return ((pos - 1) / align + 1) * align; }
private:
enum Flags {
HUGEPAGES_AVAILABLE = 1,
HUGEPAGES = 2,
LOCK = 4
FLAG_HUGEPAGES,
FLAG_LOCK,
FLAG_EXTERNAL,
FLAG_MAX
};
static int m_globalFlags;
static void osInit();
int m_flags = 0;
size_t m_size = 0;
uint8_t *m_scratchpad = nullptr;
bool allocateLargePagesMemory();
void freeLargePagesMemory();
const size_t m_size;
const uint32_t m_node;
std::bitset<FLAG_MAX> m_flags;
uint8_t *m_scratchpad = nullptr;
};

View file

@ -0,0 +1,56 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/common/VirtualMemory.h"
#include "backend/cpu/Cpu.h"
#include "backend/cpu/platform/HwlocCpuInfo.h"
#include "base/io/log/Log.h"
#include <hwloc.h>
uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t affinity)
{
if (affinity < 0 || Cpu::info()->nodes() < 2) {
return 0;
}
auto cpu = static_cast<HwlocCpuInfo *>(Cpu::info());
hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(cpu->topology(), static_cast<unsigned>(affinity));
char *buffer;
hwloc_bitmap_asprintf(&buffer, pu->cpuset);
if (pu == nullptr || !cpu->membind(pu->nodeset)) {
LOG_WARN("CPU #%02" PRId64 " warning: \"can't bind memory\"", affinity);
return 0;
}
return hwloc_bitmap_first(pu->nodeset);
}

View file

@ -25,7 +25,7 @@
*/
#include <stdlib.h>
#include <cstdlib>
#include <sys/mman.h>
@ -38,51 +38,12 @@
#endif
int xmrig::VirtualMemory::m_globalFlags = 0;
xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, size_t align) :
m_size(VirtualMemory::align(size))
bool xmrig::VirtualMemory::isHugepagesAvailable()
{
if (hugePages) {
m_scratchpad = static_cast<uint8_t*>(allocateLargePagesMemory(m_size));
if (m_scratchpad) {
m_flags |= HUGEPAGES;
madvise(m_scratchpad, size, MADV_RANDOM | MADV_WILLNEED);
if (mlock(m_scratchpad, m_size) == 0) {
m_flags |= LOCK;
}
return;
}
}
m_scratchpad = static_cast<uint8_t*>(_mm_malloc(m_size, align));
return true;
}
xmrig::VirtualMemory::~VirtualMemory()
{
if (!m_scratchpad) {
return;
}
if (isHugePages()) {
if (m_flags & LOCK) {
munlock(m_scratchpad, m_size);
}
freeLargePagesMemory(m_scratchpad, m_size);
}
else {
_mm_free(m_scratchpad);
}
}
void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size)
{
# if defined(__APPLE__)
@ -123,14 +84,6 @@ void xmrig::VirtualMemory::freeLargePagesMemory(void *p, size_t size)
}
void xmrig::VirtualMemory::init(bool hugePages)
{
if (hugePages) {
m_globalFlags = HUGEPAGES | HUGEPAGES_AVAILABLE;
}
}
void xmrig::VirtualMemory::protectExecutableMemory(void *p, size_t size)
{
mprotect(p, size, PROT_READ | PROT_EXEC);
@ -141,3 +94,37 @@ void xmrig::VirtualMemory::unprotectExecutableMemory(void *p, size_t size)
{
mprotect(p, size, PROT_WRITE | PROT_EXEC);
}
void xmrig::VirtualMemory::osInit()
{
}
bool xmrig::VirtualMemory::allocateLargePagesMemory()
{
m_scratchpad = static_cast<uint8_t*>(allocateLargePagesMemory(m_size));
if (m_scratchpad) {
m_flags.set(FLAG_HUGEPAGES, true);
madvise(m_scratchpad, m_size, MADV_RANDOM | MADV_WILLNEED);
if (mlock(m_scratchpad, m_size) == 0) {
m_flags.set(FLAG_LOCK, true);
}
return true;
}
return false;
}
void xmrig::VirtualMemory::freeLargePagesMemory()
{
if (m_flags.test(FLAG_LOCK)) {
munlock(m_scratchpad, m_size);
}
freeLargePagesMemory(m_scratchpad, m_size);
}

View file

@ -36,6 +36,12 @@
#include "crypto/common/VirtualMemory.h"
namespace xmrig {
static bool hugepagesAvailable = false;
/*****************************************************************
SetLockPagesPrivilege: a function to obtain or
release the privilege of locking physical pages.
@ -83,7 +89,7 @@ static BOOL SetLockPagesPrivilege() {
static LSA_UNICODE_STRING StringToLsaUnicodeString(LPCTSTR string) {
LSA_UNICODE_STRING lsaString;
DWORD dwLen = (DWORD) wcslen(string);
const auto dwLen = (DWORD) wcslen(string);
lsaString.Buffer = (LPWSTR) string;
lsaString.Length = (USHORT)((dwLen) * sizeof(WCHAR));
lsaString.MaximumLength = (USHORT)((dwLen + 1) * sizeof(WCHAR));
@ -141,37 +147,12 @@ static BOOL TrySetLockPagesPrivilege() {
}
int xmrig::VirtualMemory::m_globalFlags = 0;
} // namespace xmrig
xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, size_t align) :
m_size(VirtualMemory::align(size))
bool xmrig::VirtualMemory::isHugepagesAvailable()
{
if (hugePages) {
m_scratchpad = static_cast<uint8_t*>(allocateLargePagesMemory(m_size));
if (m_scratchpad) {
m_flags |= HUGEPAGES;
return;
}
}
m_scratchpad = static_cast<uint8_t*>(_mm_malloc(m_size, align));
}
xmrig::VirtualMemory::~VirtualMemory()
{
if (!m_scratchpad) {
return;
}
if (isHugePages()) {
freeLargePagesMemory(m_scratchpad, m_size);
}
else {
_mm_free(m_scratchpad);
}
return hugepagesAvailable;
}
@ -206,20 +187,6 @@ void xmrig::VirtualMemory::freeLargePagesMemory(void *p, size_t)
}
void xmrig::VirtualMemory::init(bool hugePages)
{
if (!hugePages) {
return;
}
m_globalFlags = HUGEPAGES;
if (TrySetLockPagesPrivilege()) {
m_globalFlags |= HUGEPAGES_AVAILABLE;
}
}
void xmrig::VirtualMemory::protectExecutableMemory(void *p, size_t size)
{
DWORD oldProtect;
@ -232,3 +199,28 @@ void xmrig::VirtualMemory::unprotectExecutableMemory(void *p, size_t size)
DWORD oldProtect;
VirtualProtect(p, size, PAGE_EXECUTE_READWRITE, &oldProtect);
}
void xmrig::VirtualMemory::osInit()
{
hugepagesAvailable = TrySetLockPagesPrivilege();
}
bool xmrig::VirtualMemory::allocateLargePagesMemory()
{
m_scratchpad = static_cast<uint8_t*>(allocateLargePagesMemory(m_size));
if (m_scratchpad) {
m_flags.set(FLAG_HUGEPAGES, true);
return true;
}
return false;
}
void xmrig::VirtualMemory::freeLargePagesMemory()
{
freeLargePagesMemory(m_scratchpad, m_size);
}

View file

@ -268,8 +268,6 @@ namespace randomx {
}
void JitCompilerX86::generateProgramPrologue(Program& prog, ProgramConfiguration& pcfg) {
memset(registerUsage, -1, sizeof(registerUsage));
codePos = ((uint8_t*)randomx_program_prologue_first_load) - ((uint8_t*)randomx_program_prologue);
code[codePos + 2] = 0xc0 + pcfg.readReg0;
code[codePos + 5] = 0xc0 + pcfg.readReg1;
@ -280,13 +278,21 @@ namespace randomx {
memcpy(code + codePos - 48, &pcfg.eMask, sizeof(pcfg.eMask));
memcpy(code + codePos, codeLoopLoad, loopLoadSize);
codePos += loopLoadSize;
for (unsigned i = 0; i < prog.getSize(); ++i) {
Instruction& instr = prog(i);
instr.src %= RegistersCount;
instr.dst %= RegistersCount;
instructionOffsets[i] = codePos;
(this->*(engine[instr.opcode]))(instr, i);
//mark all registers as used
uint64_t* r = (uint64_t*)registerUsage;
uint64_t k = codePos;
k |= k << 32;
for (unsigned j = 0; j < RegistersCount / 2; ++j) {
r[j] = k;
}
for (int i = 0, n = static_cast<int>(RandomX_CurrentConfig.ProgramSize); i < n; ++i) {
Instruction instr = prog(i);
*((uint64_t*)&instr) &= (uint64_t(-1) - (0xFFFF << 8)) | ((RegistersCount - 1) << 8) | ((RegistersCount - 1) << 16);
(this->*(engine[instr.opcode]))(instr);
}
emit(REX_MOV_RR, code, codePos);
emitByte(0xc0 + pcfg.readReg2, code, codePos);
emit(REX_XOR_EAX, code, codePos);
@ -402,7 +408,7 @@ namespace randomx {
}
}
void JitCompilerX86::genAddressReg(Instruction& instr, uint8_t* code, int& codePos, bool rax) {
void JitCompilerX86::genAddressReg(const Instruction& instr, uint8_t* code, int& codePos, bool rax) {
emit(LEA_32, code, codePos);
emitByte(0x80 + instr.src + (rax ? 0 : 8), code, codePos);
if (instr.src == RegisterNeedsSib) {
@ -416,7 +422,7 @@ namespace randomx {
emit32(instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask, code, codePos);
}
void JitCompilerX86::genAddressRegDst(Instruction& instr, uint8_t* code, int& codePos) {
void JitCompilerX86::genAddressRegDst(const Instruction& instr, uint8_t* code, int& codePos) {
emit(LEA_32, code, codePos);
emitByte(0x80 + instr.dst, code, codePos);
if (instr.dst == RegisterNeedsSib) {
@ -432,7 +438,7 @@ namespace randomx {
}
}
void JitCompilerX86::genAddressImm(Instruction& instr, uint8_t* code, int& codePos) {
void JitCompilerX86::genAddressImm(const Instruction& instr, uint8_t* code, int& codePos) {
emit32(instr.getImm32() & ScratchpadL3Mask, code, codePos);
}
@ -447,17 +453,18 @@ namespace randomx {
0x3c8d4f,
};
void JitCompilerX86::h_IADD_RS(Instruction& instr, int i) {
void JitCompilerX86::h_IADD_RS(const Instruction& instr) {
int pos = codePos;
uint8_t* const p = code + pos;
registerUsage[instr.dst] = i;
const uint32_t sib = (instr.getModShift() << 6) | (instr.src << 3) | instr.dst;
*(uint32_t*)(p) = template_IADD_RS[instr.dst] | (sib << 24);
*(uint32_t*)(p + 4) = instr.getImm32();
codePos = pos + ((instr.dst == RegisterNeedsDisplacement) ? 8 : 4);
pos += ((instr.dst == RegisterNeedsDisplacement) ? 8 : 4);
registerUsage[instr.dst] = pos;
codePos = pos;
}
static const uint32_t template_IADD_M[8] = {
@ -471,11 +478,10 @@ namespace randomx {
0x063c034c,
};
void JitCompilerX86::h_IADD_M(Instruction& instr, int i) {
void JitCompilerX86::h_IADD_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos);
emit32(template_IADD_M[instr.dst], p, pos);
@ -486,6 +492,7 @@ namespace randomx {
genAddressImm(instr, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
@ -493,11 +500,10 @@ namespace randomx {
emitByte((scale << 6) | (index << 3) | base, code, codePos);
}
void JitCompilerX86::h_ISUB_R(Instruction& instr, int i) {
void JitCompilerX86::h_ISUB_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
emit(REX_SUB_RR, p, pos);
emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos);
@ -508,14 +514,14 @@ namespace randomx {
emit32(instr.getImm32(), p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_ISUB_M(Instruction& instr, int i) {
void JitCompilerX86::h_ISUB_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos);
emit(REX_SUB_RM, p, pos);
@ -528,14 +534,14 @@ namespace randomx {
genAddressImm(instr, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IMUL_R(Instruction& instr, int i) {
void JitCompilerX86::h_IMUL_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
emit(REX_IMUL_RR, p, pos);
emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos);
@ -546,14 +552,14 @@ namespace randomx {
emit32(instr.getImm32(), p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IMUL_M(Instruction& instr, int i) {
void JitCompilerX86::h_IMUL_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos);
emit(REX_IMUL_RM, p, pos);
@ -566,14 +572,14 @@ namespace randomx {
genAddressImm(instr, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IMULH_R(Instruction& instr, int i) {
void JitCompilerX86::h_IMULH_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
emit(REX_MOV_RR64, p, pos);
emitByte(0xc0 + instr.dst, p, pos);
emit(REX_MUL_R, p, pos);
@ -581,14 +587,14 @@ namespace randomx {
emit(REX_MOV_R64R, p, pos);
emitByte(0xc2 + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IMULH_M(Instruction& instr, int i) {
void JitCompilerX86::h_IMULH_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos, false);
emit(REX_MOV_RR64, p, pos);
@ -605,14 +611,14 @@ namespace randomx {
emit(REX_MOV_R64R, p, pos);
emitByte(0xc2 + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_ISMULH_R(Instruction& instr, int i) {
void JitCompilerX86::h_ISMULH_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
emit(REX_MOV_RR64, p, pos);
emitByte(0xc0 + instr.dst, p, pos);
emit(REX_MUL_R, p, pos);
@ -620,14 +626,14 @@ namespace randomx {
emit(REX_MOV_R64R, p, pos);
emitByte(0xc2 + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_ISMULH_M(Instruction& instr, int i) {
void JitCompilerX86::h_ISMULH_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos, false);
emit(REX_MOV_RR64, p, pos);
@ -644,41 +650,41 @@ namespace randomx {
emit(REX_MOV_R64R, p, pos);
emitByte(0xc2 + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IMUL_RCP(Instruction& instr, int i) {
void JitCompilerX86::h_IMUL_RCP(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
uint64_t divisor = instr.getImm32();
if (!isZeroOrPowerOf2(divisor)) {
registerUsage[instr.dst] = i;
emit(MOV_RAX_I, p, pos);
emit64(randomx_reciprocal_fast(divisor), p, pos);
emit(REX_IMUL_RM, p, pos);
emitByte(0xc0 + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
}
codePos = pos;
}
void JitCompilerX86::h_INEG_R(Instruction& instr, int i) {
void JitCompilerX86::h_INEG_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
emit(REX_NEG, p, pos);
emitByte(0xd8 + instr.dst, p, pos);
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IXOR_R(Instruction& instr, int i) {
void JitCompilerX86::h_IXOR_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
emit(REX_XOR_RR, p, pos);
emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos);
@ -689,14 +695,14 @@ namespace randomx {
emit32(instr.getImm32(), p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IXOR_M(Instruction& instr, int i) {
void JitCompilerX86::h_IXOR_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
genAddressReg(instr, p, pos);
emit(REX_XOR_RM, p, pos);
@ -709,14 +715,14 @@ namespace randomx {
genAddressImm(instr, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IROR_R(Instruction& instr, int i) {
void JitCompilerX86::h_IROR_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
emit(REX_MOV_RR, p, pos);
emitByte(0xc8 + instr.src, p, pos);
@ -729,14 +735,14 @@ namespace randomx {
emitByte(instr.getImm32() & 63, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_IROL_R(Instruction& instr, int i) {
void JitCompilerX86::h_IROL_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
registerUsage[instr.dst] = i;
if (instr.src != instr.dst) {
emit(REX_MOV_RR, p, pos);
emitByte(0xc8 + instr.src, p, pos);
@ -749,24 +755,25 @@ namespace randomx {
emitByte(instr.getImm32() & 63, p, pos);
}
registerUsage[instr.dst] = pos;
codePos = pos;
}
void JitCompilerX86::h_ISWAP_R(Instruction& instr, int i) {
void JitCompilerX86::h_ISWAP_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
if (instr.src != instr.dst) {
registerUsage[instr.dst] = i;
registerUsage[instr.src] = i;
emit(REX_XCHG, p, pos);
emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos);
registerUsage[instr.dst] = pos;
registerUsage[instr.src] = pos;
}
codePos = pos;
}
void JitCompilerX86::h_FSWAP_R(Instruction& instr, int i) {
void JitCompilerX86::h_FSWAP_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
@ -777,105 +784,105 @@ namespace randomx {
codePos = pos;
}
void JitCompilerX86::h_FADD_R(Instruction& instr, int i) {
void JitCompilerX86::h_FADD_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
instr.src %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
const uint32_t src = instr.src % RegisterCountFlt;
emit(REX_ADDPD, p, pos);
emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos);
emitByte(0xc0 + src + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FADD_M(Instruction& instr, int i) {
void JitCompilerX86::h_FADD_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
genAddressReg(instr, p, pos);
emit(REX_CVTDQ2PD_XMM12, p, pos);
emit(REX_ADDPD, p, pos);
emitByte(0xc4 + 8 * instr.dst, p, pos);
emitByte(0xc4 + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FSUB_R(Instruction& instr, int i) {
void JitCompilerX86::h_FSUB_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
instr.src %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
const uint32_t src = instr.src % RegisterCountFlt;
emit(REX_SUBPD, p, pos);
emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos);
emitByte(0xc0 + src + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FSUB_M(Instruction& instr, int i) {
void JitCompilerX86::h_FSUB_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
genAddressReg(instr, p, pos);
emit(REX_CVTDQ2PD_XMM12, p, pos);
emit(REX_SUBPD, p, pos);
emitByte(0xc4 + 8 * instr.dst, p, pos);
emitByte(0xc4 + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FSCAL_R(Instruction& instr, int i) {
void JitCompilerX86::h_FSCAL_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
emit(REX_XORPS, p, pos);
emitByte(0xc7 + 8 * instr.dst, p, pos);
emitByte(0xc7 + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FMUL_R(Instruction& instr, int i) {
void JitCompilerX86::h_FMUL_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
instr.src %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
const uint32_t src = instr.src % RegisterCountFlt;
emit(REX_MULPD, p, pos);
emitByte(0xe0 + instr.src + 8 * instr.dst, p, pos);
emitByte(0xe0 + src + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FDIV_M(Instruction& instr, int i) {
void JitCompilerX86::h_FDIV_M(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
genAddressReg(instr, p, pos);
emit(REX_CVTDQ2PD_XMM12, p, pos);
emit(REX_ANDPS_XMM12, p, pos);
emit(REX_DIVPD, p, pos);
emitByte(0xe4 + 8 * instr.dst, p, pos);
emitByte(0xe4 + 8 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_FSQRT_R(Instruction& instr, int i) {
void JitCompilerX86::h_FSQRT_R(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
instr.dst %= RegisterCountFlt;
const uint32_t dst = instr.dst % RegisterCountFlt;
emit(SQRTPD, p, pos);
emitByte(0xe4 + 9 * instr.dst, p, pos);
emitByte(0xe4 + 9 * dst, p, pos);
codePos = pos;
}
void JitCompilerX86::h_CFROUND(Instruction& instr, int i) {
void JitCompilerX86::h_CFROUND(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
@ -891,12 +898,11 @@ namespace randomx {
codePos = pos;
}
void JitCompilerX86::h_CBRANCH(Instruction& instr, int i) {
void JitCompilerX86::h_CBRANCH(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
int reg = instr.dst;
int target = registerUsage[reg] + 1;
emit(REX_ADD_I, p, pos);
emitByte(0xc0 + reg, p, pos);
int shift = instr.getModCond() + RandomX_CurrentConfig.JumpOffset;
@ -908,10 +914,10 @@ namespace randomx {
emitByte(0xc0 + reg, p, pos);
emit32(RandomX_CurrentConfig.ConditionMask_Calculated << shift, p, pos);
emit(JZ, p, pos);
emit32(instructionOffsets[target] - (pos + 4), p, pos);
emit32(registerUsage[reg] - (pos + 4), p, pos);
//mark all registers as used
uint64_t* r = (uint64_t*) registerUsage;
uint64_t k = i;
uint64_t k = pos;
k |= k << 32;
for (unsigned j = 0; j < RegistersCount / 2; ++j) {
r[j] = k;
@ -920,7 +926,7 @@ namespace randomx {
codePos = pos;
}
void JitCompilerX86::h_ISTORE(Instruction& instr, int i) {
void JitCompilerX86::h_ISTORE(const Instruction& instr) {
uint8_t* const p = code;
int pos = codePos;
@ -932,7 +938,7 @@ namespace randomx {
codePos = pos;
}
void JitCompilerX86::h_NOP(Instruction& instr, int i) {
void JitCompilerX86::h_NOP(const Instruction& instr) {
emit(NOP1, code, codePos);
}

View file

@ -41,7 +41,7 @@ namespace randomx {
class JitCompilerX86;
class Instruction;
typedef void(JitCompilerX86::*InstructionGeneratorX86)(Instruction&, int);
typedef void(JitCompilerX86::*InstructionGeneratorX86)(const Instruction&);
constexpr uint32_t CodeSize = 64 * 1024;
@ -66,16 +66,15 @@ namespace randomx {
size_t getCodeSize();
static InstructionGeneratorX86 engine[256];
int32_t instructionOffsets[512];
int registerUsage[RegistersCount];
uint8_t* code;
int32_t codePos;
void generateProgramPrologue(Program&, ProgramConfiguration&);
void generateProgramEpilogue(Program&, ProgramConfiguration&);
static void genAddressReg(Instruction&, uint8_t* code, int& codePos, bool rax = true);
static void genAddressRegDst(Instruction&, uint8_t* code, int& codePos);
static void genAddressImm(Instruction&, uint8_t* code, int& codePos);
static void genAddressReg(const Instruction&, uint8_t* code, int& codePos, bool rax = true);
static void genAddressRegDst(const Instruction&, uint8_t* code, int& codePos);
static void genAddressImm(const Instruction&, uint8_t* code, int& codePos);
static void genSIB(int scale, int index, int base, uint8_t* code, int& codePos);
void generateSuperscalarCode(Instruction &, std::vector<uint64_t> &);
@ -105,36 +104,36 @@ namespace randomx {
codePos += count;
}
void h_IADD_RS(Instruction&, int);
void h_IADD_M(Instruction&, int);
void h_ISUB_R(Instruction&, int);
void h_ISUB_M(Instruction&, int);
void h_IMUL_R(Instruction&, int);
void h_IMUL_M(Instruction&, int);
void h_IMULH_R(Instruction&, int);
void h_IMULH_M(Instruction&, int);
void h_ISMULH_R(Instruction&, int);
void h_ISMULH_M(Instruction&, int);
void h_IMUL_RCP(Instruction&, int);
void h_INEG_R(Instruction&, int);
void h_IXOR_R(Instruction&, int);
void h_IXOR_M(Instruction&, int);
void h_IROR_R(Instruction&, int);
void h_IROL_R(Instruction&, int);
void h_ISWAP_R(Instruction&, int);
void h_FSWAP_R(Instruction&, int);
void h_FADD_R(Instruction&, int);
void h_FADD_M(Instruction&, int);
void h_FSUB_R(Instruction&, int);
void h_FSUB_M(Instruction&, int);
void h_FSCAL_R(Instruction&, int);
void h_FMUL_R(Instruction&, int);
void h_FDIV_M(Instruction&, int);
void h_FSQRT_R(Instruction&, int);
void h_CBRANCH(Instruction&, int);
void h_CFROUND(Instruction&, int);
void h_ISTORE(Instruction&, int);
void h_NOP(Instruction&, int);
void h_IADD_RS(const Instruction&);
void h_IADD_M(const Instruction&);
void h_ISUB_R(const Instruction&);
void h_ISUB_M(const Instruction&);
void h_IMUL_R(const Instruction&);
void h_IMUL_M(const Instruction&);
void h_IMULH_R(const Instruction&);
void h_IMULH_M(const Instruction&);
void h_ISMULH_R(const Instruction&);
void h_ISMULH_M(const Instruction&);
void h_IMUL_RCP(const Instruction&);
void h_INEG_R(const Instruction&);
void h_IXOR_R(const Instruction&);
void h_IXOR_M(const Instruction&);
void h_IROR_R(const Instruction&);
void h_IROL_R(const Instruction&);
void h_ISWAP_R(const Instruction&);
void h_FSWAP_R(const Instruction&);
void h_FADD_R(const Instruction&);
void h_FADD_M(const Instruction&);
void h_FSUB_R(const Instruction&);
void h_FSUB_M(const Instruction&);
void h_FSCAL_R(const Instruction&);
void h_FMUL_R(const Instruction&);
void h_FDIV_M(const Instruction&);
void h_FSQRT_R(const Instruction&);
void h_CBRANCH(const Instruction&);
void h_CFROUND(const Instruction&);
void h_ISTORE(const Instruction&);
void h_NOP(const Instruction&);
};
}

View file

@ -82,6 +82,16 @@ RandomX_ConfigurationLoki::RandomX_ConfigurationLoki()
RANDOMX_FREQ_CBRANCH = 16;
}
RandomX_ConfigurationArqma::RandomX_ConfigurationArqma()
{
ArgonIterations = 1;
ArgonSalt = "RandomARQ\x01";
ProgramIterations = 1024;
ProgramCount = 4;
ScratchpadL2_Size = 131072;
ScratchpadL3_Size = 262144;
}
RandomX_ConfigurationBase::RandomX_ConfigurationBase()
: ArgonMemory(262144)
, ArgonIterations(3)
@ -248,6 +258,7 @@ void RandomX_ConfigurationBase::Apply()
RandomX_ConfigurationMonero RandomX_MoneroConfig;
RandomX_ConfigurationWownero RandomX_WowneroConfig;
RandomX_ConfigurationLoki RandomX_LokiConfig;
RandomX_ConfigurationArqma RandomX_ArqmaConfig;
RandomX_ConfigurationBase RandomX_CurrentConfig;

View file

@ -176,10 +176,12 @@ struct RandomX_ConfigurationBase
struct RandomX_ConfigurationMonero : public RandomX_ConfigurationBase {};
struct RandomX_ConfigurationWownero : public RandomX_ConfigurationBase { RandomX_ConfigurationWownero(); };
struct RandomX_ConfigurationLoki : public RandomX_ConfigurationBase { RandomX_ConfigurationLoki(); };
struct RandomX_ConfigurationArqma : public RandomX_ConfigurationBase { RandomX_ConfigurationArqma(); };
extern RandomX_ConfigurationMonero RandomX_MoneroConfig;
extern RandomX_ConfigurationWownero RandomX_WowneroConfig;
extern RandomX_ConfigurationLoki RandomX_LokiConfig;
extern RandomX_ConfigurationArqma RandomX_ArqmaConfig;
extern RandomX_ConfigurationBase RandomX_CurrentConfig;

View file

@ -26,32 +26,10 @@
#include "crypto/rx/Rx.h"
#include "backend/common/interfaces/IRxListener.h"
#include "backend/cpu/Cpu.h"
#include "backend/common/Tags.h"
#include "base/io/log/Log.h"
#include "base/kernel/Platform.h"
#include "base/net/stratum/Job.h"
#include "base/tools/Buffer.h"
#include "base/tools/Chrono.h"
#include "base/tools/Handle.h"
#include "base/tools/Object.h"
#include "crypto/rx/RxAlgo.h"
#include "crypto/rx/RxCache.h"
#include "crypto/rx/RxDataset.h"
#ifdef XMRIG_FEATURE_HWLOC
# include <hwloc.h>
# include "backend/cpu/platform/HwlocCpuInfo.h"
#endif
#include <atomic>
#include <map>
#include <mutex>
#include <thread>
#include <uv.h>
#include "crypto/rx/RxConfig.h"
#include "crypto/rx/RxQueue.h"
namespace xmrig {
@ -62,239 +40,37 @@ class RxPrivate;
static const char *tag = BLUE_BG(WHITE_BOLD_S " rx ") " ";
static RxPrivate *d_ptr = nullptr;
static std::mutex mutex;
#ifdef XMRIG_FEATURE_HWLOC
static void bindToNUMANode(uint32_t nodeId)
{
hwloc_topology_t topology;
hwloc_topology_init(&topology);
hwloc_topology_load(topology);
hwloc_obj_t node = hwloc_get_numanode_obj_by_os_index(topology, nodeId);
if (node) {
if (HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) {
# if HWLOC_API_VERSION >= 0x20000
hwloc_set_membind(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET);
# else
hwloc_set_membind_nodeset(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD);
# endif
}
Platform::setThreadAffinity(static_cast<uint64_t>(hwloc_bitmap_first(node->cpuset)));
}
hwloc_topology_destroy(topology);
}
#else
inline static void bindToNUMANode(uint32_t) {}
#endif
class RxPrivate
{
public:
XMRIG_DISABLE_COPY_MOVE(RxPrivate)
inline RxPrivate(IRxListener *listener) : queue(listener) {}
inline RxPrivate() :
m_counter(0),
m_last(0)
{
m_async = new uv_async_t;
m_async->data = this;
uv_async_init(uv_default_loop(), m_async, [](uv_async_t *) { d_ptr->onReady(); });
# ifdef XMRIG_FEATURE_HWLOC
if (Cpu::info()->nodes() > 1) {
for (uint32_t nodeId : HwlocCpuInfo::nodeIndexes()) {
datasets.insert({ nodeId, nullptr });
}
}
else
# endif
{
datasets.insert({ 0, nullptr });
}
}
inline ~RxPrivate()
{
Handle::close(m_async);
for (auto const &item : datasets) {
delete item.second;
}
datasets.clear();
}
inline bool isNUMA() const { return m_numa; }
inline bool isReady(const Job &job) const { return m_ready == count() && m_algorithm == job.algorithm() && m_seed == job.seed(); }
inline const Algorithm &algorithm() const { return m_algorithm; }
inline const Buffer &seed() const { return m_seed; }
inline size_t count() const { return isNUMA() ? datasets.size() : 1; }
inline uint64_t counter() { return m_counter.load(std::memory_order_relaxed); }
inline void asyncSend(uint64_t counter) { m_ready++; if (m_ready == count()) { m_last = counter; uv_async_send(m_async); } }
static void allocate(uint32_t nodeId)
{
const uint64_t ts = Chrono::steadyMSecs();
if (d_ptr->isNUMA()) {
bindToNUMANode(nodeId);
}
LOG_INFO("%s" CYAN_BOLD("#%u") MAGENTA_BOLD(" allocate") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu) for RandomX dataset & cache"),
tag,
nodeId,
(RxDataset::maxSize() + RxCache::maxSize()) / 1024 / 1024,
RxDataset::maxSize() / 1024 / 1024,
RxCache::maxSize() / 1024 / 1024
);
auto dataset = new RxDataset(d_ptr->m_hugePages);
d_ptr->datasets[nodeId] = dataset;
if (dataset->get() != nullptr) {
const auto hugePages = dataset->hugePages();
const double percent = hugePages.first == 0 ? 0.0 : static_cast<double>(hugePages.first) / hugePages.second * 100.0;
LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" allocate done") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"),
tag,
nodeId,
(hugePages.first == hugePages.second ? GREEN_BOLD_S : (hugePages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)),
hugePages.first,
hugePages.second,
percent,
dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-",
Chrono::steadyMSecs() - ts
);
}
else {
LOG_WARN(CLEAR "%s" CYAN_BOLD("#%u") YELLOW_BOLD_S " failed to allocate RandomX dataset, switching to slow mode", tag, nodeId);
}
}
static void initDataset(uint32_t nodeId, uint32_t threads, uint64_t counter)
{
std::lock_guard<std::mutex> lock(mutex);
const uint64_t ts = Chrono::steadyMSecs();
d_ptr->getOrAllocate(nodeId)->init(d_ptr->seed(), threads);
d_ptr->asyncSend(counter);
LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" init done ") CYAN_BOLD("%zu/%zu") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, nodeId, d_ptr->m_ready, d_ptr->count(), Chrono::steadyMSecs() - ts);
}
inline RxDataset *getOrAllocate(uint32_t nodeId)
{
RxDataset *dataset = datasets.at(nodeId);
if (dataset == nullptr) {
# ifdef XMRIG_FEATURE_HWLOC
if (d_ptr->isNUMA()) {
std::thread thread(allocate, nodeId);
thread.join();
} else
# endif
{
allocate(nodeId);
}
dataset = datasets.at(nodeId);
}
return dataset;
}
inline void setState(const Job &job, bool hugePages, bool numa, IRxListener *listener)
{
if (m_algorithm != job.algorithm()) {
m_algorithm = RxAlgo::apply(job.algorithm());
}
m_ready = 0;
m_numa = numa && Cpu::info()->nodes() > 1;
m_hugePages = hugePages;
m_listener = listener;
m_seed = job.seed();
++m_counter;
}
std::map<uint32_t, RxDataset *> datasets;
private:
inline void onReady()
{
if (m_listener && counter() == m_last.load(std::memory_order_relaxed)) {
m_listener->onDatasetReady();
}
}
Algorithm m_algorithm;
bool m_hugePages = true;
bool m_numa = true;
Buffer m_seed;
IRxListener *m_listener = nullptr;
size_t m_ready = 0;
std::atomic<uint64_t> m_counter;
std::atomic<uint64_t> m_last;
uv_async_t *m_async = nullptr;
RxQueue queue;
};
} // namespace xmrig
bool xmrig::Rx::init(const Job &job, int initThreads, bool hugePages, bool numa, IRxListener *listener)
const char *xmrig::rx_tag()
{
return tag;
}
bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages)
{
if (job.algorithm().family() != Algorithm::RANDOM_X) {
return true;
}
std::lock_guard<std::mutex> lock(mutex);
if (d_ptr->isReady(job)) {
if (isReady(job)) {
return true;
}
d_ptr->setState(job, hugePages, numa, listener);
const uint32_t threads = initThreads < 1 ? static_cast<uint32_t>(Cpu::info()->threads()) : static_cast<uint32_t>(initThreads);
const String buf = Buffer::toHex(job.seed().data(), 8);
const uint64_t counter = d_ptr->counter();
LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."),
tag,
d_ptr->count() > 1 ? "s" : "",
job.algorithm().shortName(),
threads,
buf.data()
);
# ifdef XMRIG_FEATURE_HWLOC
if (d_ptr->isNUMA()) {
for (auto const &item : d_ptr->datasets) {
std::thread thread(RxPrivate::initDataset, item.first, threads, counter);
thread.detach();
}
}
else
# endif
{
std::thread thread(RxPrivate::initDataset, 0, threads, counter);
thread.detach();
}
d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages);
return false;
}
@ -302,39 +78,19 @@ bool xmrig::Rx::init(const Job &job, int initThreads, bool hugePages, bool numa,
bool xmrig::Rx::isReady(const Job &job)
{
std::lock_guard<std::mutex> lock(mutex);
return d_ptr->isReady(job);
return d_ptr->queue.isReady(job);
}
xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId)
{
std::lock_guard<std::mutex> lock(mutex);
if (!d_ptr->isReady(job)) {
return nullptr;
}
return d_ptr->datasets.at(d_ptr->isNUMA() ? (d_ptr->datasets.count(nodeId) ? nodeId : HwlocCpuInfo::nodeIndexes().front()) : 0);
return d_ptr->queue.dataset(job, nodeId);
}
std::pair<unsigned, unsigned> xmrig::Rx::hugePages()
std::pair<uint32_t, uint32_t> xmrig::Rx::hugePages()
{
std::pair<unsigned, unsigned> pages(0, 0);
std::lock_guard<std::mutex> lock(mutex);
for (auto const &item : d_ptr->datasets) {
if (!item.second) {
continue;
}
const auto p = item.second->hugePages();
pages.first += p.first;
pages.second += p.second;
}
return pages;
return d_ptr->queue.hugePages();
}
@ -346,7 +102,7 @@ void xmrig::Rx::destroy()
}
void xmrig::Rx::init()
void xmrig::Rx::init(IRxListener *listener)
{
d_ptr = new RxPrivate();
d_ptr = new RxPrivate(listener);
}

View file

@ -39,18 +39,19 @@ namespace xmrig
class Algorithm;
class IRxListener;
class Job;
class RxConfig;
class RxDataset;
class Rx
{
public:
static bool init(const Job &job, int initThreads, bool hugePages, bool numa, IRxListener *listener);
static bool init(const Job &job, const RxConfig &config, bool hugePages);
static bool isReady(const Job &job);
static RxDataset *dataset(const Job &job, uint32_t nodeId);
static std::pair<unsigned, unsigned> hugePages();
static std::pair<uint32_t, uint32_t> hugePages();
static void destroy();
static void init();
static void init(IRxListener *listener);
};

View file

@ -45,7 +45,9 @@ const RandomX_ConfigurationBase *xmrig::RxAlgo::base(Algorithm::Id algorithm)
case Algorithm::RX_LOKI:
return &RandomX_LokiConfig;
break;
case Algorithm::RX_ARQ:
return &RandomX_ArqmaConfig;
default:
break;

View file

@ -0,0 +1,169 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/rx/RxBasicStorage.h"
#include "backend/common/Tags.h"
#include "base/io/log/Log.h"
#include "base/tools/Chrono.h"
#include "base/tools/Object.h"
#include "crypto/rx/RxAlgo.h"
#include "crypto/rx/RxCache.h"
#include "crypto/rx/RxDataset.h"
#include "crypto/rx/RxSeed.h"
namespace xmrig {
constexpr size_t oneMiB = 1024 * 1024;
class RxBasicStoragePrivate
{
public:
XMRIG_DISABLE_COPY_MOVE(RxBasicStoragePrivate)
inline RxBasicStoragePrivate() = default;
inline ~RxBasicStoragePrivate()
{
delete m_dataset;
}
inline bool isReady(const Job &job) const { return m_ready && m_seed == job; }
inline RxDataset *dataset() const { return m_dataset; }
inline void setSeed(const RxSeed &seed)
{
m_ready = false;
if (m_seed.algorithm() != seed.algorithm()) {
RxAlgo::apply(seed.algorithm());
}
m_seed = seed;
}
inline void createDataset(bool hugePages)
{
const uint64_t ts = Chrono::steadyMSecs();
m_dataset = new RxDataset(hugePages, true);
printAllocStatus(ts);
}
inline void initDataset(uint32_t threads)
{
const uint64_t ts = Chrono::steadyMSecs();
m_dataset->init(m_seed.data(), threads);
LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts);
m_ready = true;
}
private:
void printAllocStatus(uint64_t ts)
{
if (m_dataset->get() != nullptr) {
const auto pages = m_dataset->hugePages();
const double percent = pages.first == 0 ? 0.0 : static_cast<double>(pages.first) / pages.second * 100.0;
LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%1.0f%% %u/%u" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"),
rx_tag(),
m_dataset->size() / oneMiB,
RxDataset::maxSize() / oneMiB,
RxCache::maxSize() / oneMiB,
(pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)),
percent,
pages.first,
pages.second,
m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-",
Chrono::steadyMSecs() - ts
);
}
else {
LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX dataset, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts);
}
}
bool m_ready = false;
RxDataset *m_dataset = nullptr;
RxSeed m_seed;
};
} // namespace xmrig
xmrig::RxBasicStorage::RxBasicStorage() :
d_ptr(new RxBasicStoragePrivate())
{
}
xmrig::RxBasicStorage::~RxBasicStorage()
{
delete d_ptr;
}
xmrig::RxDataset *xmrig::RxBasicStorage::dataset(const Job &job, uint32_t) const
{
if (!d_ptr->isReady(job)) {
return nullptr;
}
return d_ptr->dataset();
}
std::pair<uint32_t, uint32_t> xmrig::RxBasicStorage::hugePages() const
{
if (!d_ptr->dataset()) {
return { 0u, 0u };
}
return d_ptr->dataset()->hugePages();
}
void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages)
{
d_ptr->setSeed(seed);
if (!d_ptr->dataset()) {
d_ptr->createDataset(hugePages);
}
d_ptr->initDataset(threads);
}

View file

@ -0,0 +1,63 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_RX_BASICSTORAGE_H
#define XMRIG_RX_BASICSTORAGE_H
#include "backend/common/interfaces/IRxStorage.h"
#include "base/tools/Object.h"
namespace xmrig
{
class RxBasicStoragePrivate;
class RxBasicStorage : public IRxStorage
{
public:
XMRIG_DISABLE_COPY_MOVE(RxBasicStorage);
RxBasicStorage();
~RxBasicStorage() override;
protected:
RxDataset *dataset(const Job &job, uint32_t nodeId) const override;
std::pair<uint32_t, uint32_t> hugePages() const override;
void init(const RxSeed &seed, uint32_t threads, bool hugePages) override;
private:
RxBasicStoragePrivate *d_ptr;
};
} /* namespace xmrig */
#endif /* XMRIG_RX_BASICSTORAGE_H */

View file

@ -25,8 +25,9 @@
*/
#include "crypto/randomx/randomx.h"
#include "crypto/rx/RxCache.h"
#include "crypto/common/VirtualMemory.h"
#include "crypto/randomx/randomx.h"
static_assert(RANDOMX_FLAG_JIT == 8, "RANDOMX_FLAG_JIT flag mismatch");
@ -72,3 +73,17 @@ bool xmrig::RxCache::init(const Buffer &seed)
return true;
}
std::pair<uint32_t, uint32_t> xmrig::RxCache::hugePages() const
{
constexpr size_t twoMiB = 2u * 1024u * 1024u;
constexpr size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB;
uint32_t count = 0;
if (isHugePages()) {
count += total;
}
return { count, total };
}

View file

@ -55,8 +55,10 @@ public:
inline bool isJIT() const { return m_flags & 8; }
inline const Buffer &seed() const { return m_seed; }
inline randomx_cache *get() const { return m_cache; }
inline size_t size() const { return maxSize(); }
bool init(const Buffer &seed);
std::pair<uint32_t, uint32_t> hugePages() const;
static inline constexpr size_t maxSize() { return RANDOMX_CACHE_MAX_SIZE; }

View file

@ -23,41 +23,11 @@
*/
#include "base/io/json/Json.h"
#include "crypto/rx/RxConfig.h"
#include "rapidjson/document.h"
#include "backend/cpu/Cpu.h"
namespace xmrig {
static const char *kInit = "init";
static const char *kNUMA = "numa";
}
rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const
uint32_t xmrig::RxConfig::threads() const
{
using namespace rapidjson;
auto &allocator = doc.GetAllocator();
Value obj(kObjectType);
obj.AddMember(StringRef(kInit), m_threads, allocator);
obj.AddMember(StringRef(kNUMA), m_numa, allocator);
return obj;
}
bool xmrig::RxConfig::read(const rapidjson::Value &value)
{
if (value.IsObject()) {
m_numa = Json::getBool(value, kNUMA, m_numa);
m_threads = Json::getInt(value, kInit, m_threads);
return true;
}
return false;
return m_threads < 1 ? static_cast<uint32_t>(Cpu::info()->threads()) : static_cast<uint32_t>(m_threads);
}

View file

@ -29,6 +29,9 @@
#include "rapidjson/fwd.h"
#include <vector>
namespace xmrig {
@ -38,12 +41,22 @@ public:
bool read(const rapidjson::Value &value);
rapidjson::Value toJSON(rapidjson::Document &doc) const;
inline bool isNUMA() const { return m_numa; }
inline int threads() const { return m_threads; }
# ifdef XMRIG_FEATURE_HWLOC
std::vector<uint32_t> nodeset() const;
# else
inline std::vector<uint32_t> nodeset() const { return std::vector<uint32_t>(); }
# endif
uint32_t threads() const;
private:
bool m_numa = true;
int m_threads = -1;
# ifdef XMRIG_FEATURE_HWLOC
std::vector<uint32_t> m_nodeset;
# endif
};

View file

@ -0,0 +1,59 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/rx/RxConfig.h"
#include "base/io/json/Json.h"
#include "rapidjson/document.h"
namespace xmrig {
static const char *kInit = "init";
}
rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const
{
using namespace rapidjson;
auto &allocator = doc.GetAllocator();
Value obj(kObjectType);
obj.AddMember(StringRef(kInit), m_threads, allocator);
return obj;
}
bool xmrig::RxConfig::read(const rapidjson::Value &value)
{
if (value.IsObject()) {
m_threads = Json::getInt(value, kInit, m_threads);
return true;
}
return false;
}

View file

@ -0,0 +1,100 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "backend/cpu/Cpu.h"
#include "backend/cpu/platform/HwlocCpuInfo.h"
#include "base/io/json/Json.h"
#include "crypto/rx/RxConfig.h"
#include "rapidjson/document.h"
namespace xmrig {
static const char *kInit = "init";
static const char *kNUMA = "numa";
}
rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const
{
using namespace rapidjson;
auto &allocator = doc.GetAllocator();
Value obj(kObjectType);
obj.AddMember(StringRef(kInit), m_threads, allocator);
if (!m_nodeset.empty()) {
Value numa(kArrayType);
for (uint32_t i : m_nodeset) {
numa.PushBack(i, allocator);
}
obj.AddMember(StringRef(kNUMA), numa, allocator);
}
else {
obj.AddMember(StringRef(kNUMA), m_numa, allocator);
}
return obj;
}
bool xmrig::RxConfig::read(const rapidjson::Value &value)
{
if (value.IsObject()) {
m_threads = Json::getInt(value, kInit, m_threads);
const auto &numa = Json::getValue(value, kNUMA);
if (numa.IsArray()) {
m_nodeset.reserve(numa.Size());
for (const auto &node : numa.GetArray()) {
if (node.IsUint()) {
m_nodeset.emplace_back(node.GetUint());
}
}
}
else if (numa.IsBool()) {
m_numa = numa.GetBool();
}
return true;
}
return false;
}
std::vector<uint32_t> xmrig::RxConfig::nodeset() const
{
if (!m_nodeset.empty()) {
return m_nodeset;
}
return (m_numa && Cpu::info()->nodes() > 1) ? static_cast<HwlocCpuInfo *>(Cpu::info())->nodeset() : std::vector<uint32_t>();
}

View file

@ -25,32 +25,32 @@
*/
#include <thread>
#include "crypto/rx/RxDataset.h"
#include "crypto/common/VirtualMemory.h"
#include "crypto/randomx/randomx.h"
#include "crypto/rx/RxAlgo.h"
#include "crypto/rx/RxCache.h"
#include "crypto/rx/RxDataset.h"
#include <thread>
static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch");
xmrig::RxDataset::RxDataset(bool hugePages)
xmrig::RxDataset::RxDataset(bool hugePages, bool cache)
{
if (hugePages) {
m_flags = RANDOMX_FLAG_LARGE_PAGES;
m_dataset = randomx_alloc_dataset(static_cast<randomx_flags>(m_flags));
}
allocate(hugePages);
if (!m_dataset) {
m_flags = RANDOMX_FLAG_DEFAULT;
m_dataset = randomx_alloc_dataset(static_cast<randomx_flags>(m_flags));
if (cache) {
m_cache = new RxCache(hugePages);
}
}
m_cache = new RxCache(hugePages);
xmrig::RxDataset::RxDataset(RxCache *cache) :
m_cache(cache)
{
}
@ -66,7 +66,11 @@ xmrig::RxDataset::~RxDataset()
bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads)
{
cache()->init(seed);
if (!m_cache) {
return false;
}
m_cache->init(seed);
if (!get()) {
return true;
@ -96,18 +100,39 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads)
}
std::pair<size_t, size_t> xmrig::RxDataset::hugePages() const
size_t xmrig::RxDataset::size(bool cache) const
{
constexpr size_t twoMiB = 2u * 1024u * 1024u;
constexpr const size_t total = (VirtualMemory::align(maxSize(), twoMiB) + VirtualMemory::align(RxCache::maxSize(), twoMiB)) / twoMiB;
size_t size = 0;
size_t count = 0;
if (isHugePages()) {
count += VirtualMemory::align(maxSize(), twoMiB) / twoMiB;
if (m_dataset) {
size += maxSize();
}
if (m_cache->isHugePages()) {
count += VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB;
if (cache && m_cache) {
size += RxCache::maxSize();
}
return size;
}
std::pair<uint32_t, uint32_t> xmrig::RxDataset::hugePages(bool cache) const
{
constexpr size_t twoMiB = 2u * 1024u * 1024u;
constexpr size_t cacheSize = VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB;
size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB;
uint32_t count = 0;
if (isHugePages()) {
count += total;
}
if (cache && m_cache) {
total += cacheSize;
if (m_cache->isHugePages()) {
count += cacheSize;
}
}
return { count, total };
@ -118,3 +143,27 @@ void *xmrig::RxDataset::raw() const
{
return m_dataset ? randomx_get_dataset_memory(m_dataset) : nullptr;
}
void xmrig::RxDataset::setRaw(const void *raw)
{
if (!m_dataset) {
return;
}
memcpy(randomx_get_dataset_memory(m_dataset), raw, maxSize());
}
void xmrig::RxDataset::allocate(bool hugePages)
{
if (hugePages) {
m_flags = RANDOMX_FLAG_LARGE_PAGES;
m_dataset = randomx_alloc_dataset(static_cast<randomx_flags>(m_flags));
}
if (!m_dataset) {
m_flags = RANDOMX_FLAG_DEFAULT;
m_dataset = randomx_alloc_dataset(static_cast<randomx_flags>(m_flags));
}
}

View file

@ -49,22 +49,26 @@ class RxDataset
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxDataset)
RxDataset(bool hugePages = true);
RxDataset(bool hugePages, bool cache);
RxDataset(RxCache *cache);
~RxDataset();
inline bool isHugePages() const { return m_flags & 1; }
inline randomx_dataset *get() const { return m_dataset; }
inline RxCache *cache() const { return m_cache; }
inline size_t size() const { return maxSize(); }
inline bool isHugePages() const { return m_flags & 1; }
inline randomx_dataset *get() const { return m_dataset; }
inline RxCache *cache() const { return m_cache; }
inline void setCache(RxCache *cache) { m_cache = cache; }
bool init(const Buffer &seed, uint32_t numThreads);
std::pair<size_t, size_t> hugePages() const;
size_t size(bool cache = true) const;
std::pair<uint32_t, uint32_t> hugePages(bool cache = true) const;
void *raw() const;
void setRaw(const void *raw);
static inline constexpr size_t maxSize() { return RANDOMX_DATASET_MAX_SIZE; }
private:
Algorithm m_algorithm;
void allocate(bool hugePages);
int m_flags = 0;
randomx_dataset *m_dataset = nullptr;
RxCache *m_cache = nullptr;

View file

@ -0,0 +1,358 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/rx/RxNUMAStorage.h"
#include "backend/common/Tags.h"
#include "backend/cpu/Cpu.h"
#include "backend/cpu/platform/HwlocCpuInfo.h"
#include "base/io/log/Log.h"
#include "base/kernel/Platform.h"
#include "base/tools/Chrono.h"
#include "base/tools/Object.h"
#include "crypto/rx/RxAlgo.h"
#include "crypto/rx/RxCache.h"
#include "crypto/rx/RxDataset.h"
#include "crypto/rx/RxSeed.h"
#include <map>
#include <mutex>
#include <hwloc.h>
#include <thread>
namespace xmrig {
constexpr size_t oneMiB = 1024 * 1024;
static std::mutex mutex;
static bool bindToNUMANode(uint32_t nodeId)
{
auto cpu = static_cast<HwlocCpuInfo *>(Cpu::info());
hwloc_obj_t node = hwloc_get_numanode_obj_by_os_index(cpu->topology(), nodeId);
if (!node) {
return false;
}
if (cpu->membind(node->nodeset)) {
Platform::setThreadAffinity(static_cast<uint64_t>(hwloc_bitmap_first(node->cpuset)));
return true;
}
return false;
}
static inline void printSkipped(uint32_t nodeId, const char *reason)
{
LOG_WARN("%s" CYAN_BOLD("#%u ") RED_BOLD("skipped") YELLOW(" (%s)"), rx_tag(), nodeId, reason);
}
static inline void printDatasetReady(uint32_t nodeId, uint64_t ts)
{
LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), nodeId, Chrono::steadyMSecs() - ts);
}
class RxNUMAStoragePrivate
{
public:
XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxNUMAStoragePrivate)
inline RxNUMAStoragePrivate(const std::vector<uint32_t> &nodeset) :
m_nodeset(nodeset)
{
m_threads.reserve(nodeset.size());
}
inline ~RxNUMAStoragePrivate()
{
join();
for (auto const &item : m_datasets) {
delete item.second;
}
}
inline bool isAllocated() const { return m_allocated; }
inline bool isReady(const Job &job) const { return m_ready && m_seed == job; }
inline RxDataset *dataset(uint32_t nodeId) const { return m_datasets.count(nodeId) ? m_datasets.at(nodeId) : m_datasets.at(m_nodeset.front()); }
inline void setSeed(const RxSeed &seed)
{
m_ready = false;
if (m_seed.algorithm() != seed.algorithm()) {
RxAlgo::apply(seed.algorithm());
}
m_seed = seed;
}
inline void createDatasets(bool hugePages)
{
const uint64_t ts = Chrono::steadyMSecs();
for (uint32_t node : m_nodeset) {
m_threads.emplace_back(allocate, this, node, hugePages);
}
join();
std::thread thread(allocateCache, this, m_nodeset.front(), hugePages);
thread.join();
if (m_datasets.empty()) {
m_datasets.insert({ m_nodeset.front(), new RxDataset(m_cache) });
LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX datasets, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts);
}
else {
dataset(m_nodeset.front())->setCache(m_cache);
printAllocStatus(ts);
}
m_allocated = true;
}
inline void initDatasets(uint32_t threads)
{
uint64_t ts = Chrono::steadyMSecs();
auto id = m_nodeset.front();
auto primary = dataset(id);
primary->init(m_seed.data(), threads);
printDatasetReady(id, ts);
if (m_datasets.size() > 1) {
for (auto const &item : m_datasets) {
if (item.first == id) {
continue;
}
m_threads.emplace_back(copyDataset, item.second, item.first, primary->raw());
}
join();
}
m_ready = true;
}
inline std::pair<uint32_t, uint32_t> hugePages() const
{
auto pages = m_cache->hugePages();
for (auto const &item : m_datasets) {
const auto p = item.second->hugePages(false);
pages.first += p.first;
pages.second += p.second;
}
return pages;
}
private:
static void allocate(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages)
{
const uint64_t ts = Chrono::steadyMSecs();
if (!bindToNUMANode(nodeId)) {
printSkipped(nodeId, "can't bind memory");
return;
}
auto dataset = new RxDataset(hugePages, false);
if (!dataset->get()) {
printSkipped(nodeId, "failed to allocate dataset");
delete dataset;
return;
}
std::lock_guard<std::mutex> lock(mutex);
d_ptr->m_datasets.insert({ nodeId, dataset });
d_ptr->printAllocStatus(dataset, nodeId, ts);
}
static void allocateCache(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages)
{
const uint64_t ts = Chrono::steadyMSecs();
bindToNUMANode(nodeId);
auto cache = new RxCache(hugePages);
std::lock_guard<std::mutex> lock(mutex);
d_ptr->m_cache = cache;
d_ptr->printAllocStatus(cache, nodeId, ts);
}
static void copyDataset(RxDataset *dst, uint32_t nodeId, const void *raw)
{
const uint64_t ts = Chrono::steadyMSecs();
dst->setRaw(raw);
printDatasetReady(nodeId, ts);
}
void printAllocStatus(RxDataset *dataset, uint32_t nodeId, uint64_t ts)
{
const auto pages = dataset->hugePages();
const double percent = pages.first == 0 ? 0.0 : static_cast<double>(pages.first) / pages.second * 100.0;
LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") " huge pages %s%3.0f%%" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"),
rx_tag(),
nodeId,
dataset->size() / oneMiB,
(pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S),
percent,
Chrono::steadyMSecs() - ts
);
}
void printAllocStatus(RxCache *cache, uint32_t nodeId, uint64_t ts)
{
const auto pages = cache->hugePages();
const double percent = pages.first == 0 ? 0.0 : static_cast<double>(pages.first) / pages.second * 100.0;
LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"),
rx_tag(),
nodeId,
cache->size() / oneMiB,
(pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S),
percent,
cache->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-",
Chrono::steadyMSecs() - ts
);
}
void printAllocStatus(uint64_t ts)
{
size_t memory = m_cache->size();
auto pages = hugePages();
const double percent = pages.first == 0 ? 0.0 : static_cast<double>(pages.first) / pages.second * 100.0;
for (auto const &item : m_datasets) {
memory += item.second->size(false);
}
LOG_INFO("%s" CYAN_BOLD("-- ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%% %u/%u" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"),
rx_tag(),
memory / oneMiB,
(pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)),
percent,
pages.first,
pages.second,
Chrono::steadyMSecs() - ts
);
}
inline void join()
{
for (auto &thread : m_threads) {
thread.join();
}
m_threads.clear();
}
bool m_allocated = false;
bool m_ready = false;
RxCache *m_cache = nullptr;
RxSeed m_seed;
std::map<uint32_t, RxDataset *> m_datasets;
std::vector<std::thread> m_threads;
std::vector<uint32_t> m_nodeset;
};
} // namespace xmrig
xmrig::RxNUMAStorage::RxNUMAStorage(const std::vector<uint32_t> &nodeset) :
d_ptr(new RxNUMAStoragePrivate(nodeset))
{
}
xmrig::RxNUMAStorage::~RxNUMAStorage()
{
delete d_ptr;
}
xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t nodeId) const
{
if (!d_ptr->isReady(job)) {
return nullptr;
}
return d_ptr->dataset(nodeId);
}
std::pair<uint32_t, uint32_t> xmrig::RxNUMAStorage::hugePages() const
{
if (!d_ptr->isAllocated()) {
return { 0u, 0u };
}
return d_ptr->hugePages();
}
void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages)
{
d_ptr->setSeed(seed);
if (!d_ptr->isAllocated()) {
d_ptr->createDatasets(hugePages);
}
d_ptr->initDatasets(threads);
}

View file

@ -0,0 +1,66 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_RX_NUMASTORAGE_H
#define XMRIG_RX_NUMASTORAGE_H
#include "backend/common/interfaces/IRxStorage.h"
#include "base/tools/Object.h"
#include <vector>
namespace xmrig
{
class RxNUMAStoragePrivate;
class RxNUMAStorage : public IRxStorage
{
public:
XMRIG_DISABLE_COPY_MOVE(RxNUMAStorage);
RxNUMAStorage(const std::vector<uint32_t> &nodeset);
~RxNUMAStorage() override;
protected:
RxDataset *dataset(const Job &job, uint32_t nodeId) const override;
std::pair<uint32_t, uint32_t> hugePages() const override;
void init(const RxSeed &seed, uint32_t threads, bool hugePages) override;
private:
RxNUMAStoragePrivate *d_ptr;
};
} /* namespace xmrig */
#endif /* XMRIG_RX_NUMASTORAGE_H */

182
src/crypto/rx/RxQueue.cpp Normal file
View file

@ -0,0 +1,182 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto/rx/RxQueue.h"
#include "backend/common/Tags.h"
#include "base/io/log/Log.h"
#include "crypto/rx/RxBasicStorage.h"
#include "base/tools/Handle.h"
#include "backend/common/interfaces/IRxListener.h"
#ifdef XMRIG_FEATURE_HWLOC
# include "crypto/rx/RxNUMAStorage.h"
#endif
xmrig::RxQueue::RxQueue(IRxListener *listener) :
m_listener(listener)
{
m_async = new uv_async_t;
m_async->data = this;
uv_async_init(uv_default_loop(), m_async, [](uv_async_t *handle) { static_cast<RxQueue *>(handle->data)->onReady(); });
m_thread = std::move(std::thread(&RxQueue::backgroundInit, this));
}
xmrig::RxQueue::~RxQueue()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_state = STATE_SHUTDOWN;
lock.unlock();
m_cv.notify_one();
m_thread.join();
delete m_storage;
Handle::close(m_async);
}
bool xmrig::RxQueue::isReady(const Job &job)
{
std::lock_guard<std::mutex> lock(m_mutex);
return isReadyUnsafe(job);
}
xmrig::RxDataset *xmrig::RxQueue::dataset(const Job &job, uint32_t nodeId)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (isReadyUnsafe(job)) {
return m_storage->dataset(job, nodeId);
}
return nullptr;
}
std::pair<uint32_t, uint32_t> xmrig::RxQueue::hugePages()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_storage && m_state == STATE_IDLE ? m_storage->hugePages() : std::pair<uint32_t, uint32_t>(0u, 0u);
}
void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector<uint32_t> &nodeset, uint32_t threads, bool hugePages)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_storage) {
# ifdef XMRIG_FEATURE_HWLOC
if (!nodeset.empty()) {
m_storage = new RxNUMAStorage(nodeset);
}
else
# endif
{
m_storage = new RxBasicStorage();
}
}
if (m_state == STATE_PENDING && m_seed == seed) {
return;
}
m_queue.emplace_back(seed, nodeset, threads, hugePages);
m_seed = seed;
m_state = STATE_PENDING;
lock.unlock();
m_cv.notify_one();
}
bool xmrig::RxQueue::isReadyUnsafe(const Job &job) const
{
return m_storage != nullptr && m_state == STATE_IDLE && m_seed == job;
}
void xmrig::RxQueue::backgroundInit()
{
while (m_state != STATE_SHUTDOWN) {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_state == STATE_IDLE) {
m_cv.wait(lock, [this]{ return m_state != STATE_IDLE; });
}
if (m_state != STATE_PENDING) {
continue;
}
const auto item = m_queue.back();
m_queue.clear();
lock.unlock();
LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."),
rx_tag(),
item.nodeset.size() > 1 ? "s" : "",
item.seed.algorithm().shortName(),
item.threads,
Buffer::toHex(item.seed.data().data(), 8).data()
);
m_storage->init(item.seed, item.threads, item.hugePages);
lock = std::move(std::unique_lock<std::mutex>(m_mutex));
if (m_state == STATE_SHUTDOWN || !m_queue.empty()) {
continue;
}
m_state = STATE_IDLE;
uv_async_send(m_async);
}
}
void xmrig::RxQueue::onReady()
{
std::unique_lock<std::mutex> lock(m_mutex);
const bool ready = m_listener && m_state == STATE_IDLE;
lock.unlock();
if (ready) {
m_listener->onDatasetReady();
}
}

108
src/crypto/rx/RxQueue.h Normal file
View file

@ -0,0 +1,108 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_RX_QUEUE_H
#define XMRIG_RX_QUEUE_H
#include "base/tools/Object.h"
#include "crypto/rx/RxSeed.h"
#include <condition_variable>
#include <mutex>
#include <thread>
using uv_async_t = struct uv_async_s;
namespace xmrig
{
class IRxListener;
class IRxStorage;
class RxDataset;
class RxQueueItem
{
public:
RxQueueItem(const RxSeed &seed, const std::vector<uint32_t> &nodeset, uint32_t threads, bool hugePages) :
hugePages(hugePages),
seed(seed),
nodeset(nodeset),
threads(threads)
{}
const bool hugePages;
const RxSeed seed;
const std::vector<uint32_t> nodeset;
const uint32_t threads;
};
class RxQueue
{
public:
XMRIG_DISABLE_COPY_MOVE(RxQueue);
RxQueue(IRxListener *listener);
~RxQueue();
bool isReady(const Job &job);
RxDataset *dataset(const Job &job, uint32_t nodeId);
std::pair<uint32_t, uint32_t> hugePages();
void enqueue(const RxSeed &seed, const std::vector<uint32_t> &nodeset, uint32_t threads, bool hugePages);
private:
enum State {
STATE_IDLE,
STATE_PENDING,
STATE_SHUTDOWN
};
bool isReadyUnsafe(const Job &job) const;
void backgroundInit();
void onReady();
IRxListener *m_listener = nullptr;
IRxStorage *m_storage = nullptr;
RxSeed m_seed;
State m_state = STATE_IDLE;
std::condition_variable m_cv;
std::mutex m_mutex;
std::thread m_thread;
std::vector<RxQueueItem> m_queue;
uv_async_t *m_async = nullptr;
};
} /* namespace xmrig */
#endif /* XMRIG_RX_QUEUE_H */

69
src/crypto/rx/RxSeed.h Normal file
View file

@ -0,0 +1,69 @@
/* XMRig
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
* Copyright 2018-2019 tevador <tevador@gmail.com>
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_RX_SEED_H
#define XMRIG_RX_SEED_H
#include "base/net/stratum/Job.h"
#include "base/tools/Buffer.h"
namespace xmrig
{
class RxSeed;
class RxSeed
{
public:
RxSeed() = default;
inline RxSeed(const Algorithm &algorithm, const Buffer &seed) : m_algorithm(algorithm), m_data(seed) {}
inline RxSeed(const Job &job) : m_algorithm(job.algorithm()), m_data(job.seed()) {}
inline bool isEqual(const Job &job) const { return m_algorithm == job.algorithm() && m_data == job.seed(); }
inline bool isEqual(const RxSeed &other) const { return m_algorithm == other.m_algorithm && m_data == other.m_data; }
inline const Algorithm &algorithm() const { return m_algorithm; }
inline const Buffer &data() const { return m_data; }
inline bool operator!=(const Job &job) const { return !isEqual(job); }
inline bool operator!=(const RxSeed &other) const { return !isEqual(other); }
inline bool operator==(const Job &job) const { return isEqual(job); }
inline bool operator==(const RxSeed &other) const { return isEqual(other); }
private:
Algorithm m_algorithm;
Buffer m_data;
};
} /* namespace xmrig */
#endif /* XMRIG_RX_CACHE_H */

View file

@ -41,11 +41,11 @@ xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes)
m_flags |= RANDOMX_FLAG_FULL_MEM;
}
if (dataset->cache()->isJIT()) {
if (!dataset->cache() || dataset->cache()->isJIT()) {
m_flags |= RANDOMX_FLAG_JIT;
}
m_vm = randomx_create_vm(static_cast<randomx_flags>(m_flags), dataset->cache()->get(), dataset->get(), scratchpad);
m_vm = randomx_create_vm(static_cast<randomx_flags>(m_flags), dataset->cache() ? dataset->cache()->get() : nullptr, dataset->get(), scratchpad);
}

View file

@ -105,7 +105,7 @@ static inline void checkHash(const JobBundle &bundle, std::vector<JobResult> &re
static void getResults(JobBundle &bundle, std::vector<JobResult> &results, uint32_t &errors, bool hwAES)
{
const auto &algorithm = bundle.job.algorithm();
auto memory = new VirtualMemory(algorithm.l3(), false);
auto memory = new VirtualMemory(algorithm.l3(), false, false);
uint8_t hash[32]{ 0 };
if (algorithm.family() == Algorithm::RANDOM_X) {